Skip to main content
A contact is a person saved in your Blujay workspace, identified by a US phone number, with an optional email address and name. Contacts carry tags, a stage and an assigned team member, and Blujay links them to their chats and messages. Use these endpoints to keep Blujay in sync with your own system: create or update contacts as they change on your side, look them up before you send, and set their tags, stage and assignee. Opt-out endpoints are covered in Opt-outs.

Create a contact

POST /v1/contacts creates a contact. phone_number is required; email and name are optional. A contact created through the API always has a phone number. Contacts created in the Blujay app can have only an email; the API returns them with no phone_number. Sends take phone numbers, so an email-only contact can’t be messaged through the API until a number is added. Blujay accepts any US number written with 10 digits, or 11 digits starting with 1, with or without +, spaces, dashes or parentheses: +15555550123, 15555550123, (555) 555-0123 and 555-555-0123 are the same number. A + followed by anything other than 1 is rejected. Blujay stores and returns the number in E.164 format (+15555550123). Anything else returns 400 VALIDATION_ERROR.
curl
Response (201)
If the phone number or email already belongs to a contact in your workspace, you get 409 CONTACT_ALREADY_EXISTS. To create or update in one call, use import instead. Creating a contact doesn’t send a webhook and doesn’t start workflows that run when a contact is created. This endpoint doesn’t use an Idempotency-Key; a repeated create returns 409, so treat that as success when you are retrying.

Create or update with import

POST /v1/contacts/import takes the same body as create and matches on the phone number:
  • If no contact has this phone number, Blujay creates one and returns 201 with "created": true.
  • If a contact has this phone number, Blujay updates its email and name with the values you send and returns 200 with "updated": true.
  • If the email you send already belongs to another contact, one that doesn’t have this phone number, Blujay returns 409 CONTACT_MATCH_CONFLICT and changes nothing.
curl
Response (200)
Like create, import doesn’t send a webhook and doesn’t start workflows that run when a contact is created. Import is safe to repeat: sending the same body again updates the same contact.

Find contacts

List every contact, most recently updated first, with GET /v1/contacts. Pages hold 25 contacts by default and up to 100 with limit. When has_more is true, pass next_cursor as cursor.
curl
To look someone up, use GET /v1/contacts/search with at least one of these:
  • phone_number: an exact match, in any accepted US format.
  • email: an exact match, case-insensitive.
  • q: a case-insensitive substring match against name, phone number (as stored, +1…) and email. q=555-0123 finds nothing; q=5550123 does.
A contact matches when any of the parameters you send matches it. Search returns up to limit matches (10 by default, 25 at most), most recently updated first. Results aren’t paginated, so narrow the search rather than page it.
curl
Response (200)
URL-encode + as %2B in query strings. A raw + arrives as a space, and phone_number= 15555550123 returns VALIDATION_ERROR.
Read one contact with GET /v1/contacts/{contact_id}.

Fields to check before you send

Neither field tells you about Free Plan verification or the pre-reply rules. See Sending rules.

Update a contact

PATCH /v1/contacts/{contact_id} changes phone_number, email or name. Send at least one field; fields you leave out keep their values. A phone number or email that already belongs to another contact returns 409 CONTACT_ALREADY_EXISTS.
curl
Tags, stage and assignee have their own endpoints, described below.

Delete a contact

DELETE /v1/contacts/{contact_id} deletes the contact and returns 204 No Content.
curl
What deleting does:
  • The contact record is removed. Reading it again returns 404 NOT_FOUND.
  • Chats and messages with that person stay, and you can still read them. They no longer link to a contact, so they stop carrying this contact_id. A contact you create later with the same number gets a new contact_id.
  • An opt-out stays in place for their phone number or email, so you still can’t message them. See Opt-outs.
  • On the Free Plan, the person’s verification is removed with the contact. They have to text your number and be verified again before you can message them; see Plans.

Add a tag

POST /v1/contacts/{contact_id}/tags adds one tag (up to 100 characters) and returns the contact. tag_added is true when the tag was new to the contact and false when the contact already had it.
curl
When the tag is new, Blujay sends the contact.tag_added webhook and starts any workflows that run when that tag is added. Adding a tag the contact already has does neither. There is no endpoint to remove a tag or to list the tags in your workspace; remove tags in the Blujay app.

Mark a contact completed

PATCH /v1/contacts/{contact_id}/stage sets the contact’s stage. Completed is the only stage you can set through the API; any other value returns 400 VALIDATION_ERROR. stage_changed tells you whether the stage changed. There is no way to set a contact back to an earlier stage through the API.
curl

Assign a contact

First get the team members you can assign with GET /v1/team-members/assignable. It returns the active members of your workspace.
curl
Response (200)
Then pass the member’s id as team_member_id to PATCH /v1/contacts/{contact_id}/assignee. The response is the contact with assigned_team_member set and assignment_changed, which is true when the assignee changed. A change also sends the contact.assigned webhook.
curl
There is no endpoint to unassign a contact. contact.assigned carries assigned_team_member: null only when someone unassigns the contact in the Blujay app.

Limitations

  • US phone numbers only. Any other number returns 400 VALIDATION_ERROR.
  • One contact per phone number and one per email in a workspace: 409 CONTACT_ALREADY_EXISTS or CONTACT_MATCH_CONFLICT.
  • No tag removal, no tag list, no unassign, and no stage other than Completed through the API.
  • No filter on GET /v1/contacts by tag, stage, assignee or opted_out. Search covers name, phone number and email only, up to 25 results.
  • Creating or updating a contact doesn’t make them messageable. Free Plan verification (CONTACT_NOT_VERIFIED), the AI Agent inbound-first rule (CONTACT_MUST_TEXT_FIRST) and opt-outs (RECIPIENT_SUPPRESSED) still apply. See Plans.
  • Creating a contact sends no webhook and starts no workflow. Only a new tag does.

Next steps

Opt-outs

Stop messages to a contact or an address, and lift an opt-out.

Workflows

Enroll a contact in a manual workflow.

Sending rules

Who you can message, and what a first message can contain.

Webhook events

Hear about tag and assignment changes.