Voice-to-voice
Architecture
The integration consists of an HTTP setup phase followed by a real-time WebSocket phase.
sequenceDiagram
participant Caller
participant Seamly
participant Client
Caller->>Seamly: calls
Seamly->>Client: POST /api/conversations
Client-->>Seamly: 201 + WebSocket URL
Seamly->>Client: open WebSocket
Client->>Seamly: { "type": "audio", ... }
Seamly->>Caller: plays audio fragment
Caller->>Seamly: speaks
Seamly->>Client: { "type": "audio", ... }
Caller->>Seamly: hangs up
Seamly->>Client: { "type": "hangup" }
Integration flow
Call start
When an incoming call is routed to your integration, Seamly sends an authenticated request to your conversation endpoint before the conversation starts.
POST /api/conversations
Content-Type: application/json
X-Seamly-Signature: <HMAC signature>
Request body
{
"project_id": "c8dc0d63-19be-479e-b89c-c99276b7e023",
"channel_name": "voice",
"phone_number": "+31881111111",
"environment": {
"from_phone_anonymous": false,
"from_phone_number": "+31612345678"
}
}
| Property | Type | Required | Description |
|---|---|---|---|
| project_id | string | Yes | UUID of the Seamly project. Use this to identify the relevant tenant or configuration in your system. |
| channel_name | string | Yes |
Name of the Seamly channel through which the call arrived. This is normally
voice
.
|
| phone_number | string | Yes | Dialled number in international E.164 format. |
| environment | object | Yes | Information about the caller and call environment. Additional properties may be added in the future. |
| environment.from_phone_anonymous | boolean | Yes | Whether the caller withheld their telephone number. |
| environment.from_phone_number | string or null | Yes |
Caller's telephone number in E.164 format, or
null
when unavailable.
|
The request schema permits additional properties at the top level and inside
environment
. Your implementation should ignore unknown properties rather than reject the request.
Successful response
Return HTTP
201 Created
with a JSON body that configures the conversation.
{
"type": "audio",
"_links": {
"socket": {
"href": "wss://api.acme.com/conversations/client-conversation-7f31a4/websocket"
}
}
}
Response properties
| Property | Type | Required | Description |
|---|---|---|---|
| type | string | Yes | Either audio or text. For a text integration see our Text-to-voice . |
| _links.socket.href | URI | Yes | Secure WebSocket URL Seamly must connect to for this conversation. |
The response schema does not allow unknown top-level properties or unknown properties inside the defined nested objects.
Error responses
The specification defines the following error responses:
-
401 Unauthorizedwhen request authentication fails. -
500 Internal Server Errorwhen your application cannot create or configure the conversation.
Return errors in this format:
{
"error": "Human-readable error message"
}
Accept the WebSocket connection
After validating the HTTP response, Seamly connects to the URL in
_links.socket.href
using a secure WebSocket connection. Just as with the initial POST request an
X-Seamly-Signature
header is sent.
All messages are JSON objects with a
type
property.
Exchange audio messages
As soon as the WebSocket is established, both parties may start to send Audio messages.
{
"type": "audio",
"payload": {
"variables": {
"audio": "...... Base64 encoded 8 KHz 16bit mono PCM .....",
"id": "99ee8529-4edd-455d-a1ca-b1466048d67f"
}
}
}
End a call
Client-initiated hang-up
To instruct Seamly to end the call, send:
{
"type": "hangup",
"payload": {
"direction": "inbound",
"sipHeaders": {}
}
}
In a bit Seamly will close the WebSocket.
Call transfer
Transferring a call is not available yet. Do not use it until Seamly confirms support.
Caller-initiated hang-up
When the caller has ended the call by hanging up the telephone, Seamly sends:
{
"type": "hangup",
"payload": {
"direction": "outbound"
}
}
Report errors
Either side may send an
error
event:
{
"type": "error",
"payload": {
"reason": "validation_error",
"description": "Unexpected field in message",
"sipStatusCode": 486
}
}
| Property | Description |
|---|---|
| reason | Stable, machine-readable, atom-like error reason. |
| description | Human-readable explanation suitable for diagnostics. |
| sipStatusCode | SIP response status code associated with the error, when available |
Error handling
Seamly reports WebSocket protocol errors to your application. Seamly will not reconnect in case of an unexpected WebSocket connection failure. In this case the call ends without an error message being spoken to the caller.
Conversation setup failures
Your HTTP endpoint must return
201 Created
with a valid conversation configuration. HTTP failures, invalid JSON or response fields prevent Seamly from starting the conversation. Your WebSocket URL must use
wss://
.
Seamly cannot currently report these setup failures back to you through the Voice API. Setup also fails if Seamly cannot establish the initial WebSocket connection. The reconnection behaviour below applies only after a WebSocket connection has been established.
WebSocket protocol errors
If your application sends a JSON message that fails validation or violates the expected message sequence, Seamly responds with an
error
message.
Seamly only logs
error
messages sent by your application. Sending an
error
does not trigger recovery, end the call or produce speech for the caller. To explain a failure to the caller while the connection is available, send audio using the normal messages. You can then send
hangup
to end the call.
WebSocket event reference
Events sent by Seamly to your application
| Event | Purpose |
|---|---|
| audio | Audio of the caller or of the Client. |
| play_done | Stop the playback of the current Audio. In case the passed ID matches the current Audio the playback is stopped and a PlayDone message is returned. When the ID does not match an Error message is returned. |
| dtmf_key_press | A DTMF key was pressed |
| hangup | Hangup of the caller or of the Client. |
| dial_succeeded | The outbound call was dialled successfully |
| error | Inform the other party an error has occurred. |
Events sent by your application to Seamly
| Event | Purpose |
|---|---|
| audio | Audio of the caller or of the Client. |
| kill_audio | Stop the playback of the current Audio. In case the passed ID matches the current Audio the playback is stopped and a PlayDone message is returned. When the ID does not match an Error message is returned. |
| hangup | Hangup of the caller or of the Client. |
| refer | Refer the call |
| dial | ial another participant into the call |
| error | Inform the other party an error has occurred. |
Security recommendations
-
Verify the
X-Seamly-Signaturebefore processing a conversation requests. - Compare signatures using a constant-time comparison function.
- Reject malformed JSON and invalid message sequences.
Specification files
This guide describes the behaviour defined by:
- OpenAPI — HTTP conversation creation and configuration.
- AsyncAPI — WebSocket connection for an audio-based integration.
Treat the machine-readable specifications as the authoritative schema definitions. This guide explains how to combine them into a working integration and highlights operational details that must be agreed with Seamly during onboarding.f