# BlueMarlin API

BlueMarlin is the WhatsApp workspace for small businesses: a team inbox, campaigns, flexible business data (collections of records) and an AI agent on WhatsApp. This API reads and writes its collections of records (contacts, deals, anything the workspace tracks), tags, WhatsApp conversations and messages, WhatsApp templates, the do-not-contact list and the outbound webhook settings.

**Authentication.** Send `Authorization: Bearer bm_live_…` on every request. An owner or admin of the workspace creates keys in the app at https://bluemarlinchat.com (Settings → API keys), with full access or a chosen set of scopes; a key is shown once, so store it then. A key belongs to one workspace — never send `x-organization-id` with it (400 `conflicting_org_scope`). Each operation ends with the scope it needs ("Requires the `records:read` scope.") and lists it under `security`; a key holding `*` or `<resource>:*` also passes. A missing scope answers 403 `insufficient_scope`. An API key acts for the workspace, not for a person: it sees shared collections and shared WhatsApp lines only, and actions that need a person answer 403 `user_required`. `GET /v1/me` tells you which workspace and scopes a key has.

**Conventions.** Responses name their type in `object` (a few older ones return a bare body; each operation documents its shape). Lists are `{ object: "list", data, has_more, total?, next_cursor? }`: pass `limit`, then `next_cursor` back as `after` until `has_more` is false (`offset` is a legacy fallback). A deletion answers `{ object: "deleted", id }`, a count `{ object: "count", count }`. Errors are `{ error: { type, code?, message, param? } }`. Something you cannot see is 404, never 403. Ids are UUID v7 (time-ordered), timestamps ISO 8601 in UTC, phone numbers E.164 with the `+` (`+34652225000`); a resource that stores a phone also carries `phoneCountry`, ISO 3166-1 alpha-2 (`ES`). An API key may make 100 requests per 10 seconds (429 beyond that). The `/whatsapp/{version}/…` operations take the same keys but are the exception to these conventions: they mirror Meta's Cloud API — keep your Cloud API payloads and SDK, change the base URL, use the key as the token — with Meta's shapes, errors included, and no rate limit.

**Docs for agents.** The same contract as Markdown: https://bluemarlinchat.com/llms.txt (index), https://bluemarlinchat.com/llms-full.txt (everything in one file), https://bluemarlinchat.com/api/docs.md (operation index) and one page per operation at https://bluemarlinchat.com/api/docs/<slug>.md. WhatsApp template authoring rules: https://bluemarlinchat.com/api/docs/whatsapp-templates.md.

This reference: 58 operations and 9 webhook events, generated from the OpenAPI contract.

- Base URL: `https://api.bluemarlinchat.com`
- Index for agents: https://bluemarlinchat.com/llms.txt — every page, one line each
- Getting started: https://bluemarlinchat.com/api/docs.md — authentication, conventions and every operation, one page each
- Full reference: https://bluemarlinchat.com/llms-full.txt — every operation and guide in one Markdown file
- OpenAPI 3.1: https://api.bluemarlinchat.com/v1/openapi.json — the machine-readable contract
- Interactive reference: https://bluemarlinchat.com/api/docs — the page for people

## Scopes

A key can hold: `collections:read`, `records:read`, `records:write`, `tags:read`, `tags:write`, `conversations:read`, `conversations:write`, `whatsapp:read`, `whatsapp:write`, `files:read`, `files:write`, `members:read`, `settings:read`, `settings:write`. Full access means all of them.

## Errors

Every `/v1` error has this JSON shape:

- `error` (object, required)
  - `type` (string, required) — One of: `invalid_request_error`, `authentication_error`, `permission_error`, `not_found`, `conflict`, `rate_limit_error`, `api_error`.
  - `code` (string)
  - `message` (string, required)
  - `param` (string)

Any operation can answer:

- `400` — Invalid request (validation, missing parameter); `param` names the field.
- `401` — Missing or invalid API key.
- `403` — Not allowed: missing scope (`insufficient_scope`), a person is required (`user_required`), or a policy refuses it.
- `404` — Not found, or not visible to this caller (never 403 for that).
- `429` — Rate limited: 100 requests per 10 seconds per API key.
- `502` — WhatsApp failed the send; the message is kept as `failed`.

## Webhooks

Configure them with `GET /v1/webhooks`, `PUT /v1/webhooks`, `POST /v1/webhooks/regenerate-secret`, `POST /v1/webhooks/test`. The events:

