curl --request POST \
--url https://app.thinnest.ai/api/v1/contacts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"phone": "+91 98765 43210",
"name": "Asha Rao",
"email": "Asha.Rao@gmail.com",
"externalId": "LSQ-42",
"tags": [
"Hot Lead",
"Sky Towers"
],
"language": "Hindi",
"consent": {
"status": "granted",
"source": "99acres enquiry form, 17 Sep"
}
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
phone: '+91 98765 43210',
name: 'Asha Rao',
email: 'Asha.Rao@gmail.com',
externalId: 'LSQ-42',
tags: ['Hot Lead', 'Sky Towers'],
language: 'Hindi',
consent: {status: 'granted', source: '99acres enquiry form, 17 Sep'}
})
};
fetch('https://app.thinnest.ai/api/v1/contacts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.thinnest.ai/api/v1/contacts"
payload = {
"phone": "+91 98765 43210",
"name": "Asha Rao",
"email": "Asha.Rao@gmail.com",
"externalId": "LSQ-42",
"tags": ["Hot Lead", "Sky Towers"],
"language": "Hindi",
"consent": {
"status": "granted",
"source": "99acres enquiry form, 17 Sep"
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"id": "cust_4e7b1c9d-2a6f-4385-b0d2-9f6a3e8c1b74",
"phone": "919876543210",
"name": "Asha Rao",
"email": "asha.rao@gmail.com",
"externalId": "LSQ-42",
"tags": [
"hot-lead",
"sky-towers"
],
"language": "Hindi",
"note": null,
"consent": {
"status": "granted",
"source": "99acres enquiry form, 17 Sep",
"recordedAt": "2026-10-06T09:40:02.311Z"
},
"source": "agent",
"acquiredFrom": null,
"createdAt": "2026-08-30T14:12:45.008Z"
}Create Contact
Creates a contact — or, when someone is already on that number, updates that person instead, so a CRM that syncs the same lead twice never doubles your list. Answers 201 for a new contact and 200 for one it updated. Creating a contact is not consent: only consent.status: "granted" with a source is. A build key may do this.
curl --request POST \
--url https://app.thinnest.ai/api/v1/contacts \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"phone": "+91 98765 43210",
"name": "Asha Rao",
"email": "Asha.Rao@gmail.com",
"externalId": "LSQ-42",
"tags": [
"Hot Lead",
"Sky Towers"
],
"language": "Hindi",
"consent": {
"status": "granted",
"source": "99acres enquiry form, 17 Sep"
}
}
'const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
phone: '+91 98765 43210',
name: 'Asha Rao',
email: 'Asha.Rao@gmail.com',
externalId: 'LSQ-42',
tags: ['Hot Lead', 'Sky Towers'],
language: 'Hindi',
consent: {status: 'granted', source: '99acres enquiry form, 17 Sep'}
})
};
fetch('https://app.thinnest.ai/api/v1/contacts', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));import requests
url = "https://app.thinnest.ai/api/v1/contacts"
payload = {
"phone": "+91 98765 43210",
"name": "Asha Rao",
"email": "Asha.Rao@gmail.com",
"externalId": "LSQ-42",
"tags": ["Hot Lead", "Sky Towers"],
"language": "Hindi",
"consent": {
"status": "granted",
"source": "99acres enquiry form, 17 Sep"
}
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text){
"id": "cust_4e7b1c9d-2a6f-4385-b0d2-9f6a3e8c1b74",
"phone": "919876543210",
"name": "Asha Rao",
"email": "asha.rao@gmail.com",
"externalId": "LSQ-42",
"tags": [
"hot-lead",
"sky-towers"
],
"language": "Hindi",
"note": null,
"consent": {
"status": "granted",
"source": "99acres enquiry form, 17 Sep",
"recordedAt": "2026-10-06T09:40:02.311Z"
},
"source": "agent",
"acquiredFrom": null,
"createdAt": "2026-08-30T14:12:45.008Z"
}Authorizations
Your API key (ta_live_…) from Settings → API keys, sent as Authorization: Bearer <key>. Keep it on a server: it can message every customer you have. A key is full, build or read-only; a request its level does not allow is refused with 403.
Headers
Developers only: the customer workspace this request acts in — its org_… id from POST /customers. Leave it out to act in your own workspace. Refused with 404 (Workspace not found.) when the id is unknown, is not one of your customers, or is not an org_… id; 410 when that customer was deleted (restore it with POST /customers/{id}/restore until it is erased); 403 when the key belongs to one customer workspace (it cannot act as another); 400 when your workspace is not a developer workspace. /customers/* and GET /customers/usage are your own workspace's and refuse the header with 400: call them without it.
"org_3fKq9TzQ1mN8vB2xR7cLpA"
Body
Their number, with or without the +; spaces are fine. Without a +, ten digits or fewer — or a leading 0 — is read as an Indian number. The number is the contact's identity: a second create on it updates the first.
"+91 98765 43210"
Their name.
120"Asha Rao"
Their email. Stored lower-cased.
200"asha.rao@gmail.com"
Your CRM's id for them, unique in your workspace. GET /contacts?externalId= finds them by it.
200"LSQ-42"
Tags, normalised as the console stores them: lower case, spaces to hyphens, duplicates dropped, 40 characters and 20 tags at most. Replaces the list.
20["hot-lead", "sky-towers"]
The language to write to them in, by its English name as the console offers it, auto to match theirs, or null.
auto, English, Hindi, Assamese, Bengali, Bodo, Dogri, French, German, Gujarati, Indonesian, Italian, Japanese, Kannada, Kashmiri, Konkani, Korean, Maithili, Malayalam, Manipuri, Marathi, Nepali, Odia, Polish, Portuguese, Punjabi, Russian, Sanskrit, Santali, Sindhi, Spanish, Swahili, Tamil, Telugu, Thai, Turkish, Urdu, Vietnamese, null "Hindi"
Free-text notes about them.
2000Their word on marketing messages, recorded with the time and where it came from — what a regulator asks.
Show child attributes
Show child attributes
Response
Someone was already on that number; they were updated.
The contact's id (cust_…).
"cust_4e7b1c9d-2a6f-4385-b0d2-9f6a3e8c1b74"
Digits only, with the country code — 919876543210.
Their name.
Their email, lower-cased.
Your CRM's id for them, unique in your workspace.
Their tags, lower-case and hyphenated.
The language to write to them in, e.g. Hindi, or auto to match theirs.
Free-text notes.
Show child attributes
Show child attributes
How you came to have them: agent (they spoke to an agent), import, api, whatsapp_app.
Where the lead came from, when recorded.
When the contact was created.