Reference
Webhooks
Outbound: changes, as they happen
Owners and admins add webhook URLs under Settings, Webhooks. Tandem POSTs each change to them as JSON once it's saved:
{
"id": "evt_1042",
"type": "deal.won",
"created_at": "2026-11-02T09:14:03.120Z",
"workspace_id": "…",
"actor": { "kind": "oauth_client", "id": "…", "on_behalf_of_user_id": "…" },
"data": { "entity_type": "deal", "entity_id": "…", "before": { … }, "after": { … } }
}
Event types. Every change's own type, like deal.created, deal.stage_changed, person.created, activity.logged or task.completed, plus deal.won and deal.lost when a deal closes. Pick types when you add the webhook (person.* matches all of a record type's), or leave them all unticked for everything.
Signatures. Each delivery has a Tandem-Signature: t=<unix seconds>,v1=<hex> header. v1 is the HMAC-SHA256 of <t>.<raw body> with the webhook's secret, shown once when you add it. Check it, and that t is recent, before trusting a delivery. Tandem-Event carries the type.
Delivery. A 2xx response counts as delivered. Errors and timeouts (10 seconds) are retried twice. After 50 failures in a row the webhook is turned off; turn it back on from the same page. URLs must be public https addresses; redirects aren't followed.
Webhooks are managed by people only, never by agents, so an agent can't send your data somewhere new.
Inbound: leads from forms and automations
Create an inbound URL under Settings, Webhooks, and POST to it from a website form, Typeform, Zapier, Make or n8n. No headers needed: the secret is in the URL, so keep it private, and turn it off there if it leaks.
curl -X POST https://app.tandemcrm.app/hooks/inbound/crm_sk_… \
-H "Content-Type: application/json" \
-d '{"email": "jo@acme.com", "first_name": "Jo", "last_name": "Park", "company": "Acme", "message": "Can we get a demo?"}'
JSON and form-encoded bodies both work. Tandem adds or updates the company (by domain, website or a work email's domain, else by company name), the person (by email, with name or first_name and last_name, and title), and logs message or note as a note, with any other fields added to it. It answers { "received": true, "person_id", "company_id" }. An inbound URL only does this: it can't be used as an API key.