Channels API

Configure outbound and inbound messaging, manage send/receive addresses and explicit inbound routes, queue messages, and audit activity. Each channel config is rooted in a zero-trust channel zone, so access is governed by per-channel and per-address grants. Conversations consumes route snapshots to create or continue one participant thread, request exactly one Workflows item for a process route, and invoke the effective auto-reply agent independently. Address-gate changes publish `channels.inbound_route.address_gate.updated`.

Base URL/api/v1/channels

Automation triggers: view every Channels event, payload field, and predicate.

Configs

Create and manage email setups per company. Platform Domain uses an Ergon-owned domain immediately; Custom Domain verifies a customer-owned domain. Both create virtual/routed addresses, not mailboxes. Connected Mailbox is Coming soon. Each config roots a zero-trust channel zone at org/{company_id}/channel/{config_id}.

POST/api/v1/channels/configs

Create Config

Register a Platform Domain or Custom Domain configuration. Connected Mailbox is reserved but not yet available. The API uses integration_type with no aliases for historical provider values.

Bearer token required. x-company-id header required.

Request Body

NameTypeDescription
name*
stringDisplay name (1–200 characters)
integration_type*
"platform-domain" | "custom-domain" | "connected-mailbox"Email setup model. Connected mailbox is Coming soon and rejected.
domain
stringCustomer-owned domain for custom-domain (3–253 characters). Omit for platform-domain.
channel_type
stringChannel implementation slugDefault: email

Response Fields

NameTypeDescription
id*
stringConfig ID
company_id*
stringOwning company ID
channel_type*
stringChannel type slug
integration_type*
"platform-domain" | "custom-domain" | "connected-mailbox" | "business-account"Provider-neutral setup model for email and WhatsApp Business accounts.
name*
stringDisplay name
domain*
string | nullConfigured domain
status*
stringLifecycle status (e.g. pending_dns, verifying, verified)
setup_details*
object | nullSafe, channel-specific setup metadata. Email details include kind=email plus DNS records or the Platform Domain mail namespace; WhatsApp details expose operational identifiers but never secrets.
inbound_enabled*
booleanWhether inbound handling is enabled for this config
created_at*
stringCreation time (ISO 8601)
updated_at*
stringLast update time (ISO 8601)
capabilities
objectWhat the calling principal may do with this config
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs \
  -H "Authorization: Bearer {token}" \
  -H "x-company-id: {company_id}" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Primary email",
    "integration_type": "custom-domain",
    "domain": "mail.example.com",
    "channel_type": "email"
  }'

Response

201 Created
{
  "id": "8f2c1a00-1111-4222-8333-444455556666",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_type": "email",
  "integration_type": "custom-domain",
  "name": "Primary email",
  "domain": "mail.example.com",
  "status": "pending_dns",
  "setup_details": {
    "kind": "email",
    "dns_records": [
      {
        "type": "TXT",
        "name": "resend._domainkey.mail.example.com",
        "value": "p=MIGfMA0GCS..."
      }
    ]
  },
  "inbound_enabled": false,
  "created_at": "2026-04-01T12:00:00Z",
  "updated_at": "2026-04-01T12:00:00Z",
  "capabilities": {
    "can_list_addresses": true,
    "can_create_address": true,
    "can_delete_address": true,
    "can_send": true,
    "manage_addresses": true,
    "manage_settings": true,
    "manage_security": true,
    "view_activity": true
  }
}
GET/api/v1/channels/configs

List Configs

Return all channel configs visible to the caller for their company.

Bearer token required. x-company-id header required.

Response Fields

NameTypeDescription
[]*
ConfigResponse[]Configs ordered by created_at descending
curl https://platform.ergondata.ai/api/v1/channels/configs \
  -H "Authorization: Bearer {token}" \
  -H "x-company-id: {company_id}"

Response

200 OK
[
  {
    "id": "8f2c1a00-1111-4222-8333-444455556666",
    "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
    "channel_type": "email",
    "integration_type": "custom-domain",
    "name": "Primary email",
    "domain": "mail.example.com",
    "status": "verified",
    "setup_details": { "kind": "email", "dns_records": [] },
    "inbound_enabled": true,
    "created_at": "2026-04-01T12:00:00Z",
    "updated_at": "2026-04-02T09:15:00Z",
    "capabilities": { "can_send": true, "manage_settings": true }
  }
]
GET/api/v1/channels/configs/{config_id}

Get Config

Fetch one config by ID. Custom Domain may refresh DNS records and verification status; Platform Domain is already verified.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id} \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "id": "8f2c1a00-1111-4222-8333-444455556666",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_type": "email",
  "integration_type": "custom-domain",
  "name": "Primary email",
  "domain": "mail.example.com",
  "status": "verified",
  "setup_details": {
    "kind": "email",
    "dns_records": []
  },
  "inbound_enabled": true,
  "created_at": "2026-04-01T12:00:00Z",
  "updated_at": "2026-04-02T09:15:00Z",
  "capabilities": { "can_send": true, "manage_settings": true }
}
PATCH/api/v1/channels/configs/{config_id}

Update Config

Update the display name for a config.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Request Body

NameTypeDescription
name
string | nullNew display name
curl -X PATCH https://platform.ergondata.ai/api/v1/channels/configs/{config_id} \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"name": "Primary email (updated)"}'

Response

200 OK
{
  "id": "8f2c1a00-1111-4222-8333-444455556666",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_type": "email",
  "integration_type": "custom-domain",
  "name": "Primary email (updated)",
  "domain": "mail.example.com",
  "status": "verified",
  "setup_details": { "kind": "email", "dns_records": [] },
  "inbound_enabled": true,
  "created_at": "2026-04-01T12:00:00Z",
  "updated_at": "2026-04-03T16:00:00Z"
}
POST/api/v1/channels/configs/{config_id}/verify

Verify Config

Verify DNS for a Custom Domain and move it toward verified. Platform Domain requires no DNS verification.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/verify \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "id": "8f2c1a00-1111-4222-8333-444455556666",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_type": "email",
  "integration_type": "custom-domain",
  "name": "Primary email",
  "domain": "mail.example.com",
  "status": "verifying",
  "setup_details": { "kind": "email", "dns_records": [] },
  "inbound_enabled": true,
  "created_at": "2026-04-01T12:00:00Z",
  "updated_at": "2026-04-03T14:22:00Z"
}
GET/api/v1/channels/configs/{config_id}/whatsapp/phone-numbers

Discover WhatsApp Phone Numbers

List phone numbers directly from the config's Meta WABA, including verification and quality state and whether each number is already connected.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Ready WhatsApp business-account config ID

Response Fields

NameTypeDescription
items*
WhatsAppPhoneNumberCandidate[]Meta-derived phone IDs, normalized numbers, verification/quality state, and local connected state
total*
integerNumber of candidates returned by Meta
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/whatsapp/phone-numbers \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "items": [
    {
      "phone_number_id": "123456789012345",
      "business_phone_number": "+5511914409811",
      "display_name": "Brazil support",
      "verification_status": "VERIFIED",
      "quality_rating": "GREEN",
      "platform_type": "CLOUD_API",
      "operational_status": "CONNECTED",
      "connected": false,
      "connected_address_id": null
    }
  ],
  "total": 1
}
POST/api/v1/channels/configs/{config_id}/whatsapp/phone-numbers

Add WhatsApp Phone Number

Add one verified number discovered under the existing WABA. Channels re-fetches the number from Meta and derives every address field; the generic address endpoint cannot create WhatsApp numbers.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Ready WhatsApp business-account config ID

Request Body

NameTypeDescription
phone_number_id*
stringMeta phone-number ID returned by the discovery endpoint
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/whatsapp/phone-numbers \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"phone_number_id":"123456789012345"}'

Response

201 Created
{
  "id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "channel_config_id": "8f2c1a00-1111-4222-8333-444455556666",
  "address": "+5511914409811",
  "display_name": "Brazil support",
  "direction": "both",
  "status": "active",
  "channel_type": "whatsapp",
  "provider_phone_number_id": "123456789012345",
  "operational_status": "connected",
  "quality_rating": "GREEN"
}
POST/api/v1/channels/configs/{config_id}/whatsapp/credentials/connect

Connect WhatsApp Credentials

Re-run WhatsApp provisioning for the config: connect the Vault credential consumer for the channel principal, validate the Meta credentials, and refresh the config status. Use after granting or rotating the Vault credentials referenced by the config.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)WhatsApp business-account config ID
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/whatsapp/credentials/connect \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "id": "8f2c1a00-1111-4222-8333-444455556666",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_type": "whatsapp",
  "integration_type": "whatsapp-business-account",
  "name": "WhatsApp support",
  "status": "verified",
  "setup_details": { "kind": "whatsapp", "provisioning_status": "ready" },
  "inbound_enabled": true,
  "created_at": "2026-04-01T12:00:00Z",
  "updated_at": "2026-04-03T14:22:00Z"
}
DELETE/api/v1/channels/configs/{config_id}

Delete Config

Remove the config and integration-owned domain state. The account-level Resend webhook is deployment configuration and is never deleted by config lifecycle.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
curl -X DELETE https://platform.ergondata.ai/api/v1/channels/configs/{config_id} \
  -H "Authorization: Bearer {token}"

Response

204 No Content
GET/api/v1/channels/companies/{company_id}/channel-prefs

Get Channel Preferences

Get the current member's channel-tree presentation preferences. This is user-session UI state, not an agent tool or IAM resource.

Bearer user token required; token company must match company_id.

Path Parameters

NameTypeDescription
company_id*
string (UUID)Company ID from the current user session

Response Fields

NameTypeDescription
favorite_config_id
string (UUID) | nullFavorite channel config, if set
config_order*
string (UUID)[]Custom channel config ordering
curl https://platform.ergondata.ai/api/v1/channels/companies/{company_id}/channel-prefs \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "favorite_config_id": "8f2c1a00-1111-4222-8333-444455556666",
  "config_order": ["8f2c1a00-1111-4222-8333-444455556666"]
}
PUT/api/v1/channels/companies/{company_id}/channel-prefs

Update Channel Preferences

Partially update the current member's favorite channel and custom channel-tree order.

Bearer user token required; token company must match company_id.

Path Parameters

NameTypeDescription
company_id*
string (UUID)Company ID from the current user session

Request Body

NameTypeDescription
favorite_config_id
string (UUID) | nullFavorite channel config; null clears it
config_order
string (UUID)[] | nullReplacement custom ordering
curl -X PUT https://platform.ergondata.ai/api/v1/channels/companies/{company_id}/channel-prefs \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"favorite_config_id":"8f2c1a00-1111-4222-8333-444455556666"}'

