Integrate

REST API

Your own systems can call the Core API with API keys created in the admin console. Every endpoint is listed in the API reference.

#Base URL

All endpoints live under a versioned path:

Text
https://api.2people.chat/api/v1

Request and response bodies are JSON (camelCase field names); a few upload endpoints take multipart/form-data, and export endpoints return CSV or plain text. Ids are UUIDs. Requests with fields that are not defined are rejected.

#Creating an API key

  1. Open the API keys page

    In the admin console go to your tenant’s API keys page (requires settings:write).

  2. Choose a name, an expiry date and scopes

    Scopes are tenant permissions (e.g. conversations:read, analytics:read). You can only grant permissions you hold yourself. members:write and roles:write can never be given to a key; people and roles are managed in the console only. The expiry date is optional.

  3. Copy the secret

    The key (tpk_…) is shown only once; the platform stores only its hash. The list shows only the prefix (tpk_<prefix>), scopes and when it was last used.

#Authentication

Send two headers with every request:

HeaderValue
AuthorizationBearer tpk_…
X-Tenant-IdThe tenant id (UUID). It is in the address of the tenant page in the admin console (/tenants/<id>). Not needed on endpoints that carry the tenant in the path (e.g. /tenants/{id}).
Shell
curl "https://api.2people.chat/api/v1/conversations?view=open&limit=25" \
  -H "Authorization: Bearer $TWOPEOPLE_API_KEY" \
  -H "X-Tenant-Id: $TWOPEOPLE_TENANT_ID"
  • Keys can only reach endpoints that require a permission; session and profile endpoints (such as /me) and platform endpoints are closed to keys.
  • Revoked or expired keys, and keys of a suspended tenant, are rejected.
  • The API is not meant to be called from browsers (CORS is open only to the platform’s own apps); use keys on your servers only.

#Errors

Errors use application/problem+json (RFC 9457). Every body carries a stable UPPER_CASE code and the request’s correlationId. Branch on code; title and detail texts may change.

HTTP
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
X-Correlation-Id: 3f2c9a7e-…

{
  "type": "https://docs.2people.chat/problems/validation-failed",
  "title": "Bad Request",
  "status": 400,
  "code": "VALIDATION_FAILED",
  "correlationId": "3f2c9a7e-…",
  "errors": [
    { "path": "limit", "message": "limit must not be greater than 100" }
  ]
}
HTTPcodeMeaning
400VALIDATION_FAILEDInvalid or unknown field; details in errors[].
400TENANT_CONTEXT_REQUIREDThe X-Tenant-Id header is missing.
401UNAUTHENTICATEDKey missing, invalid, expired or revoked.
403FORBIDDENThe key lacks the scope this endpoint needs, or the endpoint is not available to API keys.
404TENANT_NOT_FOUNDThe tenant does not exist or the key does not belong to it (both give the same answer).
404ROUTE_NOT_FOUNDNo such endpoint.
409CONFLICTConflict (e.g. the value already exists). Some endpoints return more specific codes.
429RATE_LIMITEDRate limit exceeded.
500INTERNALUnexpected error; report it with the correlationId.

You can send your own X-Correlation-Id (8–128 characters: letters, digits and . _ : -); the same value comes back in the response and in error bodies. Otherwise the platform generates one.

#Pagination

List endpoints use cursor pagination: limit (1–100, default 25) and the opaque cursor from the previous response. Responses look like { items, nextCursor }; when nextCursor is null you are on the last page. Do not decode or build cursors yourself. The API reference shows which endpoints take these parameters.

Node.js
const BASE = 'https://api.2people.chat/api/v1';
const headers = {
  Authorization: `Bearer ${process.env.TWOPEOPLE_API_KEY}`,
  'X-Tenant-Id': process.env.TWOPEOPLE_TENANT_ID,
};

// Walks every page of a cursor-paginated list.
async function* listAll(path, params = {}) {
  let cursor;
  do {
    const query = new URLSearchParams({ ...params, limit: '100', ...(cursor ? { cursor } : {}) });
    const res = await fetch(`${BASE}${path}?${query}`, { headers });
    if (!res.ok) {
      const problem = await res.json(); // application/problem+json
      throw new Error(`${problem.code}: ${problem.detail ?? problem.title} (${problem.correlationId})`);
    }
    const page = await res.json();
    yield* page.items;
    cursor = page.nextCursor;
  } while (cursor);
}

for await (const conversation of listAll('/conversations', { view: 'closed' })) {
  console.log(conversation.id, conversation.status);
}

#Rate limits

The default limit is 300 requests per minute per client IP address. Above it the API returns 429 with RATE_LIMITED; wait and retry with increasing delays.

#API reference

The method, path, required permission, parameters, request body and responses of every endpoint are in the API reference.