ChatAman Docsby CodeEra
العربية Open app

Documentation

External Integrations API v1

Build channel-aware API and signed webhook integrations with the complete v1 contract, examples, event catalogue, security, retries, errors, and changelog.

CodeEra developer API tools inside Chataman CodeEra developer API tools inside Chataman
CodeEra developer API tools inside Chataman

Status: stable, additive contract Released: 2026-08-14 Base URL: https://dev.chataman.com for development and https://chataman.com for production

This guide is the canonical contract for server-to-server API keys, channel-aware message sending, organization data reads, webhook endpoints, signing, retries, and delivery observability. Existing /api/* clients and legacy webhook records remain supported.

Quickstart

  1. In Developer Tools → Access Tokens, create a key, select only the required scopes, optionally select a default channel, and save the full key immediately. Chat Aman stores only its SHA-256 hash and will not show the full value again.
  2. Discover channel IDs and slugs:
curl https://dev.chataman.com/api/v1/integrations/channels \
  -H "Authorization: Bearer $CHATAMAN_API_KEY" \
  -H "Accept: application/json"
  1. Send through a specific channel. Every v1 write requires a new Idempotency-Key for the logical operation:
curl -X POST https://dev.chataman.com/api/v1/integrations/messages \
  -H "Authorization: Bearer $CHATAMAN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Idempotency-Key: order-7842-confirmation-v1" \
  -d '{
    "channel_slug": "support-telegram",
    "contact_uuid": "CONTACT_UUID",
    "message": "Your order is ready."
  }'
  1. Register a signed webhook endpoint and store the returned signing_secret. The secret is returned only on creation or rotation.
curl -X POST https://dev.chataman.com/api/v1/integrations/webhook-endpoints \
  -H "Authorization: Bearer $CHATAMAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Order sync",
    "url": "https://hooks.example.com/chataman",
    "events": ["message.*", "contact.updated", "security.*"]
  }'

Authentication and scopes

Send Authorization: Bearer <key> on every request. New keys are high-entropy, hashed at rest, organization-bound, revocable, and may have an expiry and default channel. Legacy plaintext keys remain valid until revoked or rotated; keys created before scopes existed are treated as wildcard keys for backward compatibility.

Available scopes:

  • channels:read
  • contacts:read, contacts:write
  • groups:read, groups:write
  • messages:read, messages:write
  • campaigns:write
  • templates:read
  • canned_replies:read, canned_replies:write
  • users:read
  • permissions:read
  • events:read
  • webhooks:read, webhooks:write
  • flow:read, flow:write
  • * for a deliberately unrestricted key

Use one key per integration and environment. Keep keys in a secret manager, never in frontend code, rotate them after exposure or ownership changes, and choose the smallest useful scope set.

Channel selection

V1 send requests accept exactly one of:

  • channel_id: numeric ID returned by the channels endpoint.
  • channel_slug: stable API-facing slug. Chat Aman matches an explicit configured slug, channel UUID, provider identifier, or normalized channel name.
  • neither: uses the API key’s configured default channel, then the organization’s default channel.

Supplying both returns 422. A channel from another organization, an inactive channel, a missing default, or a channel that cannot send external messages returns 422. Snapchat currently exposes lead-ingestion data but is returned with can_send_messages: false; the other current channel service types are sendable.

Successful send responses include the actual channel object so callers can audit where a message originated. Webhook envelopes use the same channel identity shape.

Endpoint catalogue

All endpoints are organization-scoped and use Bearer authentication.

Method Path Scope Purpose
GET /api/v1/integrations/channels channels:read List channel IDs, UUIDs, slugs, type, status, send capability, and media support.
GET /api/v1/integrations/messages messages:read Paginated channel-aware message history.
POST /api/v1/integrations/messages messages:write Send text or interactive content; requires idempotency key.
POST /api/v1/integrations/messages/media messages:write Send media; requires idempotency key.
POST /api/v1/integrations/messages/template messages:write Send a provider template; requires idempotency key.
GET /api/v1/integrations/users users:read List organization memberships, roles, and effective permissions.
GET /api/v1/integrations/permissions permissions:read List the permission and API-scope catalogues.
GET /api/v1/integrations/events events:read Read organization audit/admin/security events.
GET /api/v1/integrations/webhook-events webhooks:read Read the authoritative event catalogue for this deployment.
GET/POST /api/v1/integrations/webhook-endpoints webhooks:read / webhooks:write List or create endpoints.
GET/PUT/DELETE /api/v1/integrations/webhook-endpoints/{uuid} webhooks:read / webhooks:write Read, update, or remove one endpoint.
POST /api/v1/integrations/webhook-endpoints/{uuid}/rotate-secret webhooks:write Rotate and return a new secret once.
POST /api/v1/integrations/webhook-endpoints/{uuid}/test webhooks:write Deliver an isolated integration.test event.
GET /api/v1/integrations/webhook-deliveries webhooks:read Inspect attempts, state, response, error, and retry time.

Legacy contact, group, canned reply, template, campaign, flow, and /api/send* routes remain available. Scoped keys must now hold the matching scope; pre-scope keys retain wildcard behavior.

Text request

{
  "channel_id": 42,
  "contact_uuid": "95cc16a4-cf69-41f8-b2c4-923de17d3fc6",
  "message": "Hello from the billing system",
  "type": "text"
}

contact_uuid is preferred. phone remains accepted for backward compatibility and is resolved only inside the authenticated organization.

Media request

{
  "channel_slug": "sales-whatsapp",
  "contact_uuid": "95cc16a4-cf69-41f8-b2c4-923de17d3fc6",
  "media_type": "document",
  "media_url": "https://cdn.example.com/invoices/7842.pdf",
  "file_name": "invoice-7842.pdf",
  "caption": "Invoice 7842"
}

Template request

{
  "channel_slug": "sales-whatsapp",
  "contact_uuid": "95cc16a4-cf69-41f8-b2c4-923de17d3fc6",
  "template": {
    "name": "order_ready",
    "language": "en",
    "components": []
  }
}

Idempotency

POST /api/v1/integrations/messages* requires Idempotency-Key (1–120 characters). The key is isolated to the API credential. Repeating the same key and request returns the stored status and JSON body with Idempotency-Replayed: true. Reusing it with a different method, path, or body returns 409 idempotency_conflict; a still-running duplicate returns 409 idempotency_in_progress. Records are retained for seven days by default.

Webhook envelope

Modern and legacy consumers retain the original top-level event and data fields. V1 adds consistent metadata and channel identity:

{
  "event": "message.sent",
  "data": {
    "message_id": 9182,
    "contact_uuid": "95cc16a4-cf69-41f8-b2c4-923de17d3fc6",
    "status": "sent"
  },
  "metadata": {
    "event_id": "6e1eb478-2888-47aa-8dce-e6f830ead865",
    "event_type": "message.sent",
    "api_version": "v1",
    "occurred_at": "2026-08-14T10:45:32+00:00",
    "organization_id": 17
  },
  "channel": {
    "id": 42,
    "uuid": "dcb67374-4d88-4930-b834-86c64359745d",
    "slug": "sales-whatsapp",
    "type": "whatsapp",
    "name": "Sales WhatsApp",
    "identifier": "PROVIDER_CHANNEL_IDENTIFIER"
  }
}

channel is null only when an event has no channel context. Use metadata.event_id as the consumer deduplication key. Event-specific data is additive: consumers must ignore unknown fields.

Full webhook event catalogue

The v1 catalogue contains 146 events. GET /api/v1/integrations/webhook-events is authoritative and includes events enabled by the deployed application. Endpoint subscriptions accept exact names, *, or namespace wildcards such as message.*.

  • account: account.email_verification_requested, account.email_verified, account.password_changed, account.password_reset_requested, account.registered
  • ai: ai.agent_failed, ai.cost_threshold_reached, ai.knowledge_sync_completed, ai.knowledge_sync_failed, ai.quota_exceeded, ai.quota_low
  • audit: audit.recorded
  • automation: automation.failed
  • autoreply: autoreply.created, autoreply.deleted, autoreply.updated
  • billing: billing.auto_recharge_failed, billing.credit_critical, billing.credit_depleted, billing.credit_low, billing.credit_recharged, billing.invoice_approved, billing.invoice_due, billing.invoice_generated, billing.invoice_overdue, billing.invoice_paid, billing.invoice_sent, billing.payment_failed, billing.payment_refunded, billing.payment_succeeded
  • broadcast: broadcast.completed, broadcast.failed
  • bulk_operation: bulk_operation.completed, bulk_operation.failed
  • campaign: campaign.completed, campaign.contact_replied, campaign.failed, campaign.paused, campaign.resumed, campaign.scheduled, campaign.started
  • channel: channel.connected, channel.disconnected, channel.health_degraded, channel.health_restored, channel.token_expiring
  • commerce: commerce.cart_abandoned, commerce.catalog_sync_failed, commerce.order_canceled, commerce.order_created, commerce.order_fulfilled, commerce.order_paid, commerce.order_refunded, commerce.order_status_changed, commerce.payment_link_created, commerce.store_disconnected
  • contact: contact.created, contact.deleted, contact.updated
  • conversation: conversation.agent_mentioned, conversation.assigned, conversation.closed, conversation.created, conversation.customer_replied, conversation.reassigned, conversation.reopened, conversation.sla_breached, conversation.sla_warning, conversation.unassigned
  • crm: crm.deal_assigned, crm.deal_closing_soon, crm.deal_created, crm.deal_lost, crm.deal_rotting, crm.deal_stage_changed, crm.deal_won, crm.lead_assigned, crm.lead_converted, crm.lead_created, crm.lead_stale, crm.meeting_booked, crm.meeting_canceled, crm.meeting_reminder, crm.meeting_rescheduled, crm.quote_accepted, crm.quote_expired, crm.quote_rejected, crm.quote_sent, crm.quote_viewed, crm.sequence_completed, crm.sequence_failed, crm.task_assigned, crm.task_completed, crm.task_due, crm.task_overdue
  • drip_campaign: drip_campaign.completed, drip_campaign.failed
  • feedback: feedback.csat_low, feedback.csat_received
  • group: group.created, group.deleted, group.updated
  • integration: integration.sync_failed, integration.sync_restored, integration.test
  • message: message.received, message.sent, message.status.update
  • organization: organization.activated, organization.suspended
  • scheduled_message: scheduled_message.canceled, scheduled_message.failed, scheduled_message.sent
  • security: security.api_key_created, security.api_key_revoked, security.new_login, security.two_factor_changed
  • storage: storage.migration_completed, storage.migration_failed, storage.threshold_reached
  • subscription: subscription.canceled, subscription.downgraded, subscription.expired, subscription.expiring, subscription.renewed, subscription.started, subscription.upgraded
  • support: support.ticket_assigned, support.ticket_created, support.ticket_reopened, support.ticket_replied, support.ticket_resolved
  • survey: survey.completed
  • team: team.invitation_accepted, team.invitation_created, team.invitation_expired, team.member_added, team.member_removed, team.role_changed
  • trial: trial.expired, trial.expiring, trial.started
  • usage: usage.quota_exceeded, usage.threshold_reached
  • webhook: webhook.delivery_failed, webhook.endpoint_disabled

Signing and verification

Modern endpoints receive:

  • X-ChatAman-Event: event name
  • X-ChatAman-Delivery: durable delivery UUID
  • X-ChatAman-Timestamp: Unix timestamp used in the signature
  • X-ChatAman-Attempt: one-based attempt number
  • X-ChatAman-Signature: v1=<hex HMAC-SHA256>

The signed bytes are timestamp + "." + raw_request_body. Verify the raw bytes before JSON parsing, reject stale timestamps (five minutes is recommended), and use a constant-time comparison.

import crypto from 'node:crypto';

export function verifyChatAman(rawBody, headers, secret) {
  const timestamp = headers['x-chataman-timestamp'];
  const received = headers['x-chataman-signature'];
  if (!timestamp || !received || Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = `v1=${crypto.createHmac('sha256', secret).update(`${timestamp}.${rawBody}`).digest('hex')}`;
  return received.length === expected.length && crypto.timingSafeEqual(Buffer.from(received), Buffer.from(expected));
}

Custom endpoint headers cannot override authorization, transport, content, cookie, host, or Chat Aman signature headers. Endpoint URLs must use public HTTPS on port 443; credentials, localhost/private/reserved IPs, internal domains, and DNS rebinding to non-public addresses are rejected. Secrets are encrypted at rest. Older module endpoints keep their unsigned payload contract until migrated.

Delivery, retries, and observability

Delivery is queued and persisted before the network call. A success is any 2xx response. Connection failures, 408, 425, 429, and 5xx are retried with backoff of 30 seconds, 5 minutes, and 30 minutes, up to four attempts by default. Other 4xx responses are permanent rejections. Repeated endpoint failures increment health state and may automatically disable the endpoint after ten failures; a later success resets the failure count.

Use GET /api/v1/integrations/webhook-deliveries?status=retrying&event=message.sent to inspect delivery_id, event ID, endpoint, attempt count, response status, error code/message, next attempt, and delivered timestamp. Delivery records are retained for 90 days by default. Alert on failed, retries_exhausted, and endpoint-disabled events. Do not log payloads, API keys, or signing secrets.

Errors and rate limits

Error responses use an HTTP status plus a stable error.code where integration middleware handles the failure.

Status Meaning Action
400 Malformed JSON or request syntax Correct the request; do not retry unchanged.
401 Missing, invalid, expired, or revoked key Add, rotate, or replace the key.
403 insufficient_scope or organization access denied Grant only the required scope or correct the tenant.
404 Resource not found in the authenticated organization Verify organization-bound IDs/UUIDs.
409 Idempotency conflict or operation still processing Reuse only for the same logical request; retry in-progress later.
422 Validation, unsafe URL, invalid channel, or provider rule failure Correct the fields; do not retry unchanged.
429 Rate limit exceeded Respect Retry-After and use jittered exponential backoff.
5xx Temporary server/provider failure Retry idempotently with capped exponential backoff.

Default limits are 120 authenticated integration reads per minute, 30 sends per minute, and 10 endpoint tests per minute. Deployments may override these limits; clients must honor response headers rather than hard-code them.

Versioning and changelog

The contract uses a path version (/api/v1/integrations) and an envelope version (metadata.api_version). Additive fields and new events do not change the major version. Removing/renaming fields, changing signatures, or changing field meaning requires a new major path or a documented compatibility window. Consumers must ignore unknown fields and events.

2026-08-14 — v1

  • Added explicit channel selection and per-key default-channel fallback.
  • Added hashed API keys, granular scopes, expiry/revocation metadata, rate limits, and legacy-key compatibility.
  • Added idempotent message writes.
  • Added channel/message/user/permission/audit discovery endpoints.
  • Unified modern and legacy webhook dispatch without duplicating same-URL deliveries.
  • Added stable event metadata, originating channel identity, encrypted signing secrets, queued retries, delivery ledger, endpoint health, and SSRF protection.
  • Expanded the catalogue to all current message, contact, group, campaign, channel, account, organization, team, security, audit, billing, CRM, commerce, AI, support, storage, and operational events.

Legacy routes are not removed. Migrate sends to /api/v1/integrations/messages*, create scoped keys, move legacy webhook records to signed endpoints, and verify signatures before depending on the v1 security guarantees.