Response

200 OK
{
  "favorite_config_id": "8f2c1a00-1111-4222-8333-444455556666",
  "config_order": []
}

Addresses

Manage send/receive addresses under a config, and list every address visible to the current company.

GET/api/v1/channels/addresses

List Company Addresses

List channel addresses for the caller's company. Optionally filter by channel type, direction, or only sendable verified addresses.

Bearer token required. x-company-id header required.

Query Parameters

NameTypeDescription
channel_type
stringFilter by config channel type (e.g. email)
direction
stringFilter by direction: send, receive, or both
sendable
booleanWhen true, only active send/both addresses on verified configsDefault: false
authorized_to_send
booleanWhen true, filter rows by the caller's channels:addresses:send permission instead of channels:addresses:viewDefault: false

Response Fields

NameTypeDescription
[]*
AddressResponse[]id, channel_config_id, address, display_name, direction, status, channel_type, channel_name, timestamps
curl "https://platform.ergondata.ai/api/v1/channels/addresses?sendable=true&channel_type=email" \
  -H "Authorization: Bearer {token}" \
  -H "x-company-id: {company_id}"

Response

200 OK
[
  {
    "id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
    "channel_config_id": "8f2c1a00-1111-4222-8333-444455556666",
    "address": "hello@mail.example.com",
    "display_name": "Support",
    "direction": "both",
    "status": "active",
    "channel_type": "email",
    "channel_name": "Primary email",
    "created_at": "2026-04-02T10:00:00Z",
    "updated_at": "2026-04-02T10:00:00Z"
  }
]
POST/api/v1/channels/configs/{config_id}/addresses

Create Address

Add an email address under a config. The address must use the config domain. Receive/both addresses require inbound to be available and may enable inbound on the config. WhatsApp numbers must use the Meta-backed discovery and add endpoints.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Request Body

NameTypeDescription
address*
stringFull address, e.g. support@mail.example.com (3–320 characters)
display_name
string | nullOptional display name for From headers
direction
stringsend, receive, or bothDefault: send
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "address": "support@mail.example.com",
    "display_name": "Support",
    "direction": "send"
  }'

Response

201 Created
{
  "id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "channel_config_id": "8f2c1a00-1111-4222-8333-444455556666",
  "address": "support@mail.example.com",
  "display_name": "Support",
  "direction": "send",
  "status": "active",
  "channel_type": null,
  "channel_name": null,
  "created_at": "2026-04-02T10:00:00Z",
  "updated_at": "2026-04-02T10:00:00Z"
}
GET/api/v1/channels/configs/{config_id}/addresses

List Addresses

List all addresses belonging to a single channel config.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses \
  -H "Authorization: Bearer {token}"

Response

200 OK
[
  {
    "id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
    "channel_config_id": "8f2c1a00-1111-4222-8333-444455556666",
    "address": "support@mail.example.com",
    "display_name": "Support",
    "direction": "send",
    "status": "active",
    "channel_type": "email",
    "channel_name": "Primary email",
    "created_at": "2026-04-02T10:00:00Z",
    "updated_at": "2026-04-02T10:00:00Z"
  }
]
GET/api/v1/channels/addresses/whatsapp/outbound

List WhatsApp Outbound Addresses

List WhatsApp phone numbers the caller can send from: active send/both addresses on verified WhatsApp configs, filtered by the caller's send permission. Prefer this over the generic address list when starting or continuing WhatsApp outreach.

Bearer token required. Requires the X-Company-Id header.

Query Parameters

NameTypeDescription
limit
integerPage size (default 100, max 200)
offset
integerPagination offset (default 0)

Response Fields

NameTypeDescription
items*
AddressResponse[]WhatsApp addresses the caller may send from
total*
integerTotal matching addresses
curl "https://platform.ergondata.ai/api/v1/channels/addresses/whatsapp/outbound?limit=50" \
  -H "Authorization: Bearer {token}" \
  -H "X-Company-Id: {company_id}"

Response

200 OK
{
  "items": [
    {
      "id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
      "channel_config_id": "8f2c1a00-1111-4222-8333-444455556666",
      "address": "+5511914409811",
      "display_name": "Brazil support",
      "direction": "both",
      "status": "active",
      "channel_type": "whatsapp"
    }
  ],
  "total": 1
}
GET/api/v1/channels/addresses/{address_id}/whatsapp/send-options

Get WhatsApp Send Options

Return what may be sent from this WhatsApp address to one recipient right now: whether the 24-hour customer-service window is open (free-form allowed), when the session expires, and the approved utility templates enabled for the address.

Bearer token required.

Path Parameters

NameTypeDescription
address_id*
string (UUID)WhatsApp address ID

Query Parameters

NameTypeDescription
to*
stringRecipient phone number in E.164 format
limit
integerTemplate page size (default 100, max 200)
offset
integerTemplate pagination offset (default 0)

Response Fields

NameTypeDescription
free_form_allowed*
booleanTrue when the 24-hour session window with this recipient is open
session_expires_at*
string (ISO 8601) | nullWhen the open session window closes, if one exists
items*
WhatsAppTemplateResponse[]Approved, active utility templates enabled for this address
total*
integerTotal templates available to this address
curl "https://platform.ergondata.ai/api/v1/channels/addresses/{address_id}/whatsapp/send-options?to=%2B5511999999999" \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "free_form_allowed": false,
  "session_expires_at": null,
  "items": [
    {
      "id": "77e3a9b2-1234-4cde-9f00-aabbccddeeff",
      "name": "order_update",
      "language": "pt_BR",
      "category": "UTILITY",
      "status": "APPROVED",
      "components": [],
      "is_active": true,
      "allowed_address_ids": ["a1d2e3f4-5566-7788-99aa-bbccddeeff00"],
      "last_synced_at": "2026-04-03T14:22:00Z"
    }
  ],
  "total": 1
}
PATCH/api/v1/channels/addresses/{address_id}/whatsapp/contacts/{wa_id}/marketing-policy

Update WhatsApp Marketing Policy

Record a marketing opt-out (or opt-in) for one WhatsApp contact on this address. Opted-out contacts are excluded from marketing template sends while transactional traffic continues.

Bearer token required.

Path Parameters

NameTypeDescription
address_id*
string (UUID)WhatsApp address ID
wa_id*
stringWhatsApp contact ID (phone-derived)

Request Body

NameTypeDescription
opted_out*
booleanTrue to opt the contact out of marketing sends

Response Fields

NameTypeDescription
wa_id*
stringWhatsApp contact ID
opted_out*
booleanCurrent marketing opt-out state
updated_at*
string (ISO 8601)When the policy was last changed
curl -X PATCH https://platform.ergondata.ai/api/v1/channels/addresses/{address_id}/whatsapp/contacts/{wa_id}/marketing-policy \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"opted_out":true}'

Response

200 OK
{
  "wa_id": "5511999999999",
  "opted_out": true,
  "updated_at": "2026-04-03T14:22:00Z"
}
PATCH/api/v1/channels/configs/{config_id}/addresses/{address_id}

Update Address

Update the display name, direction, or status slug of an address.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
address_id*
string (UUID)Address ID

Request Body

NameTypeDescription
display_name
string | nullNew display name
direction
string | nullsend, receive, or both
status
string | nullAddress status slug (e.g. active, disabled)
curl -X PATCH https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id} \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"direction": "both"}'

Response

200 OK
{
  "id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "channel_config_id": "8f2c1a00-1111-4222-8333-444455556666",
  "address": "support@mail.example.com",
  "display_name": "Support",
  "direction": "both",
  "status": "active",
  "channel_type": null,
  "channel_name": null,
  "created_at": "2026-04-02T10:00:00Z",
  "updated_at": "2026-04-03T11:30:00Z"
}
DELETE/api/v1/channels/configs/{config_id}/addresses/{address_id}

Delete Address

Remove an address from the config and reconcile inbound settings if needed.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
address_id*
string (UUID)Address ID
curl -X DELETE https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id} \
  -H "Authorization: Bearer {token}"

Response

204 No Content

Send

Queue an outbound message from a verified, active address. Enforces the channels:addresses:send ACL on the address and any matching service access grants.

POST/api/v1/channels/send

Send Message

Queue a message for asynchronous delivery. For cross-service sends, supply service_name and resource_id so a matching address grant is checked. Pass an optional Idempotency-Key header to make retries safe; a replay returns the original result with idempotent_replay set to true.

Bearer token required.

Request Body

NameTypeDescription
channel
stringChannel to use; currently only emailDefault: email
address_id*
string (UUID)Sending address ID
config*
objectChannel-specific payload. For email: to, subject, html, plus optional cc, bcc, reply_to, in_reply_to, and attachments
service_name
string | nullCalling service slug for grant checks (e.g. workflows)
resource_id
string | nullExternal resource ID for grant checks

Response Fields

NameTypeDescription
status*
stringTypically queued
channel*
stringChannel used
log_id
string | nullActivity log row ID for tracking the send
provider_id
string | nullProvider slug handling delivery
provider_message_id
string | nullOutbound SMTP Message-ID set on the email, used for reply correlation
thread_id
string | nullChannel thread ID for the outbound message
idempotent_replay
booleanTrue when the request matched a prior Idempotency-Key and was not re-dispatchedDefault: false
curl -X POST https://platform.ergondata.ai/api/v1/channels/send \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: {idempotency_key}" \
  -d '{
    "channel": "email",
    "address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
    "config": {
      "to": ["customer@example.com"],
      "subject": "Your order has shipped",
      "html": "<p>Thanks for your purchase!</p>",
      "reply_to": "support@mail.example.com",
      "cc": [],
      "bcc": []
    },
    "service_name": "workflows",
    "resource_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479"
  }'

Response

202 Accepted
{
  "status": "queued",
  "channel": "email",
  "log_id": "b2c3d4e5-6677-8899-aabb-ccddeeff0011",
  "provider_id": "resend",
  "provider_message_id": "<msg_01hqabc@mail.example.com>",
  "thread_id": "d4e5f6a7-b8c9-4012-d345-6789abcdef01",
  "idempotent_replay": false
}
POST/api/v1/channels/send/whatsapp

Send WhatsApp Message

Queue one outbound WhatsApp message from a send-capable address. The typed config carries the recipient and payload: free-form text, bucket-backed media, interactive reply buttons or list menus (all only inside the open 24-hour session window), or an approved template (allowed anytime). Check Get WhatsApp Send Options first to learn the session state and allowed templates.

