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 and invoke the effective auto-reply agent; Automations may independently fan out from the same events. 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"Email setup model. Vendor selection is an internal implementation detail.
name*
stringDisplay name
domain*
string | nullConfigured domain
status*
stringLifecycle status (e.g. pending_dns, verifying, verified)
provider_ref*
object | nullTransport metadata such as DNS records or the Platform Domain mail namespace. Account-level webhook configuration is not tenant-managed.
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",
  "provider_ref": {
    "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",
    "provider_ref": { "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",
  "provider_ref": {
    "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",
  "provider_ref": { "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",
  "provider_ref": { "dns_records": [] },
  "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": "[email protected]",
    "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 address under a config. For email, the address must use the config domain. Receive/both addresses require inbound to be available and may enable inbound on the config.

Bearer token required.

Path Parameters

NameTypeDescription
config_id*
string (UUID)Channel config ID

Request Body

NameTypeDescription
address*
stringFull address, e.g. [email protected] (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": "[email protected]",
    "display_name": "Support",
    "direction": "send"
  }'

Response

201 Created
{
  "id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
  "channel_config_id": "8f2c1a00-1111-4222-8333-444455556666",
  "address": "[email protected]",
  "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": "[email protected]",
    "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"
  }
]
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": "[email protected]",
  "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: 7c1f4e0a-2b3c-4d5e-8f90-112233445566" \
  -d '{
    "channel": "email",
    "address_id": "a1d2e3f4-5566-7788-99aa-bbccddeeff00",
    "config": {
      "to": ["[email protected]"],
      "subject": "Your order has shipped",
      "html": "<p>Thanks for your purchase!</p>",
      "reply_to": "[email protected]",
      "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": "<[email protected]>",
  "thread_id": "d4e5f6a7-b8c9-4012-d345-6789abcdef01",
  "idempotent_replay": false
}

Activity

Inspect the channel type registry, paginated message activity (company-wide or per channel), single events, 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 the channels the caller may audit. Inbound message rows are additionally filtered by channels:addresses:receive on their destination address. That permission controls visibility only; it does not affect provider delivery or routing.

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 <[email protected]>",
      "to_addresses": ["[email protected]"],
      "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. Reading an inbound message event also requires channels:addresses:receive on its destination address.

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 <[email protected]>",
  "to_addresses": ["[email protected]"],
  "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": "<[email protected]>",
  "created_at": "2026-04-06T15:22:11Z"
}
GET/api/v1/channels/configs/{config_id}/activity

List Channel Activity

Paginated activity log scoped to a single channel config. Inbound message rows are visible only when the caller has channels:addresses:receive on their destination address.

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": "[email protected]",
      "to_addresses": ["[email protected]"],
      "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/{event_id}

Get Channel Activity Event

Full detail for one activity row scoped to a channel config, including payload and provider identifiers. Reading an inbound message event also requires channels:addresses:receive on its destination address.

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 <[email protected]>",
  "to_addresses": ["[email protected]"],
  "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": "<[email protected]>",
  "created_at": "2026-04-06T15:22:11Z"
}
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": "[email protected]",
      "to_addresses": ["[email protected]"],
      "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 <[email protected]>",
      "to_addresses": ["[email protected]"],
      "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": "[email protected]",
    "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 authorizes viewing inbound messages at the address; it does not control delivery or routing.

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 to human participants or one automatic-reply agent. Receive permission controls inbound visibility and route-target eligibility, not delivery. Active user/team notify routes determine human participants; Conversations binds exactly one conversation per external thread.

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 Notify Route

Create a user/team notify route with channels:addresses:manage. The target must hold channels:addresses:receive. auto-reply creation through this address-owned path returns 403.

Bearer token required.

Request Body

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

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"Route target kind
target_id*
string (UUID)Canonical IAM principal ID
mode*
"notify" | "auto-reply"Route behavior
reply_address_id*
string (UUID) | nullAuto-reply sender; null for notify
status*
"active" | "paused"Route owner's desired state
address_status*
"allowed" | "blocked"Address owner's auto-reply gate
policy*
objectNotify uses {}; 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"Route target kind
target_id*
string (UUID)Canonical IAM principal ID
mode*
"notify" | "auto-reply"Route behavior
reply_address_id*
string (UUID) | nullAuto-reply sender; null for notify
status*
"active" | "paused"Route owner's desired state
address_status*
"allowed" | "blocked"Address owner's auto-reply gate
policy*
objectNotify uses {}; 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 notify route with channels:addresses:manage. For an agent-owned auto-reply route, the only accepted body is address_status; reconfiguration or pause/resume returns 403.

Bearer token required.

Request Body

NameTypeDescription
target_principal_type
"user" | "team" | "agent" | nullNotify target kind
target_id
string (UUID) | nullNotify target
mode
"notify" | "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"Route target kind
target_id*
string (UUID)Canonical IAM principal ID
mode*
"notify" | "auto-reply"Route behavior
reply_address_id*
string (UUID) | nullAuto-reply sender; null for notify
status*
"active" | "paused"Route owner's desired state
address_status*
"allowed" | "blocked"Address owner's auto-reply gate
policy*
objectNotify uses {}; 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 Notify Route

Delete a 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: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":"[email protected]","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":"[email protected]","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: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"Route target kind
target_id*
string (UUID)Canonical IAM principal ID
mode*
"notify" | "auto-reply"Route behavior
reply_address_id*
string (UUID) | nullAuto-reply sender; null for notify
status*
"active" | "paused"Route owner's desired state
address_status*
"allowed" | "blocked"Address owner's auto-reply gate
policy*
objectNotify uses {}; 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: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"Route target kind
target_id*
string (UUID)Canonical IAM principal ID
mode*
"notify" | "auto-reply"Route behavior
reply_address_id*
string (UUID) | nullAuto-reply sender; null for notify
status*
"active" | "paused"Route owner's desired state
address_status*
"allowed" | "blocked"Address owner's auto-reply gate
policy*
objectNotify uses {}; 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: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": "[email protected]",
      "to": ["[email protected]"],
      "subject": "Question about order #1042"
    }
  }'

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 }
}