A reference guide for building AI agents: every method, how to authenticate, and the permissions each one needs.
The Crisp API is how an app or AI agent works with a Crisp support workspace: listing conversations, sending a message to a customer, moving a conversation to resolved, or creating and updating contact profiles in the CRM. Access is granted through a plugin token, which carries fine-grained scopes set per area at read or write level, so a token reaches only the conversations, messages, contacts, or settings it was granted. Crisp can also push events to a plugin's endpoint, so an integration learns about new messages and state changes without polling.
How an app or AI agent connects to Crisp determines what it can reach. There is a route for making calls, a route for receiving events, and a hosted server that exposes Crisp tools to agents, and each is governed by the token behind it and the scopes that token carries.
The REST API takes and returns JSON over HTTPS at https://api.crisp.chat/v1, with list routes paged by a page number in the path. A call authenticates with a plugin or user token sent as HTTP Basic auth, the token identifier and key joined by a colon and base64-encoded, alongside an X-Crisp-Tier header naming the tier (plugin or user). Most routes are scoped to one website by its website_id.
Crisp POSTs a JSON body to a plugin's registered HTTPS endpoint when a subscribed event fires, like message:send or session:set_state. The receiver verifies the X-Crisp-Signature header, an HMAC-SHA256 over the request timestamp and body computed with the plugin's signing secret, and must answer 200 or Crisp retries delivery.
Crisp hosts a Model Context Protocol server that exposes a subset of its REST API to an AI agent, so an agent can retrieve workspace information and perform actions like managing the inbox and CRM. It is connected from an AI client and authenticated with Crisp plugin credentials, and the scopes on those credentials still bound what the agent can reach.
A plugin token is created in the Crisp Marketplace and can act across every workspace that installs the plugin. It carries fine-grained scopes, each requested at a read-only, read-and-write, or write-only level, so the token reaches only the areas it was granted. Scopes apply to production tokens; development tokens are unrestricted but for local use only. The token is permanent and reused on every request.
A website token is generated directly in the Crisp app for a single workspace. It is not scope-limited the way a plugin token is, and it carries a higher default daily quota. It authenticates the same way, as Basic auth from the identifier and key, but with the user tier.
The Crisp API is split into areas an agent can act on, like conversations, messages, contact profiles, website settings, operators, visitors, and campaigns. Each area maps to its own plugin scope, and writing a message or changing a conversation state reaches a real customer.
Methods for listing, creating, reading, and removing conversations, each modeled as a session.
Methods for reading and sending the messages inside a conversation.
Methods for changing a conversation's state, metadata, assignment, and block status.
Methods for listing, reading, creating, and updating CRM contact profiles.
Methods for reading and updating a website's configuration.
Methods for listing and reading the operators (team members) on a website.
Methods for listing and counting the visitors currently browsing.
Methods for listing and dispatching marketing campaigns.
Filter by method, access, or permission, or search any path. Select a row for version detail, rate limits, the related webhook event, and the source.
| Method | Endpoint | What it does | Access | Permission | Version | |
|---|---|---|---|---|---|---|
ConversationsMethods for listing, creating, reading, and removing conversations, each modeled as a session.4 | ||||||
| GET | /website/{website_id}/conversations/{page_number} | List conversations for a website, one page at a time. | read | website:conversation:sessions | Current | |
Read on the website:conversation:sessions scope. A plugin token needs this scope granted to list sessions. Acts onconversation Permission (capability) website:conversation:sessionsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /website/{website_id}/conversation | Create a new conversation (session) on a website. | write | website:conversation:sessions | Current | |
Write on the website:conversation:sessions scope. Returns the new session_id used for every later call on the conversation. Acts onconversation Permission (capability) website:conversation:sessionsVersionAvailable since the API’s base version Webhook event session-request-initiatedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /website/{website_id}/conversation/{session_id} | Get a single conversation by its session id. | read | website:conversation:sessions | Current | |
Read on the website:conversation:sessions scope. Acts onconversation Permission (capability) website:conversation:sessionsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /website/{website_id}/conversation/{session_id} | Permanently remove a conversation. | write | website:conversation:sessions | Current | |
Write on the website:conversation:sessions scope. Irreversible; the conversation and its messages are deleted. Acts onconversation Permission (capability) website:conversation:sessionsVersionAvailable since the API’s base version Webhook event session-removedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
MessagesMethods for reading and sending the messages inside a conversation.5 | ||||||
| GET | /website/{website_id}/conversation/{session_id}/messages | Get the messages in a conversation, paginated by timestamp. | read | website:conversation:messages | Current | |
Read on the website:conversation:messages scope. Acts onmessage Permission (capability) website:conversation:messagesVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /website/{website_id}/conversation/{session_id}/message | Send a message in a conversation, as text, file, or another type. | write | website:conversation:messages | Current | |
Write on the website:conversation:messages scope. A message sent from a plugin as an operator fires message:received to the visitor. Acts onmessage Permission (capability) website:conversation:messagesVersionAvailable since the API’s base version Webhook event message-receivedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /website/{website_id}/conversation/{session_id}/message/{fingerprint} | Get a single message in a conversation by its fingerprint. | read | website:conversation:messages | Current | |
Read on the website:conversation:messages scope. Acts onmessage Permission (capability) website:conversation:messagesVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /website/{website_id}/conversation/{session_id}/message/{fingerprint} | Update the content of a message already in a conversation. | write | website:conversation:messages | Current | |
Write on the website:conversation:messages scope. Acts onmessage Permission (capability) website:conversation:messagesVersionAvailable since the API’s base version Webhook event message-updatedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /website/{website_id}/conversation/{session_id}/read | Mark messages in a conversation as read. | write | website:conversation:messages | Current | |
Write on the website:conversation:messages scope. Acts onmessage Permission (capability) website:conversation:messagesVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Conversation state & routingMethods for changing a conversation's state, metadata, assignment, and block status.6 | ||||||
| GET | /website/{website_id}/conversation/{session_id}/state | Get the current state of a conversation (pending, unresolved, or resolved). | read | website:conversation:states | Current | |
Read on the website:conversation:states scope. Acts onconversation_state Permission (capability) website:conversation:statesVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /website/{website_id}/conversation/{session_id}/state | Change a conversation's state, for example to resolved. | write | website:conversation:states | Current | |
Write on the website:conversation:states scope. Moving a conversation to resolved fires session:set_state. Acts onconversation_state Permission (capability) website:conversation:statesVersionAvailable since the API’s base version Webhook event session-set-stateRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /website/{website_id}/conversation/{session_id}/meta | Get the metadata on a conversation, like the contact's nickname, email, and segments. | read | website:conversation:sessions | Current | |
Read on the website:conversation:sessions scope. Acts onconversation_meta Permission (capability) website:conversation:sessionsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /website/{website_id}/conversation/{session_id}/meta | Update a conversation's metadata, such as the contact's email, nickname, or segments. | write | website:conversation:sessions | Current | |
Write on the website:conversation:sessions scope. Setting an email or nickname fires the matching session:set_* event. Acts onconversation_meta Permission (capability) website:conversation:sessionsVersionAvailable since the API’s base version Webhook event session-set-emailRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /website/{website_id}/conversation/{session_id}/routing | Assign a conversation to an operator or team for routing. | write | website:conversation:sessions | Current | |
Write on the website:conversation:sessions scope. Sets which operator the conversation is assigned to. Acts onconversation_routing Permission (capability) website:conversation:sessionsVersionAvailable since the API’s base version Webhook event session-set-routingRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /website/{website_id}/conversation/{session_id}/block | Block or unblock incoming messages from a conversation. | write | website:conversation:sessions | Current | |
Write on the website:conversation:sessions scope. Blocking stops further incoming messages from the visitor. Acts onconversation_block Permission (capability) website:conversation:sessionsVersionAvailable since the API’s base version Webhook event session-set-blockRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
People (Contacts)Methods for listing, reading, creating, and updating CRM contact profiles.7 | ||||||
| GET | /website/{website_id}/people/profiles/{page_number} | List the CRM contact profiles on a website, one page at a time. | read | website:people:profiles | Current | |
Read on the website:people:profiles scope. Supports search and filtering by segment, data, and text. Acts onpeople_profile Permission (capability) website:people:profilesVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /website/{website_id}/people/profile | Add a new CRM contact profile. | write | website:people:profiles | Current | |
Write on the website:people:profiles scope. Creating a profile fires people:profile:created. Acts onpeople_profile Permission (capability) website:people:profilesVersionAvailable since the API’s base version Webhook event people-profile-createdRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /website/{website_id}/people/profile/{people_id} | Get a single CRM contact profile by its people id. | read | website:people:profiles | Current | |
Read on the website:people:profiles scope. Acts onpeople_profile Permission (capability) website:people:profilesVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /website/{website_id}/people/profile/{people_id} | Update fields on a CRM contact profile. | write | website:people:profiles | Current | |
Write on the website:people:profiles scope. Updating a profile fires people:profile:updated. Acts onpeople_profile Permission (capability) website:people:profilesVersionAvailable since the API’s base version Webhook event people-profile-updatedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PUT | /website/{website_id}/people/profile/{people_id} | Save a CRM contact profile, replacing its full card. | write | website:people:profiles | Current | |
Write on the website:people:profiles scope. A PUT replaces the whole profile card rather than patching fields. Acts onpeople_profile Permission (capability) website:people:profilesVersionAvailable since the API’s base version Webhook event people-profile-updatedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| DELETE | /website/{website_id}/people/profile/{people_id} | Remove a CRM contact profile. | write | website:people:profiles | Current | |
Write on the website:people:profiles scope. Irreversible; fires people:profile:removed. Acts onpeople_profile Permission (capability) website:people:profilesVersionAvailable since the API’s base version Webhook event people-profile-removedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /website/{website_id}/people/data/{people_id} | Get the custom data stored on a contact profile. | read | website:people:profiles | Current | |
Read on the website:people:profiles scope. Acts onpeople_data Permission (capability) website:people:profilesVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Website settingsMethods for reading and updating a website's configuration.2 | ||||||
| GET | /website/{website_id}/settings | Get a website's settings. | read | website:settings | Current | |
Read on the website:settings scope. Acts onwebsite_settings Permission (capability) website:settingsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| PATCH | /website/{website_id}/settings | Update a website's settings. | write | website:settings | Current | |
Write on the website:settings scope. Changes apply to the whole workspace. Acts onwebsite_settings Permission (capability) website:settingsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
OperatorsMethods for listing and reading the operators (team members) on a website.2 | ||||||
| GET | /website/{website_id}/operators/list | List the operators (team members) on a website. | read | website:operators | Current | |
Read on the website:operators scope. Acts onoperator Permission (capability) website:operatorsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /website/{website_id}/operator/{user_id} | Get a single operator on a website by user id. | read | website:operators | Current | |
Read on the website:operators scope. Acts onoperator Permission (capability) website:operatorsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
VisitorsMethods for listing and counting the visitors currently browsing.2 | ||||||
| GET | /website/{website_id}/visitors/list/{page_number} | List the visitors currently browsing a website. | read | website:visitors | Current | |
Read on the website:visitors scope. Acts onvisitor Permission (capability) website:visitorsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| GET | /website/{website_id}/visitors/count | Count the visitors currently browsing a website. | read | website:visitors | Current | |
Read on the website:visitors scope. Acts onvisitor Permission (capability) website:visitorsVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
CampaignsMethods for listing and dispatching marketing campaigns.2 | ||||||
| GET | /website/{website_id}/campaigns/list/{page_number} | List the marketing campaigns on a website. | read | website:campaign:templates | Current | |
Read on the website:campaign:templates scope. Acts oncampaign Permission (capability) website:campaign:templatesVersionAvailable since the API’s base version Webhook eventNone Rate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
| POST | /website/{website_id}/campaign/{campaign_id}/dispatch | Dispatch a campaign so it is sent to its audience. | write | website:campaign:templates | Current | |
Write on the website:campaign:templates scope. Dispatching sends the campaign to contacts. Acts oncampaign Permission (capability) website:campaign:templatesVersionAvailable since the API’s base version Webhook event campaign-dispatchedRate limitStandard limits apply SourceOfficial documentation ↗ | ||||||
Crisp can notify an app when something happens in a workspace, like a visitor sending a message or a conversation moving to resolved. A plugin registers for the events it wants and Crisp posts each one to the plugin's HTTPS endpoint, so an integration learns about activity without polling.
| Event | What it signals | Triggered by |
|---|---|---|
message:send | A message was sent from a visitor and received by operators. This is the primary signal that a customer has written in. | In-app only |
message:received | A message was sent from an operator and received by the visitor, including messages an integration sends. | /website/{website_id}/conversation/{session_id}/message |
message:updated | A message in a conversation was updated. | /website/{website_id}/conversation/{session_id}/message/{fingerprint} |
session:request:initiated | A session was initiated, meaning a conversation was started. | /website/{website_id}/conversation |
session:set_state | A conversation's state changed, for example to resolved. | /website/{website_id}/conversation/{session_id}/state |
session:set_email | An email address was set on a conversation's contact. | /website/{website_id}/conversation/{session_id}/meta |
session:set_routing | A routing assignment was set on a conversation. | /website/{website_id}/conversation/{session_id}/routing |
session:set_block | A conversation was blocked or unblocked. | /website/{website_id}/conversation/{session_id}/block |
session:removed | A session was removed, meaning a conversation was deleted. | /website/{website_id}/conversation/{session_id} |
people:profile:created | A people (CRM contact) profile was created. | /website/{website_id}/people/profile |
people:profile:updated | A people (CRM contact) profile was updated. | /website/{website_id}/people/profile/{people_id}/website/{website_id}/people/profile/{people_id} |
people:profile:removed | A people (CRM contact) profile was removed. | /website/{website_id}/people/profile/{people_id} |
campaign:dispatched | A campaign was dispatched to its audience. | /website/{website_id}/campaign/{campaign_id}/dispatch |
Crisp limits how much an app can call through a daily request quota on a plugin token, with separate global and per-route limiters guarding the platform underneath.
Crisp guards the API with several limiters. A permanent plugin token is governed by a daily request quota, 5,000 requests per day by default, and a website token by a higher 10,000 per day; a plugin quota can be raised on request in the Marketplace. Underneath, a global limiter keyed by IP and identifier and a per-route limiter apply to non-plugin traffic, and a permissive edge load-balancer limit sits on top. GET and HEAD requests pass through a cache layer that lets them be called more often without counting the same way, with a Bloom-Status response header showing MISS, HIT, DIRECT, or OFFLINE. Going over returns HTTP 429, or 420 from the calm limiter.
List routes page through results with a page number in the path, like /conversations/{page_number} or /people/profiles/{page_number}, starting at page 1 and incrementing until a page returns fewer results. Conversation messages page instead by a timestamp_before query value, fetching older messages before a given point.
List pages return a fixed number of items per page set by the route, so an integration walks pages rather than asking for a custom page size. Message history is retrieved in timestamp-bounded windows rather than one unbounded list.
The status codes an agent should handle, and what to do about each.
| Status | Code | Meaning | What to do |
|---|---|---|---|
| 400 | invalid_data | The submitted data was rejected by the route's schema validators. | Read the reason in the response body, fix the request fields, and resend. |
| 401 | invalid_session | The token was missing or not encoded correctly as Basic auth, so the request was not authenticated. | Base64-encode the identifier and key joined by a colon, send it as Basic auth, and include the X-Crisp-Tier header. |
| 403 | not_allowed | The token is valid but lacks the scope or permission the route needs. | Grant the route's scope to the plugin token at the right read or write level and retry. |
| 404 | not_found | The requested resource, like a conversation or profile, does not exist or is not visible to this token. | Verify the website_id, session_id, or people_id and that the resource lives in this workspace. |
| 409 | conflict | The request conflicts with the current state of the resource, such as creating something that already exists. | Re-check the resource state and adjust the request before retrying. |
| 429 | rate_limited | Too many requests, or a plugin token's daily quota was exhausted. Crisp may also return 420 for its calm limiter. | Back off and retry later, lean on the GET cache layer, and request a quota increase in the Marketplace if needed. |
Crisp serves a single, continuously updated version of its REST API, with no dated version to pin, and ships changes through its product updates rather than new dated versions.
Crisp serves a single, continuously updated version of its REST API under the v1 path, with no dated version to pin. New routes and fields are added in place and announced through Crisp's product updates, so an integration tracks the updates rather than upgrading a version header.
An additive platform update extended the API surface without changing the v1 version, adding programmatic control of the Hugo AI agent and hardening the MCP server.
There is one current API; track Crisp's product updates for changes.
Crisp product updates ↗Bollard AI sits between a team's AI agents and Crisp. Grant each agent exactly the access it needs, read or write, resource by resource, and every call is checked and logged.