Skip to content

Log someone who came in or called

POST
/v1/leads/logged
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.

Idempotency-Key
string
<= 255 characters

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.

Media typeapplication/json
object
kind
required

They came in, they called, or they sent a message somewhere Threadkite is not connected to

string
Allowed values: visit call message
channel

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

string
Allowed values: whatsapp messenger instagram sms email
contact
required
object
contactId

Someone already in your contacts

string format: uuid
name
string
nullable <= 200 characters

Example

Sarah Turner
email
string format: email
nullable <= 254 characters

Example

sarah@example.co.uk
phone
string
nullable <= 40 characters

Example

07700 900123
company

The 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
id

One of your companies. Give this or name.

string format: uuid
name

A company by name: the one with that name (ignoring case, punctuation and Ltd), or a new one. Give this or id.

string
>= 1 characters <= 200 characters

Example

Acme Fleet Services
inboxId

Defaults to your default inbox

string format: uuid
locationId
string format: uuid
nullable
itemIds

What they are interested in, main item first

Array<string>
<= 20 items
sourceId

Where they heard about you; Walk-in, Phone call or the message’s channel when left out

string format: uuid
note

For a message, what they said (required)

string
<= 5000 characters

Example

Wants a family SUV under £20k
consent

Channels they agreed to hear about offers on, in person or in the message logged (recorded as given to you). Left out changes nothing.

object
email

Agreed to offers by email

boolean
sms

Agreed to offers by text

boolean
whatsapp

Agreed to offers on WhatsApp

boolean
occurredAt

When (defaults to now); up to 7 days ago

string format: date-time
clientId

Your id for this log: sending it again returns the first one

string format: uuid
followUp

A follow-up task for you

object
dueAt
string format: date-time
dueDate
string format: date
title
string
>= 1 characters <= 200 characters

Example

Follow up with Sarah
appointment

An appointment with you

object
type
required
string
>= 1 characters <= 60 characters

Example

consultation
startsAt
required
string format: date-time
endsAt
string format: date-time
notifyChannel
string
nullable
Allowed values: email sms whatsapp
Media typeapplication/json
object
contactId
required
string format: uuid
threadId
required
string format: uuid
inboxId
required

The inbox it went to

string format: uuid
messageId
required
string format: uuid
isLead
required

Counted as a new lead (false when the same contact, source and item already sent one to the same inbox within the lead period)

boolean
createdContact
required
boolean
createdThread
required
boolean
duplicate
required

The externalId was already received; nothing new was stored

boolean
matchedBy
required

How the contact was matched; null for a new contact

string
nullable
Allowed values: id external_id email phone
conflict
required
One of:
object
emailContactId
required
string format: uuid
phoneContactId
required

The phone number is also on this contact

string format: uuid
task
required
One of:
object
id
required
string format: uuid
threadId
required
string format: uuid
nullable
inboxId
required

The thread’s inbox

string format: uuid
nullable
contact
required
One of:
object
id
required
string format: uuid
name
required
string
nullable
title
required
string
note
required
string
nullable
dueAt
required

When it is due; for an all-day task, 9am on its day (when it is reminded)

string format: date-time
dueAllDay
required
boolean
timeZone
required

The time zone its day belongs to

string
dueState
required

Against today, in your time zone

string
Allowed values: overdue today upcoming done
dueLabel
required
string
assignee
required
object
id
required
string
name
required
string
avatarUrl
required

Their picture; null to show their initials

string
nullable
completedAt
required
string format: date-time
nullable
completedBy
required
One of:
object
id
required
string
name
required
string
createdBy
required
One of:
object
id
required
string
name
required
string
origin
required
string
Allowed values: person api flow ai
canEdit
required

You can change, complete and delete it

boolean
createdAt
required
string format: date-time
updatedAt
required
string format: date-time
appointment
required
One of:
object
id
required
string format: uuid
threadId
required
string format: uuid
inboxId
required

The thread’s inbox

string format: uuid
contact
required
One of:
object
id
required
string format: uuid
name
required
string
nullable
type
required
string
typeLabel
required
string
startsAt
required
string format: date-time
endsAt
required
string format: date-time
timeZone
required

The time zone it is written in

string
status
required
string
Allowed values: booked confirmed cancelled no_show completed
host
required
One of:
object
id
required
string
name
required
string
locationId
required
string format: uuid
nullable
item
required
One of:
object
id
required
string format: uuid
title
required
string
notes
required
string
nullable
notifyChannel
required

The channel the customer is told on (null: not told)

string
nullable
Allowed values: email sms whatsapp
notifyFailure
required

Why the latest message to the customer wasn’t sent, if it wasn’t

string
nullable
cancelledReason
required

Why it was cancelled (for the team)

string
nullable
createdBy
required
One of:
object
id
required
string
name
required
string
canEdit
required

You can move, cancel and mark it

boolean
createdAt
required
string format: date-time
updatedAt
required
string format: date-time

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"
}
}
}
Media typeapplication/json
object
statusCode
required
number
code
required

Stable machine-readable reason

string
message
required
string
errors

With code invalid: every problem with the request, by field (message is the first)

Array<object>
object
path
required

Dotted path inside the item, e.g. attributes.price.amountMinor

string
message
required
string

Example

{
"statusCode": 403,
"code": "forbidden",
"message": "You do not have permission to do that.",
"errors": [
{
"message": "Enter the make"
}
]
}

Missing or invalid API key

Media typeapplication/json
object
statusCode
required
number
code
required

Stable machine-readable reason

string
message
required
string
errors

With code invalid: every problem with the request, by field (message is the first)

Array<object>
object
path
required

Dotted path inside the item, e.g. attributes.price.amountMinor

string
message
required
string

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)

Media typeapplication/json
object
statusCode
required
number
code
required

Stable machine-readable reason

string
message
required
string
feature
required

The feature that needs Pro

string
nullable
Allowed values: extraSeats connections aiAssistant aiReplies rules fullReports api inboxes bulkMessages videoMessages
upgradePath
required

Where to upgrade or pay, in the web app

string

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)

Media typeapplication/json
object
statusCode
required
number
code
required

Stable machine-readable reason

string
message
required
string
errors

With code invalid: every problem with the request, by field (message is the first)

Array<object>
object
path
required

Dotted path inside the item, e.g. attributes.price.amountMinor

string
message
required
string

Example

{
"statusCode": 403,
"code": "forbidden",
"message": "You do not have permission to do that.",
"errors": [
{
"message": "Enter the make"
}
]
}

Rate limit reached

Media typeapplication/json
object
statusCode
required
number
code
required

Stable machine-readable reason

string
message
required
string
retryAfter
required

Seconds until another request will be accepted

number

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
}