Skip to content

API overview

Every RolChat REST endpoint follows one contract: the same base URL, the same response envelope, the same pagination, the same error shape. Learn it once and every endpoint family in the endpoint reference reads the same way.

https://api.rolchat.com/api/v1/

The version is in the path. Adding optional fields, new endpoints or new query parameters with defaults is not a breaking change and does not bump the version. Breaking changes ship as v2, and deprecated v1 endpoints keep working for at least six months after they are announced (see Deprecation).

Method Header Used for
JWT bearer Authorization: Bearer <access_token> Interactive sessions (the web app, mobile apps)
API key Authorization: Bearer rcp_<secret> Machine-to-machine calls, plugin API keys
HMAC signature X-RolChat-Signature: sha256=… Inbound webhooks from your systems, see Webhooks
None - Public widget endpoints (/widget/…, /forms/…), scoped by site and origin

For integrations use an API key (a personal access token). Keys are created in Settings → Plugins → RolChat API keys, shown once and stored only as a hash. A key can never do more than the member who created it: its scopes are checked against that member’s current permissions on every call. See Authentication for JWT lifetimes, the X-Account-Id header and multi-company sessions.

A single resource:

{
"data": { "id": "cnt_01HK…", "email": "ada@example.com" },
"meta": {
"request_id": "req_01HK…"
}
}

A list:

{
"data": [ { … }, { … } ],
"meta": {
"request_id": "req_01HK…",
"count": 50,
"next_cursor": "cur_01HK…",
"prev_cursor": "cur_01HK…",
"has_more": true
}
}

Responses without a body (204 No Content after a delete) still carry the X-Request-Id header.

An error:

{
"errors": [
{
"code": "validation_failed",
"message": "Email must be valid.",
"field": "email",
"details": { "received": "not-an-email" }
}
],
"meta": { "request_id": "req_01HK…" }
}

code is machine-readable snake_case and is the value to branch on; message is English and meant for logs. field and details are present when they help and never contain personal data. The API never answers 200 with an errors array: if errors are present the status is 400 or higher. The full code list is on the Errors page.

List endpoints that can grow use cursor pagination. Ask for the first page without a cursor, then pass back the next_cursor you received, verbatim:

GET /api/v1/contacts?limit=50
GET /api/v1/contacts?limit=50&cursor=eyJrIjoi…
  • limit defaults to 50 and is capped at 200; larger values are rejected with 400 validation_failed.
  • Cursors are opaque and expire after 24 hours (400 validation_failed with code: cursor_expired).
  • Results are consistent against the snapshot taken on the first page: records deleted between pages disappear, new records appear only when you start again without a cursor.
  • A Link header with rel="next" and rel="prev" is also sent for clients that prefer it.

Send an Idempotency-Key header (a UUID or ULID, up to 255 characters) with any POST, PATCH or DELETE:

  • The first request with a key runs and its status and body are stored for 24 hours.
  • A repeat with the same key and the same body returns the stored response without running side effects again.
  • A repeat with the same key but a different body is rejected with 409 idempotency_key_conflict.

Use it for every call that creates something with side effects: sending a message, an e-mail or an SMS, placing a call, anything that bills. GET requests are always idempotent and ignore the header.

Limits are counted per minute and keyed by the account, the API key or the user (by IP address on routes that need no sign-in). The default ceilings:

Bucket Default limit Per minute
Requests without sign-in (by IP) 60 per IP
Signed-in user (dashboard) 1 000 per user
API key 1 000 per key
Public widget endpoints 60 per visitor, and 600 per network address per minute
Sign-in (/auth/login) 5 per IP
Inbound webhooks 300 per minute
MCP server 600 per minute
OAuth endpoints 600 per minute

The ceilings are the platform defaults and can be changed by the operator; your plan does not multiply them, and enterprise limits are agreed in your contract. Rate-limited endpoints return:

X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 543

When you cross a limit the response is 429 with code: rate_limited in the errors array and a Retry-After header in seconds. Wait that long before the next call. Ten failed sign-ins lock the account for 30 minutes.

Category Max request body Server timeout
Default 1 MB 30 s
Message with attachments 25 MB 30 s
CSV import 50 MB returns 202 and runs in the background
Knowledge base images 10 MB 30 s
Bulk API 10 MB 30 s
Search 1 MB 10 s
AI requests 1 MB 60 s
Widget endpoints 100 KB 30 s

Oversized bodies get 413 payload_too_large. A timed-out upstream answers 504 with code: upstream_timeout and a retry_after hint when a retry can help.

  • Requests and responses are application/json; charset=utf-8. File uploads use multipart/form-data; CSV exports return text/csv. Invalid UTF-8 is rejected with 400 validation_failed.
  • Timestamps are ISO-8601 in UTC with millisecond precision: 2026-04-20T14:23:45.123Z. Any ISO-8601 offset is accepted on input and normalised to UTC.
  • Identifiers are opaque ULIDs or UUIDs. There are no numeric IDs, and e-mail addresses or phone numbers never appear in a URL.

Every authenticated request runs inside one account. A resource that belongs to another account, or to a site the caller’s role does not cover, answers 404 not_found rather than 403, so that the existence of records is never leaked. A valid token without the required permission (for example contacts.delete) answers 403 forbidden. Some permissions carry a scope suffix (conversations.read:own, :team, :all) that narrows what a list returns.

PATCH accepts only writable fields. id, account_id, created_at, updated_at and state fields such as resolved_at are read-only; sending one returns 400 validation_failed with details.forbidden_fields. State changes go through dedicated actions such as POST /tickets/:id/resolve.

Every write is recorded in the account audit log.

Every response carries:

X-Request-Id: req_01HK…

Log X-Request-Id. It is the first thing support asks for.

The browser dashboard origin is https://app.rolchat.com. Public widget endpoints allow any origin, or only the origins you verify for a site. Server-to-server calls with an API key are not subject to CORS.

  1. The change is announced in the changelog and the affected endpoint starts sending Deprecation: true and a Sunset header with the removal date.
  2. Public endpoints keep working for at least six months after the announcement.
  3. Removal happens only in a new major version. Inside v1 changes are additive.

Every service exposes /healthz (liveness) and /readyz (readiness).