Developers
Creating leads with the API
The full reference for POST /api/v1/leads, including the Idempotency-Key header, the request body and every outcome it can return
3 min readUpdated 26 September 2026
POST /api/v1/leads records an inbound lead. It runs the same intake path as every other source in ClientTurn: deduplication against existing leads, a suppression check, a record of permission, and attribution for the enquiry.
Requirements
- An API key with the
leads:writescope, belonging to someone with a member role or above. - An
Idempotency-Keyheader: 8 to 200 printable characters, no spaces, unique per enquiry. - A JSON body.
Example
POST /api/v1/leads
Authorization: Bearer ct_live_…
Idempotency-Key: webflow-contact-sub_881
Content-Type: application/json
{
"first_name": "Jo",
"last_name": "Bloggs",
"email": "jo@acme.co.uk",
"phone": "07700 900123",
"company_name": "Acme Studio Ltd",
"relationship": "THEY_CONTACTED_US",
"source": {
"type": "WEB_FORM",
"provider": "webflow",
"record_id": "sub_881",
"form_name": "Contact us",
"utm_source": "google",
"submitted_at": "2026-09-25T10:04:00Z"
},
"answers": { "Budget": "£5k–£10k" },
"consent": { "marketing": true, "whatsapp": false, "evidence": "Ticked box v3 on /contact" }
}
Body fields
| Field | Required | Notes |
|---|---|---|
relationship | Yes | One of THEY_CONTACTED_US, EXISTING_CUSTOMER, REFERRAL, REQUESTED_INFORMATION, EXPLICIT_MARKETING_CONSENT, EXISTING_BUSINESS_RELATIONSHIP. A contact you merely found is a prospect, not a lead, and is not accepted here |
email, phone | At least one | UK numbers may be written in national format; they are normalised |
first_name, last_name, company_name, role_title, postcode | No | |
source | No | type is WEB_FORM, API (default), CRM or CONNECTOR. provider is a lower-case name for the sending system (default api). Optional: record_id, form_id, form_name, campaign_id, campaign_name, utm_source, utm_medium, utm_campaign, utm_term, utm_content, gclid, fbclid, referrer, landing_url, submitted_at |
answers | No | Question-and-answer pairs from your form |
consent | No, except for a referral | marketing and whatsapp (true or false) and free-text evidence. WhatsApp is only treated as allowed when whatsapp is true. With relationship REFERRAL, evidence is required: who referred them and when, in at least 20 characters |
service_id | No | The ID of one of your services |
Send submitted_at whenever you have it: speed-to-lead is measured from the time the person submitted, not from when ClientTurn received the request.
The response
Every successful call returns the outcome, never a bare "success":
{
"data": {
"outcome": "CREATED",
"lead_id": "…",
"touch_id": "…",
"matched_by": null,
"reasons": []
}
}
outcome | HTTP | Meaning |
|---|---|---|
CREATED | 201 | A new lead was created |
MERGED | 200 | The same person already existed (same email, or same phone with no conflicting email). Blank fields were filled, nothing was overwritten, and this enquiry was recorded as a new touch. matched_by says which rule matched |
DUPLICATE | 200 | This Idempotency-Key, or this source.record_id, was already received. Nothing new was created. A repeated key also returns original_outcome |
REVIEW | 200 | Recorded, but the phone number belongs to another lead with a different email. The two are not merged; a person decides |
INVALID | 422 | No usable email or phone. Nothing was stored |
REJECTED | 409 | The contact is on your suppression list. Nothing was stored: not the lead, not a touch, and not the idempotency key. reasons includes suppressed_refused |
A suppressed contact is refused rather than recorded, because the API is creating a record and "nothing stored" is the honest answer. (Leads that arrive from ad forms are still recorded when suppressed, so you can see the enquiry, and are never contacted.)
If you reuse an Idempotency-Key with a different body, the first outcome is still returned and reasons includes idempotency_key_reused_with_different_body.
Follow-up is never started by the API
A lead created over the API is recorded, qualified and counted against your plan, but automated follow-up is not started. A person chooses to message it, or selects it in Leads and chooses Actions → Start follow-up. The API has nobody present to confirm an outbound action, so it never takes one.
Errors
A missing or malformed Idempotency-Key or body returns 400 invalid_request. So does a REFERRAL whose consent.evidence is shorter than 20 characters: nothing is stored, and the message says what to send. For the other error codes, see API overview and authentication.
Related
- Reading and updating leads
- Webhooks, which can tell your system when the lead qualifies or books
Still stuck?
Customers can raise a ticket from the help button inside the app. Not a customer yet? Talk to us.
