ZChat REST API

Developers · API reference

Connect Zapier, Make, your CRM or your own code to ZChat: read and answer conversations, sync contacts, push knowledge-base articles and subscribe to events. Authenticate with a workspace API key; every plan includes the API.

Before you start

  • Difficulty: Developer
  • Time: 10 minutes to a first call
  • You need: A ZChat workspace (the free plan includes the API)
  • You need: An admin or owner account to create a key
  • You need: A server to call from: keys must never be put in a browser, an app or a URL

Steps

  1. Create an API key

    In ZChat, open Settings → API keys and choose + Create key. Name it after the integration, pick its permissions (Read only, Read & write, or individual scopes) and optionally an expiry. The full key is shown once: copy it into your integration's secret settings. ZChat stores only a hash of it, so a lost key cannot be recovered - revoke it and create another.

  2. Make your first call

    Send the key as a Bearer token. GET /api/v1/me returns the workspace, the key's scopes and its rate limit. Keys in the query string are refused.

    curl https://api.zchat.com/api/v1/me \
      -H "Authorization: Bearer zck_live_YOUR_KEY"
  3. Read conversations

    Lists are newest first. Pass limit (1-100) and the nextCursor of the previous page as cursor. Filter by status (open, pending_human, resolved), channel, contactId, assignedAgentId, tagId or updatedSince. GET /api/v1/conversations/{id} adds the messages.

    curl "https://api.zchat.com/api/v1/conversations?status=pending_human&limit=50" \
      -H "Authorization: Bearer zck_live_YOUR_KEY"
  4. Reply as your integration

    The reply reaches the visitor on the conversation's channel and is shown as written by the API key. Like any agent reply it hands the conversation to your team, so the AI stops answering it. Send an Idempotency-Key header to make retries safe.

    curl -X POST https://api.zchat.com/api/v1/conversations/1234/messages \
      -H "Authorization: Bearer zck_live_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: 6f1c2b4e-reply-1" \
      -d '{"body": "Your order shipped today."}'
  5. Keep the knowledge base in sync

    PUT an article under your own id: the first call creates it, later calls update it in place, and the AI's index is rebuilt automatically. Curation done in the dashboard (enabled, category, status) survives updates.

    curl -X PUT https://api.zchat.com/api/v1/knowledge/articles/external/shipping-times \
      -H "Authorization: Bearer zck_live_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -d '{"title": "Shipping times", "body": "Orders ship within 2 business days.", "tags": "shipping"}'
  6. Subscribe to events (REST hooks)

    POST /api/v1/webhooks with a public https URL and the events you want; DELETE it to unsubscribe. This is the subscribe/unsubscribe pattern Zapier and Make use. ZChat generates a signing secret if you send none and returns it once.

    curl -X POST https://api.zchat.com/api/v1/webhooks \
      -H "Authorization: Bearer zck_live_YOUR_KEY" \
      -H "Content-Type: application/json" \
      -d '{"url": "https://example.com/zchat-events", "events": ["conversation.created", "conversation.resolved"]}'
  7. Verify each delivery

    Every delivery carries X-ZChat-Event, X-ZChat-Timestamp and X-ZChat-Signature: t=<unix seconds>,v1=<hex HMAC-SHA256 of "<t>.<raw body>" keyed with the secret>. Reject old timestamps and compare in constant time.

    import crypto from "node:crypto";
    
    function verify(rawBody, header, secret) {
      const { t, v1 } = Object.fromEntries(header.split(",").map((p) => p.split("=")));
      if (Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
      const expected = crypto.createHmac("sha256", secret).update(`${t}.${rawBody}`).digest("hex");
      return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(v1));
    }

Every endpoint

  • GET /api/v1/me: Any key. The workspace, the key's scopes and its rate limit.
  • GET /api/v1/openapi.json: No key needed. The OpenAPI 3.1 description of this API, for code generators and API clients.
  • GET /api/v1/conversations: conversations:read. List with filters and cursor paging.
  • GET /api/v1/conversations/{id}: conversations:read. One conversation with its latest 500 messages.
  • POST /api/v1/conversations/{id}/messages: conversations:write. Reply, attributed to the API key.
  • POST /api/v1/conversations/{id}/assign: conversations:write. Assign to an agent ({"agentId": "..."}).
  • POST /api/v1/conversations/{id}/close: conversations:write. Resolve, with an optional outcome and note.
  • POST /api/v1/conversations/{id}/reopen: conversations:write. Set back to open.
  • POST /api/v1/conversations/{id}/tags: conversations:write. Apply an existing tag by tagId or name.
  • DELETE /api/v1/conversations/{id}/tags/{tagId}: conversations:write. Remove a tag.
  • GET /api/v1/tags: conversations:read. The workspace's tags.
  • GET /api/v1/agents: conversations:read. Agents you can assign to.
  • GET /api/v1/contacts: contacts:read. List or search (q, email, externalId).
  • GET /api/v1/contacts/{id}: contacts:read. One contact with its tags.
  • POST /api/v1/contacts: contacts:write. Create (name, email, phone, externalId). Accepts Idempotency-Key.
  • PATCH /api/v1/contacts/{id}: contacts:write. Update only the fields you send.
  • DELETE /api/v1/contacts/{id}: contacts:write and contacts:erase. GDPR erasure: the person and every conversation, message and file about them. Permanent.
  • GET /api/v1/contacts/{id}/events: contacts:read. The contact's event timeline.
  • POST /api/v1/contacts/{id}/events: contacts:write. Record an event or note (name plus a JSON payload) agents see on the timeline.
  • GET /api/v1/knowledge/articles: knowledge:read. List articles (without bodies).
  • GET /api/v1/knowledge/articles/{id}: knowledge:read. One article with its body.
  • PUT /api/v1/knowledge/articles/external/{externalId}: knowledge:write. Create or update by your own id.
  • DELETE /api/v1/knowledge/articles/{id}: knowledge:write. Delete an article.
  • POST /api/v1/knowledge/reindex: knowledge:write. Rebuild the AI's search index.
  • GET /api/v1/webhooks: webhooks:read. Every webhook of the workspace (secrets never shown).
  • POST /api/v1/webhooks: webhooks:write. Subscribe a URL to events. Accepts Idempotency-Key.
  • DELETE /api/v1/webhooks/{id}: webhooks:write. Unsubscribe.
  • GET /api/v1/webhooks/events: Any key. The event types: conversation.created, .visitor_replied, .agent_replied, .handoff_requested, .resolved, .sla_breached.

Good to know

  • Base URL: https://api.zchat.com. HTTPS only. JSON request and response bodies, camelCase names, ISO-8601 UTC timestamps.
  • Scopes: conversations, contacts, knowledge and webhooks each have :read and :write; :write includes :read. contacts:erase is never included in a preset and needs contacts:write too.
  • Errors are RFC 7807 problem details (application/problem+json) with a stable code: missing_api_key, invalid_api_key, api_key_revoked, api_key_expired, insufficient_scope, not_found, validation_failed, rate_limited and others.
  • Rate limit per key: 600 requests a minute on paid plans, 60 on the free plan. A 429 says how long to wait in Retry-After. Repeated invalid keys from one address are blocked for a few minutes.
  • Each workspace's keys only ever see that workspace. Revoking a key in the dashboard stops it immediately; creating and revoking are recorded in the audit log.
  • The API is for servers: it sends no CORS headers, so browsers cannot call it from another site.

Machine-readable spec: https://api.zchat.com/api/v1/openapi.json - import it into Postman, Insomnia or a code generator.