Bearer token required.

Request Body

NameTypeDescription
channel*
"whatsapp"Channel discriminator
address_id*
string (UUID)Send-capable WhatsApp address the message goes out from
config*
WhatsAppTextConfig | WhatsAppTemplatePayload | WhatsAppMediaPayload | WhatsAppInteractivePayloadDiscriminated by `type` (text | template | media | interactive); every variant carries `to` in E.164 format
service_name
stringOriginating service for attribution
resource_id
string (UUID)Originating resource for attribution
curl -X POST https://platform.ergondata.ai/api/v1/channels/send/whatsapp \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "channel": "whatsapp",
    "address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
    "config": {"type": "text", "to": "+5511999999999", "text": "Your order shipped!"}
  }'

Response

202 Accepted
{
  "status": "queued",
  "channel": "whatsapp",
  "log_id": "b2c3d4e5-6677-8899-aabb-ccddeeff0011",
  "provider_id": "whatsapp",
  "thread_id": "d4e5f6a7-b8c9-4012-d345-6789abcdef01",
  "idempotent_replay": false
}

WhatsApp Templates

Meta-approved message templates synchronized per WhatsApp config. Templates unlock outbound messages outside the 24-hour session window; each template must be explicitly enabled per address before agents can use it.

GET/api/v1/channels/configs/{config_id}/whatsapp/templates

List WhatsApp Templates

List the templates synchronized from Meta for this config, including approval status, activation state, and the addresses each template is enabled for.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)WhatsApp business-account config ID

Query Parameters

NameTypeDescription
limit
integerPage size (default 100, max 200)
offset
integerPagination offset (default 0)

Response Fields

NameTypeDescription
items*
WhatsAppTemplateResponse[]Templates with name, language, category, Meta status, components, activation state, and allowed_address_ids
total*
integerTotal templates for the config
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/whatsapp/templates \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "items": [
    {
      "id": "77e3a9b2-1234-4cde-9f00-aabbccddeeff",
      "name": "order_update",
      "language": "pt_BR",
      "category": "UTILITY",
      "status": "APPROVED",
      "components": [],
      "is_active": true,
      "allowed_address_ids": ["a1d2e3f4-5566-7788-99aa-bbccddeeff00"],
      "last_synced_at": "2026-04-03T14:22:00Z"
    }
  ],
  "total": 1
}
POST/api/v1/channels/configs/{config_id}/whatsapp/templates/sync

Sync WhatsApp Templates

Fetch the current template catalog from Meta and reconcile the local copy: new templates are added, changed ones updated, and removed ones deactivated. Returns the refreshed list.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)WhatsApp business-account config ID

Response Fields

NameTypeDescription
items*
WhatsAppTemplateResponse[]The synchronized template list
total*
integerTotal templates after the sync
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/whatsapp/templates/sync \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "items": [
    {
      "id": "77e3a9b2-1234-4cde-9f00-aabbccddeeff",
      "name": "order_update",
      "language": "pt_BR",
      "category": "UTILITY",
      "status": "APPROVED",
      "components": [],
      "is_active": true,
      "allowed_address_ids": [],
      "last_synced_at": "2026-04-05T09:00:00Z"
    }
  ],
  "total": 1
}
PUT/api/v1/channels/configs/{config_id}/addresses/{address_id}/templates/{template_id}

Allow Template For Address

Enable one approved template for one address under the config. Agents and API callers can only send templates that are enabled for the sending address.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)WhatsApp business-account config ID
address_id*
string (UUID)WhatsApp address under the config
template_id*
string (UUID)Synchronized template ID
curl -X PUT https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/templates/{template_id} \
  -H "Authorization: Bearer {token}"

Response

204 No Content
HTTP/1.1 204 No Content
DELETE/api/v1/channels/configs/{config_id}/addresses/{address_id}/templates/{template_id}

Disallow Template For Address

Disable one template for one address under the config. Subsequent sends of that template from the address are rejected.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)WhatsApp business-account config ID
address_id*
string (UUID)WhatsApp address under the config
template_id*
string (UUID)Synchronized template ID
curl -X DELETE https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/templates/{template_id} \
  -H "Authorization: Bearer {token}"

Response

204 No Content
HTTP/1.1 204 No Content

Activity

Inspect the channel type registry, paginated message activity (company-wide or per channel), single events, inbound attachment bytes, and ordered thread transcripts.

GET/api/v1/channels/channel-types

List Channel Types

Registry of channel implementations (slug, display name, icon, active flag).

Bearer token required.

Query Parameters

NameTypeDescription
include_inactive
booleanInclude types marked inactiveDefault: false

Response Fields

NameTypeDescription
[]*
ChannelTypeItem[]Channel types
curl "https://platform.ergondata.ai/api/v1/channels/channel-types?include_inactive=false" \
  -H "Authorization: Bearer {token}"

Response

200 OK
[
  {
    "slug": "email",
    "name": "Email",
    "icon": "mail",
    "is_active": true
  }
]
GET/api/v1/channels/companies/{company_id}/activity

List Activity

Paginated, company-wide activity log filtered to channels where the caller has channels:activity:view. channels:addresses:receive controls routing eligibility, not activity visibility.

Bearer token required.

Path Parameters

NameTypeDescription
company_id*
string (UUID)Company ID

Query Parameters

NameTypeDescription
channel
stringFilter by channel slug
direction
stringFilter by message direction
status
stringComma-separated status slugs
correlation_id
stringMatch a correlation ID
event_type
stringFilter by event type slug
search
stringCase-insensitive match on from_address or subject
date_from
string (ISO 8601)Inclusive lower bound on created_at
date_to
string (ISO 8601)Inclusive upper bound on created_at
page
integerPage number (1-based)Default: 1
limit
integerPage size (1–100)Default: 50

Response Fields

NameTypeDescription
items*
ActivityLogListItem[]id, event_type, channel, direction, from_address, to_addresses, subject, status, actor_type, error, created_at, optional correlation_id and summary
total*
integerTotal matching rows
page*
integerCurrent page
limit*
integerPage size
curl "https://platform.ergondata.ai/api/v1/channels/companies/{company_id}/activity?page=1&limit=50&channel=email" \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "items": [
    {
      "id": "b2c3d4e5-6677-8899-aabb-ccddeeff0011",
      "event_type": "channels.email.sent",
      "channel": "email",
      "direction": "outbound",
      "from_address": "Support <support@mail.example.com>",
      "to_addresses": ["customer@example.com"],
      "subject": "Your order has shipped",
      "status": "queued",
      "actor_type": "member",
      "correlation_id": "d4e5f6a7-b8c9-4012-d345-6789abcdef01",
      "summary": null,
      "error": null,
      "created_at": "2026-04-06T15:22:11Z"
    }
  ],
  "total": 128,
  "page": 1,
  "limit": 50
}
GET/api/v1/channels/companies/{company_id}/activity/{event_id}

Get Activity Event

Full detail for one company activity row, including payload and provider identifiers. Requires channels:activity:view on the row's channel.

Bearer token required.

Path Parameters

NameTypeDescription
company_id*
string (UUID)Company ID
event_id*
string (UUID)Activity log row ID
curl https://platform.ergondata.ai/api/v1/channels/companies/{company_id}/activity/{event_id} \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "id": "b2c3d4e5-6677-8899-aabb-ccddeeff0011",
  "event_type": "channels.email.sent",
  "channel": "email",
  "direction": "outbound",
  "from_address": "Support <support@mail.example.com>",
  "to_addresses": ["customer@example.com"],
  "subject": "Your order has shipped",
  "status": "delivered",
  "actor_type": "member",
  "actor_id": "9a000000-0000-4000-8000-000000000001",
  "correlation_id": "d4e5f6a7-b8c9-4012-d345-6789abcdef01",
  "summary": null,
  "error": null,
  "payload": {
    "html": "<p>Tracking: <b>1Z999...</b></p>",
    "text": "Tracking: 1Z999..."
  },
  "provider_id": "resend",
  "provider_message_id": "<msg_01hqabc@mail.example.com>",
  "created_at": "2026-04-06T15:22:11Z"
}
GET/api/v1/channels/configs/{config_id}/activity

List Channel Activity

Paginated immutable activity log scoped to a single channel config. Inbound rows require only channels:activity:view on the channel; subscription delivery state is exposed separately by the deliveries and claim APIs.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Query Parameters

NameTypeDescription
direction
stringFilter by message direction
status
stringComma-separated status slugs
correlation_id
stringMatch a correlation ID
event_type
stringFilter by event type slug
search
stringCase-insensitive match on from_address or subject
date_from
string (ISO 8601)Inclusive lower bound on created_at
date_to
string (ISO 8601)Inclusive upper bound on created_at
page
integerPage number (1-based)Default: 1
limit
integerPage size (1–100)Default: 50

Response Fields

NameTypeDescription
items*
ActivityLogListItem[]Activity rows for this channel
total*
integerTotal matching rows
page*
integerCurrent page
limit*
integerPage size
curl "https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/activity?page=1&limit=50" \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "items": [
    {
      "id": "b2c3d4e5-6677-8899-aabb-ccddeeff0011",
      "event_type": "channels.email.received",
      "channel": "email",
      "direction": "inbound",
      "from_address": "customer@example.com",
      "to_addresses": ["support@mail.example.com"],
      "subject": "Question about order #1042",
      "status": "delivered",
      "actor_type": null,
      "correlation_id": null,
      "summary": "When will it ship?",
      "error": null,
      "created_at": "2026-04-06T14:00:00Z"
    }
  ],
  "total": 12,
  "page": 1,
  "limit": 50
}
GET/api/v1/channels/configs/{config_id}/activity/consumptions

List Channel Deliveries

Paginated subscription-scoped delivery history joined to immutable event summaries. Filter by subscription or delivery status to observe ACK/NACK outcomes. Requires channels:activity:view and never returns lease tokens.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Query Parameters

NameTypeDescription
subscription_id
string (UUID)Return deliveries for one stable subscription
status
"pending" | "claimed" | "acked" | "failed"Filter by delivery state
direction
stringFilter the joined event by direction
event_status
stringFilter the joined immutable event status
search
stringMatch event type, sender, or subject
page
integerPage number (1-based)Default: 1
limit
integerPage size (1–100)Default: 50

Response Fields