- [`message.sent`](https://bluemarlinchat.com/api/docs/webhook-message-sent.md) — An outbound message was sent
- [`contact.unsubscribed`](https://bluemarlinchat.com/api/docs/webhook-contact-unsubscribed.md) — A contact was blocked or opted out
- [`contact.resubscribed`](https://bluemarlinchat.com/api/docs/webhook-contact-resubscribed.md) — A block or opt-out was lifted
- [`record.created`](https://bluemarlinchat.com/api/docs/webhook-record-created.md) — A record was created
- [`record.updated`](https://bluemarlinchat.com/api/docs/webhook-record-updated.md) — A record was updated
- [`record.deleted`](https://bluemarlinchat.com/api/docs/webhook-record-deleted.md) — A record was deleted
- [`whatsapp.message`](https://bluemarlinchat.com/api/docs/webhook-whatsapp-message.md) — Inbound WhatsApp message (relayed from Meta)
- [`whatsapp.status`](https://bluemarlinchat.com/api/docs/webhook-whatsapp-status.md) — Message delivery receipt (relayed from Meta)
- [`whatsapp.template`](https://bluemarlinchat.com/api/docs/webhook-whatsapp-template.md) — Template status change (relayed from Meta)

Each delivery is a POST with a JSON body and the headers `X-BlueMarlin-Event` (the event), `X-BlueMarlin-Timestamp`, `X-BlueMarlin-Delivery-Attempt` and `X-Webhook-Signature-256: sha256=<hex>` — the HMAC-SHA256 of the raw body with your webhook secret. Verify it before parsing:

```js
const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex");
```

Any 2xx acknowledges; retries and how to stop them are on each event's page.

## REST API or MCP

- REST (this reference): your code or another system, with an API key, acting as the workspace.
- MCP server `https://mcp.bluemarlinchat.com/mcp`: an AI assistant (Claude, ChatGPT…) acting for a person. The person signs in and consents in the browser (OAuth) and picks the workspaces; the assistant then gets BlueMarlin's own tools, within that person's role and what they can see. Claude Code: `claude mcp add --transport http bluemarlin https://mcp.bluemarlinchat.com/mcp`.
- Only an MCP connection reaches `collections` (write), `notes` (read, write), `pages` (read, write), `knowledge` (read, write), `skills` (read, write).

# Operations

## Conversations

- [`GET /v1/conversations`](https://bluemarlinchat.com/api/docs/get-conversations.md) — List conversations — `conversations:read`
- [`GET /v1/conversations/{conversationId}`](https://bluemarlinchat.com/api/docs/get-conversation.md) — Retrieve a conversation — `conversations:read`
- [`POST /v1/conversations/{conversationId}/archive`](https://bluemarlinchat.com/api/docs/post-conversation-archive.md) — Archive a conversation — `conversations:write`
- [`POST /v1/conversations/{conversationId}/assign`](https://bluemarlinchat.com/api/docs/post-conversation-assign.md) — Assign (or unassign) a conversation to a member — `conversations:write`
- [`POST /v1/conversations/{conversationId}/handler`](https://bluemarlinchat.com/api/docs/post-conversation-handler.md) — Set the conversation handler (AI vs human) — `conversations:write`
- [`GET /v1/conversations/{conversationId}/messages`](https://bluemarlinchat.com/api/docs/get-conversation-messages.md) — List messages for a conversation — `conversations:read`
- [`POST /v1/conversations/{conversationId}/messages`](https://bluemarlinchat.com/api/docs/post-conversation-messages.md) — Send a message in a conversation — `conversations:write`
- [`GET /v1/conversations/{conversationId}/messages/{messageId}/media`](https://bluemarlinchat.com/api/docs/get-conversation-message-media.md) — Get a presigned URL for a message's media — `conversations:read`
- [`POST /v1/conversations/{conversationId}/messages/{messageId}/media`](https://bluemarlinchat.com/api/docs/post-conversation-message-media.md) — Download media from WhatsApp and store it — `conversations:write`
- [`POST /v1/conversations/{conversationId}/read`](https://bluemarlinchat.com/api/docs/post-conversation-read.md) — Mark a conversation as read — `conversations:write`
- [`POST /v1/conversations/{conversationId}/unarchive`](https://bluemarlinchat.com/api/docs/post-conversation-unarchive.md) — Unarchive a conversation — `conversations:write`
- [`POST /v1/conversations/{conversationId}/unread`](https://bluemarlinchat.com/api/docs/post-conversation-unread.md) — Mark a conversation as unread — `conversations:write`

## Tags

- [`POST /v1/tags/by-phones`](https://bluemarlinchat.com/api/docs/post-tags-by-phones.md) — Lookup tags for many phone numbers at once — `tags:read`
- [`GET /v1/records/{recordId}/tags`](https://bluemarlinchat.com/api/docs/get-record-tags.md) — List tags attached to a record — `tags:read`
- [`PUT /v1/records/{recordId}/tags`](https://bluemarlinchat.com/api/docs/put-record-tags.md) — Replace the set of tags attached to a record — `tags:write`
- [`POST /v1/records/{recordId}/tags`](https://bluemarlinchat.com/api/docs/post-record-tags.md) — Attach a tag to a record — `tags:write`
- [`DELETE /v1/records/{recordId}/tags/{tagId}`](https://bluemarlinchat.com/api/docs/delete-record-tag.md) — Detach a tag from a record — `tags:write`
- [`GET /v1/tags`](https://bluemarlinchat.com/api/docs/get-tags.md) — List tags — `tags:read`
- [`POST /v1/tags`](https://bluemarlinchat.com/api/docs/post-tags.md) — Create a tag — `tags:write`
- [`PATCH /v1/tags/{tagId}`](https://bluemarlinchat.com/api/docs/patch-tag.md) — Update a tag — `tags:write`
- [`DELETE /v1/tags/{tagId}`](https://bluemarlinchat.com/api/docs/delete-tag.md) — Delete a tag — `tags:write`

## Auth

- [`GET /v1/me/usage`](https://bluemarlinchat.com/api/docs/get-me-usage.md) — Month-to-date AI usage for the caller and their organization — `settings:read`
- [`GET /v1/me`](https://bluemarlinchat.com/api/docs/get-me.md) — Inspect the current authentication context

## Records

- [`GET /v1/collections/{collectionId}/records`](https://bluemarlinchat.com/api/docs/get-collection-records.md) — List records in a collection — `records:read`
- [`POST /v1/collections/{collectionId}/records`](https://bluemarlinchat.com/api/docs/post-collection-records.md) — Create a record — `records:write`
- [`GET /v1/collections/{collectionId}/records/{recordId}`](https://bluemarlinchat.com/api/docs/get-collection-record.md) — Retrieve a record — `records:read`
- [`PATCH /v1/collections/{collectionId}/records/{recordId}`](https://bluemarlinchat.com/api/docs/patch-collection-record.md) — Update a record — `records:write`
- [`DELETE /v1/collections/{collectionId}/records/{recordId}`](https://bluemarlinchat.com/api/docs/delete-collection-record.md) — Delete a record — `records:write`
- [`GET /v1/collections/{collectionId}/records/count`](https://bluemarlinchat.com/api/docs/get-collection-records-count.md) — Count records in a collection — `records:read`

## WhatsApp Cloud API (compatible)

- [`POST /whatsapp/{version}/{phone_number_id}/messages`](https://bluemarlinchat.com/api/docs/post-whatsapp-messages.md) — Send a message (Meta Cloud API compatible) — `whatsapp:write`
- [`GET /whatsapp/{version}/{waba_id}/message_templates`](https://bluemarlinchat.com/api/docs/get-whatsapp-message-templates.md) — List templates (Meta Cloud API compatible) — `whatsapp:read`
- [`POST /whatsapp/{version}/{waba_id}/message_templates`](https://bluemarlinchat.com/api/docs/post-whatsapp-message-templates.md) — Create a template (Meta Cloud API compatible) — `whatsapp:write`
- [`DELETE /whatsapp/{version}/{waba_id}/message_templates`](https://bluemarlinchat.com/api/docs/delete-whatsapp-message-templates.md) — Delete a template (Meta Cloud API compatible) — `whatsapp:write`

## Search

- [`GET /v1/search`](https://bluemarlinchat.com/api/docs/get-search.md) — Global search across records — `records:read`

## Collections

- [`GET /v1/collections`](https://bluemarlinchat.com/api/docs/get-collections.md) — List collections — `collections:read`
- [`GET /v1/collections/{collectionId}`](https://bluemarlinchat.com/api/docs/get-collection.md) — Retrieve a collection — `collections:read`

## Contact Restrictions

- [`GET /v1/contact-restrictions`](https://bluemarlinchat.com/api/docs/get-contact-restrictions.md) — List contact restrictions — `records:read`
- [`POST /v1/contact-restrictions`](https://bluemarlinchat.com/api/docs/post-contact-restrictions.md) — Add a contact restriction — `records:write`
- [`GET /v1/contact-restrictions/{phoneNumber}`](https://bluemarlinchat.com/api/docs/get-contact-restriction.md) — Get the restriction status for a phone number — `records:read`
- [`DELETE /v1/contact-restrictions/{phoneNumber}`](https://bluemarlinchat.com/api/docs/delete-contact-restriction.md) — Remove a restriction — `records:write`

## Files

- [`POST /v1/files/bulk-external`](https://bluemarlinchat.com/api/docs/post-files-bulk-external.md) — Attach external URLs as files in bulk — `files:write`
- [`GET /v1/files/download-url`](https://bluemarlinchat.com/api/docs/get-files-download-url.md) — Get a presigned URL for a storage key owned by the organization — `files:read`
- [`POST /v1/media/upload-url`](https://bluemarlinchat.com/api/docs/post-media-upload-url.md) — Presigned upload URL for WhatsApp message media — `conversations:write`

## Members

- [`GET /v1/members`](https://bluemarlinchat.com/api/docs/get-members.md) — List organization members — `members:read`

## Messages

- [`GET /v1/messages`](https://bluemarlinchat.com/api/docs/get-messages.md) — Search messages across the organization — `conversations:read`
- [`POST /v1/messages`](https://bluemarlinchat.com/api/docs/post-messages.md) — Send a message — `conversations:write`

## Webhooks

- [`GET /v1/webhooks`](https://bluemarlinchat.com/api/docs/get-webhooks.md) — Retrieve the organization's outbound webhook configuration — `settings:read`
- [`PUT /v1/webhooks`](https://bluemarlinchat.com/api/docs/put-webhooks.md) — Update the outbound webhook configuration — `settings:write`
- [`POST /v1/webhooks/regenerate-secret`](https://bluemarlinchat.com/api/docs/post-webhooks-regenerate-secret.md) — Regenerate the outbound webhook signing secret — `settings:write`
- [`POST /v1/webhooks/test`](https://bluemarlinchat.com/api/docs/post-webhooks-test.md) — Send a test event to the configured webhook URL — `settings:write`

## WhatsApp

- [`GET /v1/whatsapp/accounts`](https://bluemarlinchat.com/api/docs/get-whatsapp-accounts.md) — List WhatsApp Business accounts — `whatsapp:read`
- [`GET /v1/whatsapp/accounts/{accountId}`](https://bluemarlinchat.com/api/docs/get-whatsapp-account.md) — Retrieve a WhatsApp Business account — `whatsapp:read`
- [`GET /v1/whatsapp/accounts/{accountId}/templates`](https://bluemarlinchat.com/api/docs/get-whatsapp-account-templates.md) — List templates cached for a WhatsApp account — `whatsapp:read`
- [`POST /v1/whatsapp/accounts/{accountId}/templates`](https://bluemarlinchat.com/api/docs/post-whatsapp-account-templates.md) — Create a WhatsApp message template — `whatsapp:write`
- [`GET /v1/whatsapp/accounts/{accountId}/templates/{templateId}`](https://bluemarlinchat.com/api/docs/get-whatsapp-account-template.md) — Retrieve a WhatsApp template — `whatsapp:read`
- [`PATCH /v1/whatsapp/accounts/{accountId}/templates/{templateId}`](https://bluemarlinchat.com/api/docs/patch-whatsapp-account-template.md) — Update a WhatsApp template (Meta API + local cache) — `whatsapp:write`
- [`DELETE /v1/whatsapp/accounts/{accountId}/templates/{templateId}`](https://bluemarlinchat.com/api/docs/delete-whatsapp-account-template.md) — Delete a WhatsApp template (all language variants) — `whatsapp:write`
- [`POST /v1/whatsapp/accounts/{accountId}/templates/sync`](https://bluemarlinchat.com/api/docs/post-whatsapp-account-templates-sync.md) — Synchronize templates from Meta API to local cache — `whatsapp:write`

## Webhook events

- [`message.sent`](https://bluemarlinchat.com/api/docs/webhook-message-sent.md) — An outbound message was sent
- [`contact.unsubscribed`](https://bluemarlinchat.com/api/docs/webhook-contact-unsubscribed.md) — A contact was blocked or opted out
- [`contact.resubscribed`](https://bluemarlinchat.com/api/docs/webhook-contact-resubscribed.md) — A block or opt-out was lifted
- [`record.created`](https://bluemarlinchat.com/api/docs/webhook-record-created.md) — A record was created
- [`record.updated`](https://bluemarlinchat.com/api/docs/webhook-record-updated.md) — A record was updated
- [`record.deleted`](https://bluemarlinchat.com/api/docs/webhook-record-deleted.md) — A record was deleted
- [`whatsapp.message`](https://bluemarlinchat.com/api/docs/webhook-whatsapp-message.md) — Inbound WhatsApp message (relayed from Meta)
- [`whatsapp.status`](https://bluemarlinchat.com/api/docs/webhook-whatsapp-status.md) — Message delivery receipt (relayed from Meta)
- [`whatsapp.template`](https://bluemarlinchat.com/api/docs/webhook-whatsapp-template.md) — Template status change (relayed from Meta)

## Guides

- [WhatsApp message templates](https://bluemarlinchat.com/api/docs/whatsapp-templates.md) — How to write a template Meta approves: the rules the server checks before submitting it, categories, variables and samples, buttons, languages, review and edits, rejection reasons with their fixes, and how to send one
