Skip to the page

API reference

Add a contact

Adds a person with their name, tags and custom fields, which templates fill in.

POST/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. Keys and scopes

Send an Idempotency-Key so a retry never acts twice. Idempotency

Headers

  • Idempotency-Keystring

    Any 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

  • handlestringrequired

    Their phone number (US numbers may leave off +1) or the email address their Apple ID uses.

    3 to 320 characters.

  • first_namestring or null

    The first name. Send null or an empty string to clear it.

    Up to 100 characters.

  • last_namestring or null

    The last name. Send null or an empty string to clear it.

    Up to 100 characters.

  • companystring or null

    The company. Send null or an empty string to clear it.

    Up to 200 characters.

  • tagsarray of strings

    Tags, 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 string

    Your own fields for templates, at most 50. Names are made template-safe (Showing address becomes showing_address); an empty value drops the field.

Example

POST/v1/contactsAdd a contact →
curl -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.

  • idstringrequired

    A ct_ id.

    Starts with ct_.

  • object"contact"required
  • handlestringrequired

    A phone number in E.164 (+15555550123) or an email address an Apple ID uses (iMessage only).

  • kindstringrequired

    phone or email.

    One of phone, email.

  • first_namestring or nullrequired
  • last_namestring or nullrequired
  • companystring or nullrequired
  • tagsarray of stringsrequired

    The contact’s tags.

  • custom_fieldsmap of stringrequired

    Your own fields, for templates.

  • stoppedbooleanrequired

    True when the person is on your stop list. Texts to them are canceled.

  • stopped_attimestamp or nullrequired

    When they were put on the stop list.

  • stop_sourcestring or nullrequired

    How: reply (they texted a stop word), dashboard, api, import or admin.

    One of reply, dashboard, api, import, admin.

  • last_servicestring or nullrequired

    The service of the newest text with them.

    One of imessage, sms.

  • first_message_attimestamp or nullrequired

    The first text either way. Null for somebody you have never texted with, who counts as a new person.

  • last_message_attimestamp or nullrequired

    The newest text either way.

  • created_viastringrequired

    What first added them.

    One of message, inbound, import, api, dashboard, widget, hook, mcp, assistant.

  • created_attimestamprequired

    When the contact was added.

Response201
{
  "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.

CodeStatusWhat it means
unauthorized401The request carried no key, or a key BlueBot doesn’t know: mistyped, revoked, or a test key sent to the live API.
insufficient_scope403The key doesn’t carry the scope the endpoint needs. Each endpoint’s reference names its scope.
rate_limited429Too 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.
internal500Something went wrong on BlueBot’s side. It was reported. Send the request again; with an Idempotency-Key that is always safe.
invalid_request400Something in the request doesn’t fit: a missing or malformed field, a bad id, or a combination the endpoint refuses. details names each field.
conflict409The request doesn’t fit the thing’s current state, or another request with the same Idempotency-Key is still running.
idempotency_conflict409The 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.