NameTypeDescription
items*
ActivityConsumptionListItem[]Event summary plus subscription, status, attempts, consumer, and delivery timestamps
total*
integerTotal matching rows
page*
integerCurrent page
limit*
integerPage size
curl "https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/activity/consumptions?status=acked&page=1&limit=50" \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "items": [{
    "event": {
      "id": "b2c3d4e5-6677-8899-aabb-ccddeeff0011",
      "event_type": "channels.email.received",
      "direction": "inbound",
      "subject": "Question about order #1042",
      "status": "received",
      "created_at": "2026-08-15T15:00:00Z"
    },
    "subscription_id": "11111111-1111-4111-8111-111111111111",
    "status": "acked",
    "acked_at": "2026-08-15T15:01:00Z",
    "available_at": null,
    "lease_expires_at": null,
    "consumer_id": "inbox-worker-1",
    "attempt_count": 1,
    "created_at": "2026-08-15T15:00:01Z",
    "updated_at": "2026-08-15T15:01:00Z"
  }],
  "total": 1,
  "page": 1,
  "limit": 50
}
GET/api/v1/channels/configs/{config_id}/activity/{event_id}

Get Channel Activity Event

Full detail for one activity row scoped to a channel config, including payload and provider identifiers. Requires channels:activity:view on the channel.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
event_id*
string (UUID)Activity log row ID
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/activity/{event_id} \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "id": "b2c3d4e5-6677-8899-aabb-ccddeeff0011",
  "event_type": "channels.email.sent",
  "channel": "email",
  "direction": "outbound",
  "from_address": "Support <support@mail.example.com>",
  "to_addresses": ["customer@example.com"],
  "subject": "Your order has shipped",
  "status": "delivered",
  "actor_type": "member",
  "actor_id": "9a000000-0000-4000-8000-000000000001",
  "correlation_id": "d4e5f6a7-b8c9-4012-d345-6789abcdef01",
  "summary": null,
  "error": null,
  "payload": { "html": "<p>Tracking: 1Z999...</p>" },
  "provider_id": "resend",
  "provider_message_id": "<msg_01hqabc@mail.example.com>",
  "thread_id": "c3d4e5f6-7788-99aa-bbcc-ddeeff001122",
  "in_reply_to": null,
  "address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "created_at": "2026-04-06T15:22:11Z"
}
POST/api/v1/channels/configs/{config_id}/activity/claims

Claim Inbound Channel Deliveries

Atomically leases canonical channels.email.received deliveries for one inbox address and a stable subscription. Requires both consume and view. Replicas sharing a subscription compete; independent subscriptions each receive the event.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Request Body

NameTypeDescription
subscription_id*
string (UUID)Stable connector subscription identity
address_id*
string (UUID)Inbox address on this channel whose deliveries to claim
consumer_id*
stringCurrent worker or replica identifier
limit
integerMaximum deliveries to claim (1–100)Default: 10
visibility_timeout_seconds
integerLease duration before redelivery (1–3600)Default: 60
cursor
string | nullOpaque keyset cursor for continuing the current scan; omit it on a new polling pass

Response Fields

NameTypeDescription
items*
ActivityClaimedItem[]Immutable event plus subscription delivery lease
next_cursor*
string | nullCursor after the last claimed immutable event
idempotent_replay*
booleanTrue when Idempotency-Key matched a prior claim and the original batch was returned
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/activity/claims \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: claim-batch-1" \
  -d '{"subscription_id":"11111111-1111-4111-8111-111111111111","address_id":"a1d2e3f4-5566-7788-99aa-bbccddeeff00","consumer_id":"inbox-worker-1"}'

Response

200 OK
{
  "items": [{
    "event": {"id": "b2c3d4e5-6677-8899-aabb-ccddeeff0011", "event_type": "channels.email.received", "address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00"},
    "delivery": {
      "event_id": "b2c3d4e5-6677-8899-aabb-ccddeeff0011",
      "config_id": "8f2c1a00-1111-4222-8333-444455556666",
      "subscription_id": "11111111-1111-4111-8111-111111111111",
      "status": "claimed",
      "lease_token": "22222222-2222-4222-8222-222222222222",
      "lease_expires_at": "2026-08-15T16:00:00Z",
      "attempt_count": 1
    }
  }],
  "next_cursor": "opaque",
  "idempotent_replay": false
}
GET/api/v1/channels/configs/{config_id}/activity/{event_id}/attachments/{attachment_id}/file

Download Channel Activity Attachment

Stream one inbound email attachment as raw bytes with a bounded size and RFC 6266 filename. Requires channels:activity:view on the channel. attachment_id is payload.attachments[].resend_attachment_id. Unknown-length bodies are buffered up to the limit so oversized files still return 413. Binary transport — not an agent tool.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
event_id*
string (UUID)Activity log row ID
attachment_id*
stringProvider attachment id (resend_attachment_id)

Response Fields

NameTypeDescription
(body)*
application/octet-stream | …Raw attachment content
curl -L -o invoice.pdf \
  https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/activity/{event_id}/attachments/{attachment_id}/file \
  -H "Authorization: Bearer {token}"

Response

200 OK
POST/api/v1/channels/configs/{config_id}/activity/{event_id}/ack

Acknowledge Channel Activity Event

Acknowledges one claimed inbound delivery for a subscription using its active lease token. Repeating the same acknowledgement is idempotent.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
event_id*
string (UUID)Activity event ID

Request Body

NameTypeDescription
subscription_id*
string (UUID)Subscription that owns the delivery
lease_token*
string (UUID)Active token returned by the claim

Response Fields

NameTypeDescription
event_id*
string (UUID)Event ID
config_id*
string (UUID)Channel config ID
status*
"acked"Consumption status
acked_at*
string (ISO 8601)First acknowledgement time
idempotent_replay*
booleanTrue when already acknowledged
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/activity/{event_id}/ack \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"subscription_id":"11111111-1111-4111-8111-111111111111","lease_token":"22222222-2222-4222-8222-222222222222"}'

Response

200 OK
{
  "event_id": "b2c3d4e5-6677-8899-aabb-ccddeeff0011",
  "config_id": "8f2c1a00-1111-4222-8333-444455556666",
  "subscription_id": "11111111-1111-4111-8111-111111111111",
  "status": "acked",
  "acked_at": "2026-08-14T05:00:00Z",
  "available_at": null,
  "consumer_id": "inbox-worker-1",
  "attempt_count": 0,
  "idempotent_replay": false
}
POST/api/v1/channels/configs/{config_id}/activity/{event_id}/nack

Negatively Acknowledge Channel Activity Event

Requeues or fails one claimed subscription delivery. The body must include the active subscription and lease token. Repeating the same token after a successful nack is an idempotent replay.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
event_id*
string (UUID)Activity event ID

Request Body

NameTypeDescription
subscription_id*
string (UUID)Subscription that owns the delivery
lease_token*
string (UUID)Active token returned by the claim
requeue
booleanReturn to pending instead of marking failedDefault: true
delay_seconds
integerDelay before a requeued event is available (0–86400)Default: 0
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/activity/{event_id}/nack \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"subscription_id":"11111111-1111-4111-8111-111111111111","lease_token":"22222222-2222-4222-8222-222222222222","requeue":true,"delay_seconds":30}'

Response

200 OK
{
  "event_id": "b2c3d4e5-6677-8899-aabb-ccddeeff0011",
  "config_id": "8f2c1a00-1111-4222-8333-444455556666",
  "subscription_id": "11111111-1111-4111-8111-111111111111",
  "status": "pending",
  "acked_at": null,
  "available_at": "2026-08-14T05:00:30Z",
  "consumer_id": "inbox-worker-1",
  "attempt_count": 1,
  "idempotent_replay": false
}
GET/api/v1/channels/threads/{thread_id}/messages

List Thread Messages

Chronological transcript of a conversation thread, used for automation and agent context. Full inbound message bodies require channels:addresses:receive on each destination address represented in the thread.

Bearer token required.

Path Parameters

NameTypeDescription
thread_id*
string (UUID)Conversation thread ID

Response Fields

NameTypeDescription
messages*
ThreadMessageItem[]id, direction, from_address, to_addresses, subject, status, actor_type, html, text, created_at
curl https://platform.ergondata.ai/api/v1/channels/threads/{thread_id}/messages \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "messages": [
    {
      "id": "b2c3d4e5-6677-8899-aabb-ccddeeff0011",
      "direction": "inbound",
      "from_address": "customer@example.com",
      "to_addresses": ["support@mail.example.com"],
      "subject": "Question about order #1042",
      "status": "delivered",
      "actor_type": null,
      "html": "<p>When will it ship?</p>",
      "text": null,
      "created_at": "2026-04-06T14:00:00Z"
    },
    {
      "id": "c3d4e5f6-7788-9900-bbcc-ddeeff002233",
      "direction": "outbound",
      "from_address": "Support <support@mail.example.com>",
      "to_addresses": ["customer@example.com"],
      "subject": "Re: Question about order #1042",
      "status": "delivered",
      "actor_type": "member",
      "html": "<p>It ships tomorrow morning.</p>",
      "text": null,
      "created_at": "2026-04-06T14:05:22Z"
    }
  ]
}

Service grants

Service grants let another service (for example workflows) send from a specific address when its service_name and resource_id match. They are distinct from address ACL grants and from channel-zone access grants.

GET/api/v1/channels/addresses/granted

List Granted Addresses

Addresses the caller may use for a given service (and optional resource), typically when configuring a template in another product.

Bearer token required. Company context from the member token or x-company-id header.

Query Parameters

NameTypeDescription
service*
stringService slug to match stored grants
resource_id
stringFurther narrow to one external resource
sendable
booleanWhen true, restrict to addresses eligible to sendDefault: false
channel_type
stringFilter by underlying config channel type

Response Fields

NameTypeDescription
[]*
GrantedAddressResponse[]id, channel_config_id, address, display_name, direction, status, channel_type, channel_name, timestamps
curl "https://platform.ergondata.ai/api/v1/channels/addresses/granted?service=workflows&resource_id=f47ac10b-58cc-4372-a567-0e02b2c3d479" \
  -H "Authorization: Bearer {token}" \
  -H "x-company-id: {company_id}"

Response

200 OK
[
  {
    "id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
    "channel_config_id": "8f2c1a00-1111-4222-8333-444455556666",
    "address": "hello@mail.example.com",
    "display_name": "Support",
    "direction": "send",
    "status": "active",
    "channel_type": "email",
    "channel_name": "Primary email",
    "created_at": "2026-04-02T10:00:00Z",
    "updated_at": "2026-04-02T10:00:00Z"
  }
]
GET/api/v1/channels/configs/{config_id}/addresses/{address_id}/grants

