Add a contact
Adds a person with their name, tags and custom fields, which templates fill in.
/v1/contacts
Adds a person with their name, tags and custom fields, which templates fill in. Somebody you text is added on their own, so this is for adding details ahead of time. A handle that is already a contact answers conflict; change that contact with PATCH /v1/contacts/{id} instead.
Needs a key with the contacts:write scope.
Send an Idempotency-Key so a retry never acts twice.
Headers
Idempotency-KeystringAny string of up to 255 characters, new for each request. A retry with the same key gets the first answer back for 24 hours, with
Idempotent-Replayed: true.Up to 255 characters.
Request body
handlestringrequiredTheir phone number (US numbers may leave off +1) or the email address their Apple ID uses.
3 to 320 characters.
first_namestring or nullThe first name. Send null or an empty string to clear it.
Up to 100 characters.
last_namestring or nullThe last name. Send null or an empty string to clear it.
Up to 100 characters.
companystring or nullThe company. Send null or an empty string to clear it.
Up to 200 characters.
tagsarray of stringsTags, at most 50 of up to 50 characters each. Capitals are kept but a tag counts once however it is spelled.
Up to 50 items.
custom_fieldsmap of stringYour own fields for templates, at most 50. Names are made template-safe (
Showing addressbecomesshowing_address); an empty value drops the field.
Example
/v1/contactscurl -X POST "https://usebluebot.com/api/v1/contacts" \
-H "Authorization: Bearer $BLUEBOT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 0f6d8c2e-5b1a-4c7e-9d3f-2a8b6e1c4d70" \
-d '{
"handle": "+15555550123",
"first_name": "Maria",
"last_name": "Alvarez",
"tags": [
"buyer"
],
"custom_fields": {
"showing": "412 Oak St"
}
}'Response
201: The new contact.
idstringrequiredA ct_ id.
Starts with
ct_.object"contact"requiredhandlestringrequiredA phone number in E.164 (
+15555550123) or an email address an Apple ID uses (iMessage only).kindstringrequiredphoneoremail.One of
phone,email.first_namestring or nullrequiredlast_namestring or nullrequiredcompanystring or nullrequiredtagsarray of stringsrequiredThe contact’s tags.
custom_fieldsmap of stringrequiredYour own fields, for templates.
stoppedbooleanrequiredTrue when the person is on your stop list. Texts to them are canceled.
stopped_attimestamp or nullrequiredWhen they were put on the stop list.
stop_sourcestring or nullrequiredHow:
reply(they texted a stop word),dashboard,api,importoradmin.One of
reply,dashboard,api,import,admin.last_servicestring or nullrequiredThe service of the newest text with them.
One of
imessage,sms.first_message_attimestamp or nullrequiredThe first text either way. Null for somebody you have never texted with, who counts as a new person.
last_message_attimestamp or nullrequiredThe newest text either way.
created_viastringrequiredWhat first added them.
One of
message,inbound,import,api,dashboard,widget,hook,mcp,assistant.created_attimestamprequiredWhen the contact was added.
{
"id": "ct_1Hq2LxW9cVbN3mKpRtYs0a",
"object": "contact",
"handle": "+15555550123",
"kind": "phone",
"first_name": "Maria",
"last_name": "Alvarez",
"company": null,
"tags": [
"buyer"
],
"custom_fields": {
"showing": "412 Oak St"
},
"stopped": false,
"stopped_at": null,
"stop_source": null,
"last_service": null,
"first_message_at": null,
"last_message_at": null,
"created_via": "api",
"created_at": "2026-11-02T15:20:00.000Z"
}Errors
Every refusal has the same envelope and a page of its own. These are the ones this operation can give.
| Code | Status | What it means |
|---|---|---|
| 401 | The request carried no key, or a key BlueBot doesn’t know: mistyped, revoked, or a test key sent to the live API. | |
| 403 | The key doesn’t carry the scope the endpoint needs. Each endpoint’s reference names its scope. | |
| 429 | Too many requests. A key may make 120 requests a minute. The RateLimit header says how many are left, and Retry-After how long to wait. | |
| 500 | Something went wrong on BlueBot’s side. It was reported. Send the request again; with an Idempotency-Key that is always safe. | |
| 400 | Something in the request doesn’t fit: a missing or malformed field, a bad id, or a combination the endpoint refuses. details names each field. | |
| 409 | The request doesn’t fit the thing’s current state, or another request with the same Idempotency-Key is still running. | |
| 409 | The Idempotency-Key was used in the last 24 hours with a different request. A key stands for one request. |
Try it
Call this operation from the page with your own key. The key stays in this tab and is forgotten when you leave.