Overview
The Custom Digital Channel lets your platform deliver customer turns to Salted CX. Direction is defined from your point of view:
- Trigger (inbound, you → us) — a message the customer already sent in your widget. You push it to us.
- Action (outbound, us → you) — asking your platform to render a reply. This is a future capability and is not part of this guide.
Each trigger runs through the same conversation lifecycle as a native digital turn (translation, routing to an agent or bot).
Authentication
All requests use your per-account bearer token (the same token used for the YourLogic integration):
Authorization: Bearer <your-account-token>
Content-Type: application/jsonRequests without a valid token for the account in the path are rejected.
Flow
sequenceDiagram
autonumber
participant You as Your platform
participant CreateApi as Create-conversation endpoint
participant API as Triggers endpoint
You->>CreateApi: create conversation
CreateApi-->>You: conversationPid
You->>API: push triggers on conversationPid
API-->>You: per-trigger resultCreate the conversation once (you receive a conversationPid), then push one or more triggers to that conversation.
Create the conversation
Create the conversation first; the response returns a conversationId that you use as conversationPid when pushing triggers. The contact is created or matched automatically from the customer block — you do not make a separate contact call.
POST /api/v1/live/custom-channel/accounts/{accountId}/conversationsThe conversation is created as an inbound chat conversation. Request body:
{
"brandPid": "<brand-uuid>",
"customer": {
"displayName": "Jane Doe",
"contact": { "contact": "[email protected]", "contactType": "Anonymous" }
},
"languageCustomer": "en",
"url": "https://widget.example.com/chat",
"custom": null
}Request fields:
| Field | Type | Required | Description |
|---|---|---|---|
brandPid | uuid | yes | Your brand id |
customer | object | yes | Customer details (see below) |
languageCustomer | string | no | Customer language, e.g. en |
url | string | no | Last page the customer visited |
custom | object | no | Custom properties (any JSON), or null |
customer fields:
| Field | Type | Required | Description |
|---|---|---|---|
displayName | string | yes | Customer display name |
contact.contact | string | yes | A stable identifier for the customer (email, phone, or your own id) |
contact.contactType | string | yes | e.g. Anonymous, Email, Phone |
Response:
{ "conversationId": "11111111-2222-3333-4444-555555555555" }Push triggers
POST /api/v1/live/custom-channel/accounts/{accountId}/conversations/{conversationPid}/triggersThe body is an ordered array of triggers. v1 supports participantType = CUSTOMER and two trigger types: MESSAGE and ANSWER.
Common fields
| Field | Type | Required | Description |
|---|---|---|---|
type | string | yes | MESSAGE or ANSWER |
participantType | string | yes | CUSTOMER (only value in v1) |
externalId | string | yes | Your unique id for this trigger; used as the idempotency key |
MESSAGE — a customer text message
| Field | Type | Required | Description |
|---|---|---|---|
content | string | yes | The message text |
attachments | array | no | List of attachments (see below) |
Attachment object:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | yes | File name |
body | string | yes | Base64-encoded file content |
mimeType | string | no | e.g. image/jpeg |
ANSWER — a customer answering a question
| Field | Type | Required | Description |
|---|---|---|---|
answerId | string | yes | The id of the answer the customer chose |
responseToId | uuid | yes | The id of the question turn being answered |
Examples
Text message:
{
"triggers": [
{
"type": "MESSAGE",
"participantType": "CUSTOMER",
"externalId": "a1b2c3-0001",
"content": "Hello, I need help with my order"
}
]
}Message with an attachment:
{
"triggers": [
{
"type": "MESSAGE",
"participantType": "CUSTOMER",
"externalId": "a1b2c3-0002",
"content": "Here is the photo",
"attachments": [
{ "name": "receipt.jpg", "body": "<base64-bytes>", "mimeType": "image/jpeg" }
]
}
]
}Answer to a question:
{
"triggers": [
{
"type": "ANSWER",
"participantType": "CUSTOMER",
"externalId": "a1b2c3-0003",
"answerId": "yes",
"responseToId": "11111111-2222-3333-4444-555555555555"
}
]
}Response
The response reports a status per trigger, in the same order:
{
"conversationPid": "11111111-2222-3333-4444-555555555555",
"results": [
{ "index": 0, "type": "MESSAGE", "status": "CREATED", "message": "Created" }
]
}Per-trigger status:
| Status | Meaning |
|---|---|
CREATED | The trigger was applied and a turn was created |
DUPLICATE | A trigger with this externalId was already applied; nothing changed (idempotent) |
FAILED | The trigger could not be applied; processing of the batch stops here |
SKIPPED | Not attempted because an earlier trigger in the batch failed |
HTTP status codes:
| Code | When |
|---|---|
200 OK | All triggers applied (CREATED or DUPLICATE) |
207 Multi-Status | At least one trigger FAILED; inspect the per-trigger results |
400 Bad Request | A trigger is malformed (missing required field, unknown type/participantType). No trigger in the batch is applied. |
404 Not Found | The conversation does not exist |
Idempotency
Each trigger carries an externalId that you choose. Re-sending a trigger with the same externalId does not create a second turn — it returns DUPLICATE. Use stable ids so safe retries never duplicate messages.
Batch semantics
- Triggers are applied strictly in order.
- On the first failure, processing stops; remaining triggers are returned as
SKIPPEDand the HTTP status is207.
- Send a single trigger per request if you prefer to handle each independently.
participantType = CUSTOMER only; trigger types MESSAGE (with attachments) and ANSWER. Typing indicators and outbound delivery are not yet available.