List Grants

Return the service grants attached to an address.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
address_id*
string (UUID)Address ID

Response Fields

NameTypeDescription
[]*
GrantResponse[]id, address_id, company_id, service_name, resource_id, resource_label, label, created_by, created_at
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/grants \
  -H "Authorization: Bearer {token}"

Response

200 OK
[
  {
    "id": "a7c9e1d2-3344-4566-8788-99aabbccddee",
    "address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
    "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
    "service_name": "workflows",
    "resource_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "resource_label": "Order notifications",
    "label": "Production",
    "created_by": "9a000000-0000-4000-8000-000000000001",
    "created_at": "2026-04-05T12:00:00Z"
  }
]
POST/api/v1/channels/configs/{config_id}/addresses/{address_id}/grants

Create Grant

Record that an external service may send using this address for a given resource scope.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
address_id*
string (UUID)Address ID

Request Body

NameTypeDescription
service_name*
stringCalling service slug (1–50 characters)
resource_id
string | nullScoped resource in the calling service
resource_label
string | nullHuman-readable resource label
label
string | nullOptional note for operators
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/grants \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "service_name": "workflows",
    "resource_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
    "resource_label": "Order notifications",
    "label": "Production"
  }'

Response

201 Created
{
  "id": "a7c9e1d2-3344-4566-8788-99aabbccddee",
  "address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "service_name": "workflows",
  "resource_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "resource_label": "Order notifications",
  "label": "Production",
  "created_by": "9a000000-0000-4000-8000-000000000001",
  "created_at": "2026-04-05T12:00:00Z"
}
DELETE/api/v1/channels/grants/{grant_id}

Revoke Grant

Revoke a service grant by ID.

Bearer token required.

Path Parameters

NameTypeDescription
grant_id*
string (UUID)Grant ID
curl -X DELETE https://platform.ergondata.ai/api/v1/channels/grants/{grant_id} \
  -H "Authorization: Bearer {token}"

Response

204 No Content

Address ACL

Per-address ACL grants control which IAM principals or roles may send from a specific address or view inbound messages received at it. Receive grants authorize visibility; they do not enable, disable, deliver, or route inbound mail.

GET/api/v1/channels/configs/{config_id}/addresses/{address_id}/acl

List Address ACL

List ACL grants for a specific address — the permission grants that control who can use this address.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
address_id*
string (UUID)Address ID

Response Fields

NameTypeDescription
[]*
GrantResponse[]id, principal_type, principal_id, principal_label, permission_name, resource, effect, granted_at
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/acl \
  -H "Authorization: Bearer {token}"

Response

200 OK
[
  {
    "id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
    "principal_type": "member",
    "principal_id": "9a000000-0000-4000-8000-000000000001",
    "principal_label": "Jane Doe",
    "permission_name": "channels:addresses:send",
    "resource": "org/c0ffee00-cafe-babe-dead-beefcafebabe/channel/8f2c1a00-1111-4222-8333-444455556666/address/a1d2e3f4-5566-7788-99aa-bbccddeeff00",
    "effect": "allow",
    "granted_at": "2026-04-05T10:00:00Z"
  }
]
POST/api/v1/channels/configs/{config_id}/addresses/{address_id}/acl

Grant Address ACL

Grant a principal an address permission. Send maps to channels:addresses:send. Receive maps to channels:addresses:receive and makes the principal eligible as an inbound route target; activity visibility remains governed by channels:activity:view.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
address_id*
string (UUID)Address ID

Request Body

NameTypeDescription
principal_type*
stringPrincipal kind: member, api_key, or agent
principal_id*
stringPrincipal identifier
capability
stringsend or receiveDefault: send

Response Fields

NameTypeDescription
id*
stringGrant ID
principal_type*
stringPrincipal kind
principal_id*
stringPrincipal identifier
principal_label*
stringResolved display name
permission_name*
stringPermission slug granted
resource*
stringScoped resource URN
effect*
stringGrant effect (allow)
granted_at*
string (ISO 8601)When the grant was created
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/acl \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "principal_type": "member",
    "principal_id": "9a000000-0000-4000-8000-000000000001",
    "capability": "send"
  }'

Response

201 Created
{
  "id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "principal_type": "member",
  "principal_id": "9a000000-0000-4000-8000-000000000001",
  "principal_label": "Jane Doe",
  "permission_name": "channels:addresses:send",
  "resource": "org/c0ffee00-cafe-babe-dead-beefcafebabe/channel/8f2c1a00-1111-4222-8333-444455556666/address/a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "effect": "allow",
  "granted_at": "2026-04-05T10:00:00Z"
}
DELETE/api/v1/channels/configs/{config_id}/addresses/{address_id}/acl/{grant_id}

Revoke Address ACL

Revoke a permission grant on this address.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
address_id*
string (UUID)Address ID
grant_id*
string (UUID)ACL grant ID
curl -X DELETE https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/acl/{grant_id} \
  -H "Authorization: Bearer {token}"

Response

204 No Content
GET/api/v1/channels/configs/{config_id}/addresses/{address_id}/eligible-principals

List Eligible Principals

List principals that can be granted ACL on this address, for building a grant picker.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
address_id*
string (UUID)Address ID

Response Fields

NameTypeDescription
[]*
EligiblePrincipal[]principal_type, principal_id, label
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/eligible-principals \
  -H "Authorization: Bearer {token}"

Response

200 OK
[
  {
    "principal_type": "member",
    "principal_id": "9a000000-0000-4000-8000-000000000001",
    "label": "Jane Doe"
  }
]
GET/api/v1/channels/configs/{config_id}/addresses/{address_id}/access/eligible

List Address Access Eligibility

List principals eligible on the concrete address. Requires channels:permissions:manage and forwards the optional permission filter to IAM.

Bearer token required.

Query Parameters

NameTypeDescription
permission
stringFilter to principals eligible for this permission, such as channels:addresses:receive or channels:addresses:send

Response Fields

NameTypeDescription
[]*
EligiblePrincipal[]principal_type, principal_id, label
curl "https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/access/eligible?permission=channels%3Aaddresses%3Areceive" -H "Authorization: Bearer {token}"

Response

200 OK
[
  {
    "principal_type": "member",
    "principal_id": "9a000000-0000-4000-8000-000000000001",
    "label": "Jane Doe"
  }
]
GET/api/v1/channels/configs/{config_id}/addresses/{address_id}/access/resource-types

List Address Access Resource Types

List grantable address permissions and resource types. Directional send/receive permissions are filtered to the address direction.

Bearer token with channels:permissions:manage on the address.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Parent channel config ID
address_id*
string (UUID)Channel address ID

Response Fields

NameTypeDescription
resource_types*
ResourceTypeNode[]Channels resource-type tree
permissions*
PermissionOption[]Permissions valid for this address
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/access/resource-types \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "resource_types": [],
  "permissions": [{
    "id": "7e5d6c4b-3a21-4f09-8e7d-6c5b4a392817",
    "name": "channels:addresses:receive",
    "scope_anchor": "instance",
    "display_order": 0
  }]
}
GET/api/v1/channels/configs/{config_id}/addresses/{address_id}/access/grants

List Address Access Grants

List paginated IAM grants scoped to one channel address.

Bearer token with channels:permissions:manage on the address.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Parent channel config ID
address_id*
string (UUID)Channel address ID

Query Parameters

NameTypeDescription
page
integerPage number (1-based)Default: 1
limit
integerPage size (1–500)Default: 100

Response Fields

NameTypeDescription
items*
GrantEntry[]Address-scoped grants
total*
integerTotal grants
page*
integerCurrent page
limit*
integerPage size
curl "https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/access/grants?page=1&limit=100" \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "items": [],
  "total": 0,
  "page": 1,
  "limit": 100
}
POST/api/v1/channels/configs/{config_id}/addresses/{address_id}/access/grants

Create Address Access Grant

Grant one catalog permission on the concrete address. Send and receive permissions must match the address direction.

Bearer token with channels:permissions:manage on the address.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Parent channel config ID
address_id*
string (UUID)Channel address ID

Request Body

NameTypeDescription
principal_type*
"member" | "api_key" | "agent" | "role" | "team"IAM principal kind
principal_id*
string (UUID)IAM principal ID
permission_id*
string (UUID)Permission UUID from this address's resource-types response
resource
string | nullMust equal this address resource when provided
effect
stringGrant effectDefault: allow
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/access/grants \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"principal_type":"agent","principal_id":"{principal_id}","permission_id":"{permission_id}"}'

Response

201 Created
{
  "id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "permission_id": "7e5d6c4b-3a21-4f09-8e7d-6c5b4a392817",
  "name": "channels:addresses:receive",
  "resource": "org/{company_id}/channel/{config_id}/address/{address_id}",
  "effect": "allow",
  "is_system": false,
  "granted_at": "2026-08-06T12:00:00Z"
}
DELETE/api/v1/channels/configs/{config_id}/addresses/{address_id}/access/grants/{grant_id}

Delete Address Access Grant

Revoke one IAM grant scoped to the channel address.

Bearer token with channels:permissions:manage on the address.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Parent channel config ID
address_id*
string (UUID)Channel address ID
grant_id*
string (UUID)IAM grant ID
curl -X DELETE https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/access/grants/{grant_id} \
  -H "Authorization: Bearer {token}"

Response

204 No Content

Inbound Routes

Route inbound email or WhatsApp into one Workflows process entry phase, to observer participants, one automatic-reply agent, and optionally one legacy human-assignment queue. Conversations binds exactly one conversation per external thread and requests process intake only when a new episode is created.

GET/api/v1/channels/configs/{config_id}/inbound-routes

List Config Routes

List all inbound routes under a config. Requires channels:channels:manage on the channel.

Bearer token required.

Response Fields

NameTypeDescription
[]*
InboundRouteResponse[]Routes ordered by creation time
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/inbound-routes -H "Authorization: Bearer {token}"

Response

200 OK
[{
  "id": "70000000-0000-4000-8000-000000000001",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "target_principal_type": "agent",
  "target_id": "90000000-0000-4000-8000-000000000001",
  "mode": "auto-reply",
  "reply_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "status": "active",
  "address_status": "allowed",
  "policy": {"max_auto_replies_per_thread": 10},
  "created_at": "2026-07-30T12:00:00Z",
  "updated_at": "2026-07-30T12:00:00Z"
}]
GET/api/v1/channels/configs/{config_id}/addresses/{address_id}/inbound-routes

