CRM sync
Keep your own CRM in step with the leads from your SalesLobe campaigns. The CRM API reads and updates the CRM part of a lead: phase, deal value, next action and notes. It works both ways and is built for incremental sync.
What it does not do: it never creates leads, never imports contacts and never sends email. A lead exists in SalesLobe once a prospect replied to one of your campaigns.
Requirements
- A key locked to one client (Settings → API Keys → pick the client).
- The scopes
crm.readand/orcrm.write, granted explicitly. A full-access (*) key does not include them. - The CRM API switched on for your account. If it is not, every CRM call answers
403 feature_not_enabled.
The lead object
{
"id": "5f0c…",
"email": "anna@acme.com",
"name": "Anna de Vries",
"company": { "name": null, "domain": "acme.com" },
"campaign": { "id": "b87f…", "name": "Q4 SaaS outreach" },
"classification": "hot",
"phase": "proposal",
"deal": { "value": 12000, "currency": "EUR" },
"next_action": { "date": "2026-10-07", "note": "Demo" },
"notes": [
{ "at": "2026-09-01T10:00:00Z", "by": "legacy", "text": "Called on Monday" },
{ "at": "2026-10-01T14:03:00Z", "by": "Mark's CRM", "text": "Demo booked" }
],
"last_activity_at": "2026-09-30T08:12:00Z",
"reply_count": 3,
"thread_url": "https://app.saleslobe.com/your-agency/portal/your-client?lead=…",
"updated_at": "2026-10-01T14:03:00Z",
"crm_updated_at": "2026-10-01T14:03:00Z"
}
idis stable: it does not change when new replies come in.thread_urlalways uses this id and opens the lead's current conversation in the portal.nameisnullwhen the prospect's name is unknown (never the e-mail address).company.domainis the domain of the e-mail address (nullfor Gmail, Outlook and other freemail);company.nameis not stored yet.phase:new,contacted,discovery,proposal,negotiation,won,lost.classificationis read only (SalesLobe classifies every reply).updated_atchanges when anything on the lead changes: use it forupdated_since.crm_updated_atchanges only when a CRM field or note changes: use it forif_updated_at.- Notes written before the CRM API existed appear as the first note with author
legacy.
Which leads
Exactly the leads the client sees in its portal: never out-of-office, spam or not-yet-classified replies; hot, warm and cold unless the agency switched them off in the portal permissions; not interested only when the agency shows them. A lead that is not visible does not exist in the API: it is not listed and get, update and notes answer 404. When an out-of-office lead later sends a real reply, it appears automatically (with updated_since).
Filters
GET /v1/crm/leads?classification=hot,warm&phase=proposal,negotiation&has_next_action=true. Filters apply per lead, so a page can hold fewer than limit leads: keep following next_cursor.
Incremental sync
- First run:
GET /v1/crm/leadsand follownext_cursoruntilhas_moreisfalse. - Store the time you started.
- Next runs:
GET /v1/crm/leads?updated_since=<stored time>.
Conflicts
Last write wins. Both sides are recorded in the Decision Trail. To avoid overwriting a newer change, send if_updated_at with the crm_updated_at you last read: if the lead changed since, you get 409 conflict with the current lead and nothing is written.
What a CRM update does in SalesLobe
phase→wonorlost: all planned follow-ups and check-ins for that lead are cancelled.- Any other CRM update (phase, next action, deal, note): Corty's planned follow-ups for that lead go to review instead of sending automatically, with the reason "lead bijgewerkt in CRM door <key name>".
- Every write shows in the Decision Trail and in the client portal with the key name as actor.
Limits
crm.write: at most 60 writes per minute per key (429 crm_write_rate_limited, Retry-After: 60). The key's normal rate limit applies to all calls.
Claude Desktop (MCP)
The same endpoints are available as an MCP server for Claude Desktop: see MCP server.