API reference

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

GET /api/v1/accounts

Search accounts by email, username or page name (admin keys only)

Up to 20 matches, ordered by id.

Sources

Pages, forms, connections and destination fields.

GET /api/v1/connections

Source to form to destination graph, plus the account's connections

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).

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.

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.

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.

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.

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.

Autoresponders

Email autoresponders on connections.

GET /api/v1/autoresponders

The account's autoresponders

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.

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.

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.

GET /api/v1/email-log

Emails LeadSync sent for the account

Newest first. Same filter semantics as the email log screen.