List Address Routes

List routes for an address. Requires channels:addresses:manage on the address.

Bearer token required.

Response Fields

NameTypeDescription
[]*
InboundRouteResponse[]Routes ordered by creation time
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/inbound-routes -H "Authorization: Bearer {token}"

Response

200 OK
[{
  "id": "70000000-0000-4000-8000-000000000001",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "target_principal_type": "agent",
  "target_id": "90000000-0000-4000-8000-000000000001",
  "mode": "auto-reply",
  "reply_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "status": "active",
  "address_status": "allowed",
  "policy": {"max_auto_replies_per_thread": 10},
  "created_at": "2026-07-30T12:00:00Z",
  "updated_at": "2026-07-30T12:00:00Z"
}]
POST/api/v1/channels/configs/{config_id}/addresses/{address_id}/inbound-routes

Create Address Route

Create a workflow_phase process route or user/team notify route. Process routes require Workflows items:create on the entry phase. auto-reply creation through this address-owned path returns 403.

Bearer token required.

Request Body

NameTypeDescription
target_principal_type*
"user" | "team" | "agent" | "workflow_phase"Process accepts workflow_phase; notify accepts user/team
target_id*
string (UUID)Target entity/principal ID
mode*
"notify" | "process" | "auto-reply"Must be notify or process on this path
reply_address_id
string (UUID) | nullMust be null for notify or process
status
"active" | "paused"Route stateDefault: active
policy
objectMust be empty for notify or processDefault: {}

Response Fields

NameTypeDescription
id*
string (UUID)Route ID
company_id*
string (UUID)Owning company
channel_address_id*
string (UUID)Receiving address
target_principal_type*
"user" | "team" | "agent" | "workflow_phase"Route target kind
target_id*
string (UUID)Canonical principal or workflow entry-phase ID
mode*
"notify" | "auto-reply" | "process"Route behavior
reply_address_id*
string (UUID) | nullAuto-reply sender; null for notify, assign, and process
status*
"active" | "paused"Route owner's desired state
address_status*
"allowed" | "blocked"Address owner's auto-reply gate
policy*
objectNotify, assign, and process use {}; auto-reply supports max_auto_replies_per_thread (1–100, default 10)
created_at*
string (ISO 8601)Creation time
updated_at*
string (ISO 8601)Last update time
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/inbound-routes -H "Authorization: Bearer {token}" -H "Content-Type: application/json" -d '{"target_principal_type":"user","target_id":"{principal_id}","mode":"notify"}'

Response

201 Created
{
  "id": "70000000-0000-4000-8000-000000000001",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "target_principal_type": "agent",
  "target_id": "90000000-0000-4000-8000-000000000001",
  "mode": "auto-reply",
  "reply_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "status": "active",
  "address_status": "allowed",
  "policy": {"max_auto_replies_per_thread": 10},
  "created_at": "2026-07-30T12:00:00Z",
  "updated_at": "2026-07-30T12:00:00Z"
}
GET/api/v1/channels/configs/{config_id}/addresses/{address_id}/inbound-routes/{route_id}

Get Address Route

Get one address route. Requires channels:addresses:manage on the address.

Bearer token required.

Response Fields

NameTypeDescription
id*
string (UUID)Route ID
company_id*
string (UUID)Owning company
channel_address_id*
string (UUID)Receiving address
target_principal_type*
"user" | "team" | "agent" | "workflow_phase"Route target kind
target_id*
string (UUID)Canonical principal or workflow entry-phase ID
mode*
"notify" | "auto-reply" | "process"Route behavior
reply_address_id*
string (UUID) | nullAuto-reply sender; null for notify, assign, and process
status*
"active" | "paused"Route owner's desired state
address_status*
"allowed" | "blocked"Address owner's auto-reply gate
policy*
objectNotify, assign, and process use {}; auto-reply supports max_auto_replies_per_thread (1–100, default 10)
created_at*
string (ISO 8601)Creation time
updated_at*
string (ISO 8601)Last update time
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/inbound-routes/{route_id} -H "Authorization: Bearer {token}"

Response

200 OK
{
  "id": "70000000-0000-4000-8000-000000000001",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "target_principal_type": "agent",
  "target_id": "90000000-0000-4000-8000-000000000001",
  "mode": "auto-reply",
  "reply_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "status": "active",
  "address_status": "allowed",
  "policy": {"max_auto_replies_per_thread": 10},
  "created_at": "2026-07-30T12:00:00Z",
  "updated_at": "2026-07-30T12:00:00Z"
}
PATCH/api/v1/channels/configs/{config_id}/addresses/{address_id}/inbound-routes/{route_id}

Update Address Route

Update a process or notify route with channels:addresses:manage. Process activation revalidates Workflows access. For an agent-owned auto-reply route, the only accepted body is address_status.

Bearer token required.

Request Body

NameTypeDescription
target_principal_type
"user" | "team" | "agent" | "workflow_phase" | nullNotify target kind
target_id
string (UUID) | nullNotify target
mode
"notify" | "process" | "auto-reply" | nullCannot convert to auto-reply through this path
reply_address_id
string (UUID) | nullNotify routes cannot use a reply address
status
"active" | "paused" | nullNotify route state
address_status
"allowed" | "blocked" | nullOnly field address managers may change on auto-reply
policy
object | nullNotify policy must be empty

Response Fields

NameTypeDescription
id*
string (UUID)Route ID
company_id*
string (UUID)Owning company
channel_address_id*
string (UUID)Receiving address
target_principal_type*
"user" | "team" | "agent" | "workflow_phase"Route target kind
target_id*
string (UUID)Canonical principal or workflow entry-phase ID
mode*
"notify" | "auto-reply" | "process"Route behavior
reply_address_id*
string (UUID) | nullAuto-reply sender; null for notify, assign, and process
status*
"active" | "paused"Route owner's desired state
address_status*
"allowed" | "blocked"Address owner's auto-reply gate
policy*
objectNotify, assign, and process use {}; auto-reply supports max_auto_replies_per_thread (1–100, default 10)
created_at*
string (ISO 8601)Creation time
updated_at*
string (ISO 8601)Last update time
curl -X PATCH https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/inbound-routes/{route_id} -H "Authorization: Bearer {token}" -H "Content-Type: application/json" -d '{"address_status":"blocked"}'

Response

200 OK
{
  "id": "70000000-0000-4000-8000-000000000001",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "target_principal_type": "agent",
  "target_id": "90000000-0000-4000-8000-000000000001",
  "mode": "auto-reply",
  "reply_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "status": "active",
  "address_status": "allowed",
  "policy": {"max_auto_replies_per_thread": 10},
  "created_at": "2026-07-30T12:00:00Z",
  "updated_at": "2026-07-30T12:00:00Z"
}
DELETE/api/v1/channels/configs/{config_id}/addresses/{address_id}/inbound-routes/{route_id}

Delete Address Route

Delete a process or notify route with channels:addresses:manage. Deleting an auto-reply through the address path returns 403; block it or use agent settings.

Bearer token required.

curl -X DELETE https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/addresses/{address_id}/inbound-routes/{route_id} -H "Authorization: Bearer {token}"

Response

204 No Content
GET/api/v1/channels/companies/{company_id}/agents/{agent_id}/inbound-routes

List Agent Route Settings

List active receive-capable addresses eligible for this agent, with all routes and send-capability reply candidates. Requires agents:agents:channels:manage on the agent; address visibility remains row-filtered.

Bearer token required.

Response Fields

NameTypeDescription
[]*
AgentInboundAddressResponse[]Agent route settings per visible address
curl https://platform.ergondata.ai/api/v1/channels/companies/{company_id}/agents/{agent_id}/inbound-routes -H "Authorization: Bearer {token}"

Response

200 OK
[
  {
    "address": {"id":"a1d2e3f4-5566-7788-99aa-bbccddeeff00","channel_config_id":"8f2c1a00-1111-4222-8333-444455556666","address":"support@example.com","display_name":"Support","direction":"both","status":"active"},
    "routes": [{
  "id": "70000000-0000-4000-8000-000000000001",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "target_principal_type": "agent",
  "target_id": "90000000-0000-4000-8000-000000000001",
  "mode": "auto-reply",
  "reply_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "status": "active",
  "address_status": "allowed",
  "policy": {"max_auto_replies_per_thread": 10},
  "created_at": "2026-07-30T12:00:00Z",
  "updated_at": "2026-07-30T12:00:00Z"
}],
    "reply_addresses": [{"id":"a1d2e3f4-5566-7788-99aa-bbccddeeff00","channel_config_id":"8f2c1a00-1111-4222-8333-444455556666","address":"support@example.com","display_name":"Support","direction":"both","status":"active","can_send":true}]
  }
]
POST/api/v1/channels/companies/{company_id}/agents/{agent_id}/inbound-routes/{address_id}

Create Agent Auto-Reply

Create this agent's auto-reply route. Requires agents:agents:channels:manage on the agent plus the agent's receive grant on the source and send grant on the reply address. Only one active auto-reply may exist per address.

Bearer token required.

Request Body

NameTypeDescription
reply_address_id
string (UUID) | nullReply sender; defaults to the source address
status
"active" | "paused"Agent-owned route stateDefault: active
policy
objectmax_auto_replies_per_thread must be 1–100Default: {"max_auto_replies_per_thread":10}

Response Fields

NameTypeDescription
id*
string (UUID)Route ID
company_id*
string (UUID)Owning company
channel_address_id*
string (UUID)Receiving address
target_principal_type*
"user" | "team" | "agent" | "workflow_phase"Route target kind
target_id*
string (UUID)Canonical principal or workflow entry-phase ID
mode*
"notify" | "auto-reply" | "process"Route behavior
reply_address_id*
string (UUID) | nullAuto-reply sender; null for notify, assign, and process
status*
"active" | "paused"Route owner's desired state
address_status*
"allowed" | "blocked"Address owner's auto-reply gate
policy*
objectNotify, assign, and process use {}; auto-reply supports max_auto_replies_per_thread (1–100, default 10)
created_at*
string (ISO 8601)Creation time
updated_at*
string (ISO 8601)Last update time
curl -X POST https://platform.ergondata.ai/api/v1/channels/companies/{company_id}/agents/{agent_id}/inbound-routes/{address_id} -H "Authorization: Bearer {token}" -H "Content-Type: application/json" -d '{"policy":{"max_auto_replies_per_thread":10}}'

