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)
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
201with"created": true. - If a contact has this phone number, Blujay updates its
emailandnamewith the values you send and returns200with"updated": true. - If the email you send already belongs to another contact, one that doesn’t have this phone number, Blujay returns
409CONTACT_MATCH_CONFLICTand changes nothing.
curl
Response (200)
Find contacts
List every contact, most recently updated first, withGET /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
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-0123finds nothing;q=5550123does.
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)
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
Delete a contact
DELETE /v1/contacts/{contact_id} deletes the contact and returns 204 No Content.
curl
- The contact record is removed. Reading it again returns
404NOT_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 newcontact_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
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 withGET /v1/team-members/assignable. It returns the active members of your workspace.
curl
Response (200)
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
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
400VALIDATION_ERROR. - One contact per phone number and one per email in a workspace:
409CONTACT_ALREADY_EXISTSorCONTACT_MATCH_CONFLICT. - No tag removal, no tag list, no unassign, and no stage other than
Completedthrough the API. - No filter on
GET /v1/contactsby tag, stage, assignee oropted_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.