LeadSync API v1
Everything the LeadSync dashboard does for Meta (Facebook and Instagram) lead forms, as JSON: connections, lead forms, field mapping, autoresponders, leads and delivery logs.
Getting a key. Create one in LeadSync under Account, Settings, API keys (/account/settings#api-keys), or run npx @leadsync/skills login and approve the request in your browser. Use one key per assistant or integration so you can revoke each on its own. Send it as Authorization: Bearer ls_live_.... The key is shown once, when it is created.
Scope. Reads cover every source LeadSync holds. Writes are for Meta lead forms only: pages whose page_fb_id is google, linkedin or tiktok are read-only here and writes return 400 not_meta_source.
Rate limit. 120 requests per minute per key. Over the limit returns 429 with Retry-After.
Errors are RFC 7807 (application/problem+json) with a stable code. Every operation lists the codes it can produce in x-problem-codes. Codes common to all operations (401 unauthorized, 401 key_revoked, 429 rate_limited, 500 internal) are listed once in the Problem schema and not repeated per operation.
Timestamps are ISO 8601 UTC except lead_date_local (the advertiser's local time) and created on autoresponders (a calendar date). There are no DELETE verbs.
Base URL: https://leadsync.me.
The full specification is available as an OpenAPI file.
Working from an AI assistant? See the MCP set-up.
Login
Get a key without copying it by hand. No key is needed for these two calls.
POST /api/v1/device/code
Start a device login
Step 1 of the device flow (RFC 8628). Show user_code and verification_uri to the person, or open verification_uri_complete for them. They sign in to LeadSync and approve the request. Meanwhile poll POST /api/v1/device/token every interval seconds until expires_in runs out. Limited to 20 starts per IP address per 10 minutes.
POST /api/v1/device/token
Collect the key once the person approves
Step 2 of the device flow. Poll no faster than interval seconds; a faster poll returns 400 slow_down. While the person has not decided yet the answer is 400 authorization_pending. On approval the response carries the new key, once. A second poll with the same device_code returns 400 invalid_grant.
Account
Who the key is.
GET /api/v1/me
The account and key this request resolves to
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden.
GET /api/v1/accounts
Search accounts by email, username or page name (admin keys only)
Up to 20 matches, ordered by id.
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden. -
q(query, required): Substring matched against email, username and page name.
Sources
Pages, forms, connections and destination fields.
GET /api/v1/connections
Source to form to destination graph, plus the account's connections
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden.
GET /api/v1/destinations/{dua}/fields
A connection's destination fields, as the mapping table loads them
Read-only. dua is a connection id (destination_user_access id).
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden. -
dua(path, required) -
sheet_id(query): Required for Google Sheets connections.
POST /api/v1/pages/{id}/forms/refresh
Re-read a Facebook page's lead forms from Meta
Calls Meta Graph for the page's lead forms and marks which are already attached to a list. Needs no write permission (it changes nothing in LeadSync). Meta pages only.
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden. -
id(path, required): LeadSync page id (not the Facebook page id).
Lists
Attaching forms to destinations, mapping, activation and test leads.
POST /api/v1/lists
Attach a Meta lead form (or the page catch-all) to a connection
Mirrors the UI save: creates a list and its single destination row. Idempotent on (page, form, connection): an existing list is returned with created: false (200) and its mapping is never rewritten; if mapping or settings were sent, the response carries mapping_applied: false and a hint pointing at PUT /api/v1/lists/{id}/mapping. The existing list may be inactive (paused): then list.active is false and the hint says to PATCH /api/v1/lists/{id} with {"active": true} to resume delivery (one combined hint when both apply).
A new list runs the same form-variation backfill the UI runs: up to 7 days of the form's leads are re-sent to the new destination. backfill reports the counts, or is null for the catch-all, for an existing list, or when the backfill failed. Callers must expect replayed leads.
It also queues delivery to the new destination of that form's leads from the last 7 days (up to 200) that never reached the account because a destination failed (backfill.replayed_held). This is skipped when the destination's connection has an autoresponder, or when the account is inactive or on the free plan.
Limits checked in order: account_inactive, source_limit, forms_limit. override_limits: true needs an admin key with write access and skips them (the audit row records which were bypassed). Needs a write-capable key.
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden.
PATCH /api/v1/lists/{id}
Activate or deactivate a list
Activating can use a source slot, so it is gated by account_inactive and source_limit (skippable with override_limits on an admin write key) and refuses when another active list already covers the same form and connection (conflict). Setting the state a list already has returns 200 with changed: false. Needs a write-capable key.
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden. -
id(path, required): List id.
PUT /api/v1/lists/{id}/mapping
Replace a list's field mapping and/or per-list destination settings
Send mapping, settings, or both. Settings are merged over the stored ones; the connection and name are never changed. Refuses lists with more than one destination (one_destination_per_list). Needs a write-capable key.
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden. -
id(path, required): List id.
POST /api/v1/lists/{id}/test-lead
Ask Meta to create a test lead for a list's form
Meta lists only. Permission is the key's can_test_lead flag, not the write flag. When acting on another account (admin act-as) the body must carry acknowledge_customer_impact: true, because the lead goes to the customer's real destinations and may email them. Throttled per form per minute and per account per day. The body is optional.
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden. -
id(path, required): List id.
Autoresponders
Email autoresponders on connections.
GET /api/v1/autoresponders
The account's autoresponders
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden.
POST /api/v1/autoresponders
Create an email autoresponder on a connection
Mirrors the UI save for a new email autoresponder: plan gate, then the shared-connection guard, then the plan's quantity limit. If the connection already has autoresponders that would stack with this one, the request is refused with 409 shared_connection_autoresponder (body carries existing and message); repeat with acknowledge_shared_connection: true to proceed. override_limits (admin write key) skips the plan and quantity limits but never the shared-connection guard. Moderation is off today, so under_moderation is 0. Needs a write-capable key.
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden.
Leads
Delivery status, resend and the email log.
GET /api/v1/leads
Recent leads with one leg per destination list
Newest activity first. since defaults to 7 days ago when omitted and the response echoes the value used as since_used; pass an older since to see older leads. Filtering by status returns only the matching legs of each lead. since filters on the leg's creation time, so a leg re-sent after since of an older lead is not returned unless that lead was first queued after since.
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden. -
list_id(query) -
since(query): ISO 8601 timestamp with an explicit offset or Z, for example 2026-10-27T02:20:15Z. -
status(query) -
limit(query)
POST /api/v1/leads/{leadgen_id}/resend
Requeue one Meta lead's failed or held deliveries
Any valid key may call this, including a read-only admin key (a resend only re-delivers to lists the lead already has a delivery on). Returns 202: the workers deliver asynchronously (about a minute); poll GET /api/v1/leads.
The server predicts the delivery fan-out and refuses (409 conflict) if the resend would create a first or duplicate delivery. Account keys are also refused when the re-run would retry another account's deliveries; admin keys proceed and other_account_legs counts them. Legs that were not requeued are listed in skipped with a reason: success (already delivered), not_failed (not in a retryable state), not_routed (the list no longer receives this form's leads), other_list (outside the list_id filter). Body is optional.
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden. -
leadgen_id(path, required)
GET /api/v1/email-log
Emails LeadSync sent for the account
Newest first. Same filter semantics as the email log screen.
-
X-LeadSync-Account(header): Admin keys only. The account (user id) to act on. Account keys sending another id get 403act_as_forbidden. -
list_id(query) -
type(query) -
since(query): ISO 8601 timestamp with an explicit offset or Z, for example 2026-10-27T02:20:15Z. -
limit(query)