Response

201 Created
{
  "id": "70000000-0000-4000-8000-000000000001",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "target_principal_type": "agent",
  "target_id": "90000000-0000-4000-8000-000000000001",
  "mode": "auto-reply",
  "reply_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "status": "active",
  "address_status": "allowed",
  "policy": {"max_auto_replies_per_thread": 10},
  "created_at": "2026-07-30T12:00:00Z",
  "updated_at": "2026-07-30T12:00:00Z"
}
PATCH/api/v1/channels/companies/{company_id}/agents/{agent_id}/inbound-routes/{address_id}/{route_id}

Update Agent Auto-Reply

Reconfigure or pause/resume this agent's owned auto-reply route. Requires agents:agents:channels:manage; address_status is not agent-controlled.

Bearer token required.

Request Body

NameTypeDescription
reply_address_id
string (UUID) | nullReply sender
status
"active" | "paused" | nullAgent-owned route state
policy
object | nullmax_auto_replies_per_thread must be 1–100

Response Fields

NameTypeDescription
id*
string (UUID)Route ID
company_id*
string (UUID)Owning company
channel_address_id*
string (UUID)Receiving address
target_principal_type*
"user" | "team" | "agent" | "workflow_phase"Route target kind
target_id*
string (UUID)Canonical principal or workflow entry-phase ID
mode*
"notify" | "auto-reply" | "process"Route behavior
reply_address_id*
string (UUID) | nullAuto-reply sender; null for notify, assign, and process
status*
"active" | "paused"Route owner's desired state
address_status*
"allowed" | "blocked"Address owner's auto-reply gate
policy*
objectNotify, assign, and process use {}; auto-reply supports max_auto_replies_per_thread (1–100, default 10)
created_at*
string (ISO 8601)Creation time
updated_at*
string (ISO 8601)Last update time
curl -X PATCH https://platform.ergondata.ai/api/v1/channels/companies/{company_id}/agents/{agent_id}/inbound-routes/{address_id}/{route_id} -H "Authorization: Bearer {token}" -H "Content-Type: application/json" -d '{"status":"paused"}'

Response

200 OK
{
  "id": "70000000-0000-4000-8000-000000000001",
  "company_id": "c0ffee00-cafe-babe-dead-beefcafebabe",
  "channel_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "target_principal_type": "agent",
  "target_id": "90000000-0000-4000-8000-000000000001",
  "mode": "auto-reply",
  "reply_address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "status": "active",
  "address_status": "allowed",
  "policy": {"max_auto_replies_per_thread": 10},
  "created_at": "2026-07-30T12:00:00Z",
  "updated_at": "2026-07-30T12:00:00Z"
}
DELETE/api/v1/channels/companies/{company_id}/agents/{agent_id}/inbound-routes/{address_id}/{route_id}

Delete Agent Auto-Reply

Delete this agent's owned auto-reply route. Requires agents:agents:channels:manage on the agent.

Bearer token required.

curl -X DELETE https://platform.ergondata.ai/api/v1/channels/companies/{company_id}/agents/{agent_id}/inbound-routes/{address_id}/{route_id} -H "Authorization: Bearer {token}"

Response

204 No Content

Channel zone access

Govern the zero-trust channel zone rooted at a config: enumerate resource types and permissions, manage zone-level access grants, list principals eligible for access, and approve, reject, or sever inbound connection requests from other principals.

GET/api/v1/channels/configs/{config_id}/access/grants

List Access Grants

Paginated list of access grants on this channel zone.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Query Parameters

NameTypeDescription
page
integerPage number (1-based)Default: 1
limit
integerPage size (1–500)Default: 100

Response Fields

NameTypeDescription
items*
GrantResponse[]id, permission_id, name, resource, effect, is_system, granted_at
total
integerTotal matching rows
page
integerCurrent page
limit
integerPage size
curl "https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/access/grants?page=1&limit=100" \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "items": [
    {
      "id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
      "permission_id": "7e5d6c4b-3a21-4f09-8e7d-6c5b4a392817",
      "name": "channels:channels:manage",
      "resource": "org/c0ffee00-cafe-babe-dead-beefcafebabe/channel/8f2c1a00-1111-4222-8333-444455556666",
      "effect": "allow",
      "is_system": false,
      "granted_at": "2026-04-05T10:00:00Z"
    }
  ],
  "total": 3,
  "page": 1,
  "limit": 100
}
POST/api/v1/channels/configs/{config_id}/access/grants

Create Access Grant

Grant a permission on this channel zone (or a nested resource) to a principal.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Request Body

NameTypeDescription
principal_type*
stringPrincipal kind: member, api_key, agent, or role
principal_id*
stringPrincipal identifier
permission_id*
stringPermission to grant (from List Channel Permissions)
resource
string | nullResource URN to scope the grant to; defaults to the channel zone
effect
stringGrant effectDefault: allow

Response Fields

NameTypeDescription
id*
stringGrant ID
permission_id*
stringPermission granted
name*
stringPermission slug
resource*
stringScoped resource URN
effect*
stringGrant effect
is_system*
booleanWhether the grant is system-managed
granted_at*
string (ISO 8601)When the grant was created
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/access/grants \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "principal_type": "member",
    "principal_id": "9a000000-0000-4000-8000-000000000001",
    "permission_id": "7e5d6c4b-3a21-4f09-8e7d-6c5b4a392817",
    "effect": "allow"
  }'

Response

201 Created
{
  "id": "1a2b3c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
  "permission_id": "7e5d6c4b-3a21-4f09-8e7d-6c5b4a392817",
  "name": "channels:channels:manage",
  "resource": "org/c0ffee00-cafe-babe-dead-beefcafebabe/channel/8f2c1a00-1111-4222-8333-444455556666",
  "effect": "allow",
  "is_system": false,
  "granted_at": "2026-04-05T10:00:00Z"
}
DELETE/api/v1/channels/configs/{config_id}/access/grants/{grant_id}

Delete Access Grant

Revoke an access grant on this channel zone by ID.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
grant_id*
string (UUID)Access grant ID
curl -X DELETE https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/access/grants/{grant_id} \
  -H "Authorization: Bearer {token}"

Response

204 No Content
GET/api/v1/channels/configs/{config_id}/access/permissions

List Channel Permissions

Permissions that can be granted on this channel zone and its resources.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Response Fields

NameTypeDescription
[]*
PermissionOption[]id, name, friendly_name, description, scope_anchor, display_order, resource_type_id, parent_resource_type_slug
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/access/permissions \
  -H "Authorization: Bearer {token}"

Response

200 OK
[
  {
    "id": "7e5d6c4b-3a21-4f09-8e7d-6c5b4a392817",
    "name": "channels:addresses:send",
    "friendly_name": "Send",
    "friendly_name_singular": "Send",
    "description": "Send messages from this address",
    "scope_anchor": "instance",
    "display_order": 10,
    "resource_type_id": "2b1c0d9e-8f7a-4b6c-9d5e-4f3a2b1c0d9e",
    "parent_resource_type_slug": "channel"
  }
]
GET/api/v1/channels/configs/{config_id}/access/resource-types

List Resource Types

Resource-type tree for the channel zone plus the permissions available at each level, for building an access editor.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Response Fields

NameTypeDescription
resource_types*
ResourceTypeNode[]Nested resource types (id, name, slug, parent_id, children)
permissions*
PermissionOption[]Permissions available across the tree
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/access/resource-types \
  -H "Authorization: Bearer {token}"

Response

200 OK
{
  "resource_types": [
    {
      "id": "2b1c0d9e-8f7a-4b6c-9d5e-4f3a2b1c0d9e",
      "name": "Channel",
      "slug": "channel",
      "parent_id": null,
      "children": [
        {
          "id": "3c2d1e0f-9a8b-4c7d-8e6f-5a4b3c2d1e0f",
          "name": "Address",
          "slug": "address",
          "parent_id": "2b1c0d9e-8f7a-4b6c-9d5e-4f3a2b1c0d9e",
          "children": []
        }
      ]
    }
  ],
  "permissions": [
    {
      "id": "7e5d6c4b-3a21-4f09-8e7d-6c5b4a392817",
      "name": "channels:addresses:send",
      "friendly_name": "Send",
      "scope_anchor": "instance"
    }
  ]
}
GET/api/v1/channels/configs/{config_id}/access/eligible

List Eligible Principals

Principals eligible to receive access on this channel zone, for building a grant picker.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Response Fields

NameTypeDescription
[]*
EligiblePrincipal[]principal_type, principal_id, label
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/access/eligible \
  -H "Authorization: Bearer {token}"

Response

200 OK
[
  {
    "principal_type": "member",
    "principal_id": "9a000000-0000-4000-8000-000000000001",
    "label": "Jane Doe"
  }
]
GET/api/v1/channels/configs/{config_id}/access/connection-requests

List Connection Requests

List requests from other principals asking to connect to this channel zone.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Query Parameters

NameTypeDescription
status
stringFilter by request statusDefault: pending

Response Fields

NameTypeDescription
[]*
ConnectionRequestEntry[]id, principal_id, target_service, target_resource, status, created_at, plus optional connection_id, requested_by, requested_permissions, message, decided_at, decided_by
curl "https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/access/connection-requests?status=pending" \
  -H "Authorization: Bearer {token}"

Response

200 OK
[
  {
    "id": "11112222-3333-4444-5555-666677778888",
    "principal_id": "9a000000-0000-4000-8000-000000000002",
    "target_service": "workflows",
    "target_resource": "org/c0ffee00-cafe-babe-dead-beefcafebabe/channel/8f2c1a00-1111-4222-8333-444455556666",
    "status": "pending",
    "requested_by": "9a000000-0000-4000-8000-000000000002",
    "requested_permissions": ["channels:addresses:send"],
    "message": "Need to send order notifications",
    "connection_id": null,
    "decided_at": null,
    "decided_by": null,
    "created_at": "2026-04-05T09:00:00Z"
  }
]
POST/api/v1/channels/configs/{config_id}/access/connection-requests/{request_id}/approve

Approve Connection Request

Approve a pending connection request, optionally granting a specific set of permissions.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
request_id*
string (UUID)Connection request ID

Request Body

NameTypeDescription
grant
booleanWhether to grant access on approvalDefault: true
permissions
string[] | nullPermission slugs to grant; defaults to the requested permissions
label
string | nullOptional label for the resulting connection
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/access/connection-requests/{request_id}/approve \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{
    "grant": true,
    "permissions": ["channels:addresses:send"],
    "label": "Order notifications"
  }'

