Log someone who came in or called
const url = 'https://api.threadkite.app/v1/leads/logged';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"kind":"visit","channel":"whatsapp","contact":{"contactId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","name":"Sarah Turner","email":"sarah@example.co.uk","phone":"07700 900123","company":{"id":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","name":"Acme Fleet Services"}},"inboxId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","locationId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","itemIds":["2489E9AD-2EE2-8E00-8EC9-32D5F69181C0"],"sourceId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","note":"Wants a family SUV under £20k","consent":{"email":true,"sms":true,"whatsapp":true},"occurredAt":"2026-04-15T12:00:00Z","clientId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","followUp":{"dueAt":"2026-04-15T12:00:00Z","dueDate":"2026-04-15","title":"Follow up with Sarah"},"appointment":{"type":"consultation","startsAt":"2026-04-15T12:00:00Z","endsAt":"2026-04-15T12:00:00Z","notifyChannel":"email"}}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.threadkite.app/v1/leads/logged \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "kind": "visit", "channel": "whatsapp", "contact": { "contactId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "Sarah Turner", "email": "sarah@example.co.uk", "phone": "07700 900123", "company": { "id": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "name": "Acme Fleet Services" } }, "inboxId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "locationId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "itemIds": [ "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0" ], "sourceId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "note": "Wants a family SUV under £20k", "consent": { "email": true, "sms": true, "whatsapp": true }, "occurredAt": "2026-04-15T12:00:00Z", "clientId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "followUp": { "dueAt": "2026-04-15T12:00:00Z", "dueDate": "2026-04-15", "title": "Follow up with Sarah" }, "appointment": { "type": "consultation", "startsAt": "2026-04-15T12:00:00Z", "endsAt": "2026-04-15T12:00:00Z", "notifyChannel": "email" } }'A lead in the inbox (channel visit or phone), matched to a contact and counted under the lead rule, sourced Walk-in or Phone call unless you give a source. You’ve already spoken to them, so the thread isn’t waiting on you; it’s given to you if nobody looks after it. Ticked consent is recorded as given to you in person. Add a follow-up or an appointment in the same step. Send a clientId so a retry from a phone that was offline never logs twice. Needs a signed-in person.
Authorizations
Section titled “Authorizations”Parameters
Section titled “Parameters”Header Parameters
Section titled “Header Parameters”Any unique value up to 255 characters (a UUID, or the id of the item in your outbox). For 24 hours a retry with the same key and request returns the first response, with Idempotent-Replayed: true, instead of doing it again. Works with an API key or a signed-in session; each has its own keys.
Request Bodyrequired
Section titled “Request Bodyrequired”object
They came in, they called, or they sent a message somewhere Threadkite is not connected to
Where they sent the message (message only): its source is that channel’s own (WhatsApp, Facebook, Instagram, Text message or Email) when sourceId is left out
object
Someone already in your contacts
Example
Sarah TurnerExample
sarah@example.co.ukExample
07700 900123The company they work for: one of yours by id, or by name (the one with that name, or a new one). Linked as their primary company when they have none
object
One of your companies. Give this or name.
A company by name: the one with that name (ignoring case, punctuation and Ltd), or a new one. Give this or id.
Example
Acme Fleet ServicesDefaults to your default inbox
What they are interested in, main item first
Where they heard about you; Walk-in, Phone call or the message’s channel when left out
For a message, what they said (required)
Example
Wants a family SUV under £20kChannels they agreed to hear about offers on, in person or in the message logged (recorded as given to you). Left out changes nothing.
object
Agreed to offers by email
Agreed to offers by text
Agreed to offers on WhatsApp
When (defaults to now); up to 7 days ago
Your id for this log: sending it again returns the first one
A follow-up task for you
object
Example
Follow up with SarahAn appointment with you
object
Example
consultationResponses
Section titled “Responses”object
The inbox it went to
Counted as a new lead (false when the same contact, source and item already sent one to the same inbox within the lead period)
The externalId was already received; nothing new was stored
How the contact was matched; null for a new contact
object
The phone number is also on this contact
object
The thread’s inbox
object
When it is due; for an all-day task, 9am on its day (when it is reminded)
The time zone its day belongs to
Against today, in your time zone
object
Their picture; null to show their initials
object
object
You can change, complete and delete it
object
The thread’s inbox
object
The time zone it is written in
object
object
The channel the customer is told on (null: not told)
Why the latest message to the customer wasn’t sent, if it wasn’t
Why it was cancelled (for the team)
object
You can move, cancel and mark it
Example
{ "matchedBy": "id", "task": { "contact": { "name": "Sarah Turner" }, "title": "Follow up with Sarah", "timeZone": "Europe/London", "dueState": "overdue", "dueLabel": "Tomorrow, 9am", "assignee": { "id": "usr_8f2c", "name": "James Patel", "avatarUrl": "https://api.threadkite.app/avatars/0b7c3a52-5d9e-4f3a-9a51-2f1e8c6d4b10.jpg" }, "completedBy": { "id": "usr_8f2c", "name": "James Patel" }, "createdBy": { "id": "usr_8f2c", "name": "James Patel" }, "origin": "person" }, "appointment": { "contact": { "name": "Sarah Turner" }, "type": "consultation", "typeLabel": "Consultation", "timeZone": "Europe/London", "status": "booked", "host": { "id": "usr_8f2c", "name": "James Patel" }, "item": { "title": "Oak dining table" }, "notifyChannel": "email", "notifyFailure": "They have opted out of texts.", "createdBy": { "id": "usr_8f2c", "name": "James Patel" } }}object
Stable machine-readable reason
With code invalid: every problem with the request, by field (message is the first)
object
Dotted path inside the item, e.g. attributes.price.amountMinor
Example
{ "statusCode": 403, "code": "forbidden", "message": "You do not have permission to do that.", "errors": [ { "message": "Enter the make" } ]}Missing or invalid API key
object
Stable machine-readable reason
With code invalid: every problem with the request, by field (message is the first)
object
Dotted path inside the item, e.g. attributes.price.amountMinor
Example
{ "statusCode": 403, "code": "forbidden", "message": "You do not have permission to do that.", "errors": [ { "message": "Enter the make" } ]}The organisation is on the Free plan (code upgrade_required)
object
Stable machine-readable reason
The feature that needs Pro
Where to upgrade or pay, in the web app
Example
{ "statusCode": 402, "code": "upgrade_required", "message": "Connections are part of Pro. Upgrade to connect texts, calls and more.", "feature": "extraSeats", "upgradePath": "/settings/billing"}The key does not have the scope (or the person the permission)
object
Stable machine-readable reason
With code invalid: every problem with the request, by field (message is the first)
object
Dotted path inside the item, e.g. attributes.price.amountMinor
Example
{ "statusCode": 403, "code": "forbidden", "message": "You do not have permission to do that.", "errors": [ { "message": "Enter the make" } ]}Rate limit reached
object
Stable machine-readable reason
Seconds until another request will be accepted
Example
{ "statusCode": 429, "code": "rate_limited", "message": "Too many requests. Each key can make 120 requests a minute; try again in 12 seconds.", "retryAfter": 12}