REST APIPractitioner and above

REST API and webhooks: queue LinkedIn actions and get events back

A keyed REST rail that queues LinkedIn actions for the desktop app to run, and posts HMAC-signed webhooks back when tasks finish or a lead replies.

Verified against the product on September 3, 2026 · Updated · Set up in /dashboard/api

What this integration is

ZenMode's public REST API lives at /api/v1/* and authenticates with an API key, not a dashboard session. A key is a Bearer token shaped zm_ plus 32 random hex bytes; ZenMode keeps only its SHA-256 hash and a short prefix, so the full key is shown once, at creation, and never again. Identity is derived server-side from that hash and every query carries the owning user_id, so a key reaches only its own account's data.

Two things sit on the rail: a task queue that the ZenMode desktop app drains against LinkedIn, and HMAC-signed webhooks that call an endpoint of yours back. Request and response shapes are on the public API reference.

This surface is plan-gated: an active paid plan, with Novice refused, so Practitioner and above. Tiers are listed on pricing.

What it triggers

RouteMethodsWhat it does
/api/v1/tasksPOST, GETQueue a task or list tasks. action_type is one of connect, message, follow, view_profile, withdraw_connection; the body carries payload.profile_url, payload.message, linkedin_account_id, priority, max_retries. Checked against the suppression registry and a per-user daily cap.
/api/v1/tasks/[id]GET, PATCH, DELETERead, update or cancel a task. A PATCH to completed or failed fires your webhook.
/api/v1/leadsGET, POSTRead and create leads, on a pinned status vocabulary. Exposes replied_at and calendar_clicked.
/api/v1/leads/[id]One lead.
/api/v1/leads/[id]/activityThat lead's activity trail.
/api/v1/analyticsGETdays 1–365 or all, optional campaign_id: total_contacts, sent, accepted, replied, meetings_booked, a daily series and a campaign comparison.
/api/v1/statusGETdesktop_active (heartbeat inside 10 minutes), last_seen, and per account display_name, is_running, phase, campaign_id, daily_actions, daily_limit, weekly_actions, weekly_limit, linkedin_status, started_at.
/api/v1/suppressionsGET, POST, DELETEThe do-not-contact registry queueing checks.
/api/v1/webhooksPOST, GET, DELETERegister, list and delete endpoints.

/api/v1/keys exists but is dashboard-only — it takes a session, not a zm_ key. Three events can be registered:

EventDirectionFires whenPayload
task.completedZenMode → your URLa queued task finishes{ event, data, timestamp }
task.failedZenMode → your URLa queued task fails{ event, data, timestamp }
lead.replied (opt-in)ZenMode → your URLinbox sync links an inbound replylead_id, campaign_id, name, linkedin_url, replied_at

What does not come across. lead.replied carries no reply text — the message body is deliberately not pushed. Message opens are not exposed, and neither is general click tracking: replied_at and calendar_clicked are the engagement fields the leads endpoints return. Reads are rate-limited to 100 requests a minute per key prefix.

How to set it up

  1. Open /dashboard/api and create a key. The full zm_… value is shown once — copy it into your secret store there and then.
  2. Send it as Authorization: Bearer zm_… on every /api/v1/* call.
  3. On the same page, register a webhook: paste your URL, tick the events you want, and keep the signing secret you get back — whsec_ plus 32 hex characters.
  4. Verify each delivery: recompute an HMAC-SHA256 of the raw body with that secret and compare it to the X-ZenMode-Signature header. X-ZenMode-Event names the event.
  5. To queue tasks, install the desktop app and stay signed in — it is what executes them.

When it runs

Queueing and execution are separate. A POST puts a task in the queue; the desktop app polls for work and runs it through the browser, and the server re-checks entitlement at hand-out, so a lapsed plan stops tasks being handed out. Every action routes through the per-tier action budget and paces at roughly 90–180 seconds or more. A batch drains gradually — this is a queue, not remote control.

Webhooks fire on the event itself. The body is JSON { event, data, timestamp }, delivery is retried three times with backoff, destinations are SSRF-guarded and redirects are not followed. These are the signed ones: the separate Settings card labelled "Webhooks (Zapier / Make)" posts unsigned.

What it's useful for

Queue work from your own systems: when a lead lands in your database, POST a view_profile and then a connect, and let the desktop pace them. Close the loop on replies: subscribe to lead.replied, open a task for whoever owns the account, and have them read the message itself in ZenMode. Watch the machine: poll /api/v1/status and alert when desktop_active goes false or an account's linkedin_status changes.

What it doesn't do (yet)

  • lead.replied does not carry the reply text. There is no event that does.
  • No message-open data and no general click tracking.
  • Nothing executes while the desktop app is closed; tasks queue and wait.
  • There is no ZenMode app in Zapier, no Make module and no n8n node. You reach those by registering their catch-hook URL as a webhook.
  • Delivery history is not readable in the product: the Logs button on the CRM webhook card calls a route that does not exist.

FAQ

Which plans include the API?

Practitioner and above. It requires an active paid plan and excludes Novice, the no-AI entry tier. A team member covered by someone else's plan, with no subscription of their own, is refused — the key has to belong to a subscribed account.

Does the API need my LinkedIn password?

No. ZenMode integrations never take LinkedIn credentials. Queued actions are carried out by the desktop app on your own machine, in the LinkedIn session you are already signed into there.

Is it two-way?

Yes — read, write and callbacks. Read leads, analytics and desktop status; write tasks, leads and suppressions; receive the three webhook events above.