Response

200 OK
{
  "id": "11112222-3333-4444-5555-666677778888",
  "principal_id": "9a000000-0000-4000-8000-000000000002",
  "target_service": "workflows",
  "target_resource": "org/c0ffee00-cafe-babe-dead-beefcafebabe/channel/8f2c1a00-1111-4222-8333-444455556666",
  "status": "approved",
  "requested_by": "9a000000-0000-4000-8000-000000000002",
  "requested_permissions": ["channels:addresses:send"],
  "message": "Need to send order notifications",
  "connection_id": "aaaa1111-2222-4333-8444-555566667777",
  "decided_at": "2026-04-05T09:30:00Z",
  "decided_by": "9a000000-0000-4000-8000-000000000001",
  "created_at": "2026-04-05T09:00:00Z"
}
POST/api/v1/channels/configs/{config_id}/access/connection-requests/{request_id}/reject

Reject Connection Request

Reject a pending connection request with an optional reason.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
request_id*
string (UUID)Connection request ID

Request Body

NameTypeDescription
reason
string | nullOptional rejection reason
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/access/connection-requests/{request_id}/reject \
  -H "Authorization: Bearer {token}" \
  -H "Content-Type: application/json" \
  -d '{"reason": "Not needed"}'

Response

200 OK
{
  "id": "11112222-3333-4444-5555-666677778888",
  "principal_id": "9a000000-0000-4000-8000-000000000002",
  "target_service": "workflows",
  "target_resource": "org/c0ffee00-cafe-babe-dead-beefcafebabe/channel/8f2c1a00-1111-4222-8333-444455556666",
  "status": "rejected",
  "requested_by": "9a000000-0000-4000-8000-000000000002",
  "requested_permissions": ["channels:addresses:send"],
  "message": "Need to send order notifications",
  "connection_id": null,
  "decided_at": "2026-04-05T09:30:00Z",
  "decided_by": "9a000000-0000-4000-8000-000000000001",
  "created_at": "2026-04-05T09:00:00Z"
}
GET/api/v1/channels/configs/{config_id}/access/connections

List Inbound Connections

List active connections where another principal connects to this channel zone.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Response Fields

NameTypeDescription
[]*
InboundConnectionEntry[]id, principal_id, target_service, target_resource, created_at, plus optional principal_type, principal_label, label, created_by, created_by_label, requested_by, requested_by_label, system_managed_by
curl https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/access/connections \
  -H "Authorization: Bearer {token}"

Response

200 OK
[
  {
    "id": "aaaa1111-2222-4333-8444-555566667777",
    "principal_id": "9a000000-0000-4000-8000-000000000002",
    "principal_type": "automation",
    "principal_label": "Order notifier",
    "target_service": "workflows",
    "target_resource": "org/c0ffee00-cafe-babe-dead-beefcafebabe/channel/8f2c1a00-1111-4222-8333-444455556666",
    "label": "Order notifications",
    "created_by": "9a000000-0000-4000-8000-000000000001",
    "created_by_label": "Jane Doe",
    "requested_by": "9a000000-0000-4000-8000-000000000002",
    "requested_by_label": "Order notifier",
    "system_managed_by": null,
    "created_at": "2026-04-05T09:30:00Z"
  }
]
DELETE/api/v1/channels/configs/{config_id}/access/connections/{connection_id}

Revoke Inbound Connection

Sever an inbound connection to this channel, cutting the principal's runtime access.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID
connection_id*
string (UUID)Connection ID
curl -X DELETE https://platform.ergondata.ai/api/v1/channels/configs/{config_id}/access/connections/{connection_id} \
  -H "Authorization: Bearer {token}"

Response

204 No Content

Webhooks

Inbound provider callback. This endpoint is called by the email provider when it delivers an inbound message; it is not invoked directly by API consumers.

POST/api/v1/channels/webhooks/email

Receive Email

Provider webhook for inbound email. The email provider posts delivery and inbound-message events here; the body is provider-specific and verified by signature. Inbound messages route to the matching receive/both address and may emit channel events. IAM receive grants are not evaluated for provider delivery or routing; they govern subsequent message visibility.

Called by the email provider. Authenticated by provider webhook signature, not a bearer token.

Request Body

NameTypeDescription
<provider payload>*
objectProvider-specific event envelope (e.g. message, headers, recipients)
curl -X POST https://platform.ergondata.ai/api/v1/channels/webhooks/email \
  -H "Content-Type: application/json" \
  -H "svix-signature: {provider_signature}" \
  -d '{
    "type": "email.received",
    "data": {
      "from": "customer@example.com",
      "to": ["support@mail.example.com"],
      "subject": "Question about order #1042"
    }
  }'

Response

200 OK
{ "ok": true }
GET/api/v1/channels/webhooks/whatsapp/{webhook_token}

Verify WhatsApp Webhook

Meta's webhook subscription handshake. Meta calls this endpoint with hub.mode, hub.challenge, and hub.verify_token; when the verify token matches the Vault-stored secret for the config, the challenge is echoed back as plain text. Not invoked by API consumers.

Authenticated by the per-config webhook token in the path plus the Vault-stored verify token; no bearer token.

Path Parameters

NameTypeDescription
webhook_token*
stringPer-config opaque webhook token issued during WhatsApp setup

Query Parameters

NameTypeDescription
hub.mode*
stringMust be "subscribe"
hub.challenge*
stringRandom challenge Meta expects echoed back
hub.verify_token*
stringVerify token compared against the Vault-stored secret
curl "https://platform.ergondata.ai/api/v1/channels/webhooks/whatsapp/{webhook_token}?hub.mode=subscribe&hub.challenge=12345&hub.verify_token={verify_token}"

Response

200 OK
12345
POST/api/v1/channels/webhooks/whatsapp/{webhook_token}

Receive WhatsApp Webhook

Meta Cloud API callback for inbound messages, delivery statuses, account updates, and template status changes. The body is verified with the X-Hub-Signature-256 HMAC computed from the Vault-stored app secret. Inbound messages route to the matching WhatsApp address and drive episodic conversations. Not invoked by API consumers.

Authenticated by the per-config webhook token in the path plus the X-Hub-Signature-256 HMAC signature; no bearer token.

Path Parameters

NameTypeDescription
webhook_token*
stringPer-config opaque webhook token issued during WhatsApp setup

Request Body

NameTypeDescription
<Meta payload>*
objectMeta Cloud API event envelope (entry, changes, messages, statuses)
curl -X POST https://platform.ergondata.ai/api/v1/channels/webhooks/whatsapp/{webhook_token} \
  -H "Content-Type: application/json" \
  -H "X-Hub-Signature-256: sha256={signature}" \
  -d '{"object":"whatsapp_business_account","entry":[]}'

Response

200 OK
{ "ok": true }

Batch Access Grants

Public batch grant routes accept BatchCreateGrantsRequest and return ordered BatchCreateGrantsResponse partial-success envelopes. Operations expand as resources × permission_ids with a maximum of 200 expanded grants. HTTP 200 may include failed items; `already_exists` is an idempotent success, so whole-request or failed-item retries are safe. Side-effect errors do not roll back successful grants and require separate reconciliation.

POST/api/v1/channels/configs/access/grants/batch

Batch Create Channel Grants

Create grants across channel-config roots with independent per-item validation. Agent ToolDef slug: `channels.channel_access.create_grants_batch`.

Bearer token and company context required. IAM permission `channels:permissions:manage` on every concrete channel root.

Request Body

NameTypeDescription
operations*
BatchGrantOperation[]One or more grouped operations; maximum 200 expanded grants

Response Fields

NameTypeDescription
results*
BatchGrantResult[]Ordered results with index, client_ref, status, principal, permission_id, canonical resource, effect, grant, error_status/error_detail, and side_effect_error_status/side_effect_error_detail
summary*
objectcreated, already_exists, and failed counts
curl -X POST https://platform.ergondata.ai/api/v1/channels/configs/access/grants/batch \
  -H "Authorization: Bearer {token}" \
  -H "X-Company-Id: {company_id}" \
  -H "Content-Type: application/json" \
  -d '{"operations":[{"client_ref":"channel-access","principal_type":"member","principal_id":"{principal_id}","resources":["org/{company_id}/channel/{config_id}"],"permission_ids":["{permission_id}"],"effect":"allow"}]}'

Response

200 OK
{
  "results": [{
    "index": 0, "client_ref": "channel-access", "status": "created",
    "principal_type": "member", "principal_id": "{principal_id}",
    "permission_id": "{permission_id}", "resource": "org/{company_id}/channel/{config_id}",
    "effect": "allow", "grant": {}, "error_status": null, "error_detail": null,
    "side_effect_error_status": null, "side_effect_error_detail": null
  }],
  "summary": { "created": 1, "already_exists": 0, "failed": 0 }
}
POST/api/v1/channels/addresses/access/grants/batch

Batch Create Address Grants

Create grants across address roots. Address resources must end at the address root, and directional permissions must match the address's send/receive/both direction. Agent ToolDef slug: `channels.address_access.create_grants_batch`.

Bearer token and company context required. IAM permission `channels:permissions:manage` on every concrete address root.

Request Body

NameTypeDescription
operations*
BatchGrantOperation[]BatchCreateGrantsRequest operations (client_ref, principal_type/id, resources, permission_ids, effect); maximum 200 expanded grants

Response Fields

NameTypeDescription
results*
BatchGrantResult[]Ordered partial-success results with primary and side-effect error fields
summary*
objectcreated, already_exists, and failed counts
curl -X POST https://platform.ergondata.ai/api/v1/channels/addresses/access/grants/batch \
  -H "Authorization: Bearer {token}" \
  -H "X-Company-Id: {company_id}" \
  -H "Content-Type: application/json" \
  -d '{"operations":[{"principal_type":"agent","principal_id":"{principal_id}","resources":["org/{company_id}/channel/{config_id}/address/{address_id}"],"permission_ids":["{permission_id}"]}]}'

Response

200 OK
{
  "results": [{
    "index": 0, "client_ref": null, "status": "already_exists",
    "principal_type": "agent", "principal_id": "{principal_id}",
    "permission_id": "{permission_id}", "resource": "org/{company_id}/channel/{config_id}/address/{address_id}",
    "effect": "allow", "grant": {}, "error_status": null, "error_detail": null,
    "side_effect_error_status": null, "side_effect_error_detail": null
  }],
  "summary": { "created": 0, "already_exists": 1, "failed": 0 }
}