> For the complete documentation index, see [llms.txt](https://docs.concurrence.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.concurrence.com/developer-guide/platform-api/conversations/channel-manager.md).

# Channel Manager

Use Channel Manager with your own application. Find setup requirements, credentials, messaging and voice APIs, and the differences between channels.

Channel Manager connects your application to phone calls, SMS/MMS, email, iMessage, and WhatsApp. With the standalone API, your team configures and runs the agent. Channel Manager provides the provisioned senders, channel APIs, and delivery records.

Your application owns the conversation, consent decisions, scheduling, and business records. You can use Channel Manager without a Concurrence workspace, agent, service, or Developer Console. Start with [Integrate Channel Manager Through the API](/developer-guide/guides/integrate-channel-manager.md).

<figure><img src="https://1651797362-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F2YwM0LHtEGLt9S21OyC3%2Fuploads%2Fgit-blob-121a7d8d6e275a21144f9e8b39526027574b4ca9%2Fchannel-manager-ownership.svg?alt=media" alt="Before API integration, request a channel, complete the required approvals, and receive the provisioned setup from Concurrence. The customer application owns its agent, workflow, consent decisions, and records. Channel Manager connects it to the recipient through the approved channel."><figcaption><p>Required approvals and Concurrence provisioning come before API integration. Test the integration before launch. The boxes show responsibilities, not deployment locations.</p></figcaption></figure>

If you use a Concurrence workspace service, follow the separate [service-binding path](#service-binding).

Webchat is separate from the standalone Channel Manager API. Your application can provide its own webchat; to embed a Concurrence Platform agent, see [Serve an Agent From a Web App](/developer-guide/platform-api/conversations/serve-agent-from-web-app.md).

## Supported Channels

Choose a channel for the intended workflow, then confirm that its sender and use case are approved. Access to an API does not establish permission to run a particular outreach program.

| Channel                | Before the first test                                                         | What differs                                                                                                      |
| ---------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| SMS/MMS                | Assigned number, required sender registration, and recipient consent          | SMS replies do not identify a specific outbound message. MMS also needs verified content access.                  |
| Email                  | Verified sending domain, sender, reply routing, and unsubscribe configuration | Replies can carry a parent message reference; bodies and attachments need authenticated retrieval.                |
| iMessage               | Assigned sending lines and an approved recipient                              | Delivery may fall back to SMS. Check the reported service and downgrade result.                                   |
| WhatsApp               | Provisioned business number and a recipient who has contacted the use case    | The current send API accepts free-form replies within 23 hours of an inbound message. It does not send templates. |
| Inbound/outbound voice | Assigned number, voice event receiver, and a compatible audio endpoint        | REST controls the call and participants. Audio uses a separate connection to your runtime.                        |

## Provision a Channel

Request the channel through your Concurrence implementation contact. Describe the intended sender, recipients, purpose, and sample content. Your contact will confirm which sender registrations and channel-provider approvals are required.

Before using the route, get confirmation that the required approvals are complete and the sender and use case are ready. Submitting a request or obtaining API credentials does not approve the channel or provision it automatically.

Ask your Concurrence implementation contact for the setup ID, use-case ID, assigned sender, and setup-scoped client credentials. A **setup** is the access boundary for channel resources. A **use case** identifies the provisioned purpose, channel, and routing configuration within it.

For messaging, provide an HTTPS receiver and obtain its signing secret. For voice, agree the call-event and audio connection contracts as well. Confirm the API host, processing region, retention, and support responsibilities before handling customer data. See [Program Readiness](https://docs.concurrence.com/channels/program-readiness).

## Standalone API

Use Channel Manager-issued IDs to identify resources in your application, API requests, and support records. Save message IDs alongside your own workflow ID; do not depend on provider identifiers.

The current public base URL is `https://channels.platform.amigo.ai`. Its [OpenAPI schema](https://channels.platform.amigo.ai/openapi.json) and [full interactive reference](https://channels.platform.amigo.ai/redoc) are separate from the workspace Platform API. Use the host assigned during onboarding. Using this public endpoint does not mean Channel Manager runs in your cloud account; that deployment requires its own agreement and acceptance.

Keep the host in configuration. Change it only when you receive an explicit API migration notice; a website or email-domain change does not change API URLs or callback destinations.

### Authenticate

Exchange the provisioned client ID and secret for a bearer token. Tokens last one hour; there is no refresh token. Request the scopes needed for your channel and check the returned `scope`: ungranted scopes are omitted. Access is restricted by both operation scope and setup ID.

Keep client secrets on your server. Use separate credentials for runtime traffic and administration. Rotating a client secret prevents new token exchanges with that secret; issued tokens can remain valid until expiry.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/oauth/token" method="post" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

### Inspect Provisioned Use Cases

List the use cases in your assigned setup, then read the selected use case. Check its channel, sender configuration, and receiver URL against the provisioning handoff. For phone-based channels, use the detailed read to inspect assigned numbers; the collection response can leave `phone_numbers` empty.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/use-case" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/use-case/{use\_case\_id}" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

### Send and Reconcile SMS

Save the Channel Manager message `id` with your workflow ID. Use it for message and attachment reads. A successful send response means the provider accepted the message. Read the message again for later delivery or error information.

The send operation has no client-supplied idempotency key. After a timeout, reconcile the attempt before resending. When listing messages, supply `setup_id` and at least one of `use_case_id` or `peer_phone_number`.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/sms/" method="post" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/sms/{message\_id}" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/sms" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

MMS uses the same send operation with attachments. Test retrieval from your application before enabling inbound media.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/sms/{sms\_id}/attachment/{attachment\_name}" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

### SMS Recipient Opt-In and Opt-Out

Recipient controls use separate setup-scoped permissions: `sms:consent:read` to read opt-outs and `sms:consent:write` to send an opt-in message or clear a setup-wide opt-out. A workspace API key or an `sms:send` grant alone does not authorize these operations. Supply recipient phone numbers in E.164 format.

**Send the registered opt-in message.** Supply `use_case_id` and `to_phone_number`. Channel Manager selects the confirmation copy and sender from the use case's verified campaign or approved US/Canada toll-free registration. This is required before ordinary sends on the supported US long-code and US/Canada toll-free paths. Your application must already hold the appropriate consent evidence.

The operation returns `201` without a message resource ID. Once the recipient has an opt-in record for that use case, repeating it is a no-op. It returns `409` for an active setup-wide opt-out; sending an opt-in message cannot reverse a STOP. The record establishes acceptance of the confirmation send, not receipt or proof of consent.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/sms/opt-in" method="post" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

**Read opt-outs.** Supply `setup_id`, with optional `user_phone_number` to check one recipient. Results contain active setup-wide suppressions by default; `include_deleted=true` includes cleared records and their reversal reasons. Use the returned CM record and message IDs for correlation. An empty result is not proof of consent or of clearance on every sending line.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/sms/opt-out" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

**Clear a setup-wide opt-out.** An authorized caller supplies `setup_id`, `user_phone_number`, and a non-empty `reason` after reviewing renewed consent. Success returns `204` and retains the cleared record. The operation returns `404` if no active setup-wide opt-out exists, or `409` while a toll-free line under the setup still has an active opt-out for that recipient. The recipient must text `START` or `UNSTOP` to the affected toll-free line first.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/sms/remove-setup-opt-out" method="post" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

Recipients opt out through the supported [SMS keywords](https://docs.concurrence.com/channels/sms#opt-out-and-re-subscription). The current public API exposes opt-out reads and reversal; it does not expose an operation to create an opt-out directly.

### Send and Reconcile Email

Send inline HTML or use a stored template. For an unsubscribable use case, inline HTML must reference both `unsubscribe_link` and `unsubscribe_from_all_link`; Channel Manager supplies the URLs. For a non-unsubscribable use case, inline HTML must contain no template variables. Stored templates have their own declared parameters.

Save the returned Channel Manager email `id`. Use it for email reads and as `email_id` when reading events; the event query also requires `setup_id`. These reads expose delivery, bounce, and complaint information. Treat opens and clicks as tracking signals, not proof of an outcome.

When replying to an inbound email, supply its Channel Manager ID as `reply_to_inbound_email_id`. It must belong to the same setup and use case. The send path has no client-supplied idempotency key, so reconcile uncertain sends before retrying.

To correlate an inbound reply, read the email using the callback's `inbound_message_ref`. The returned `in_reply_to_email_id` identifies the outbound CM email it replies to, or is `null` for an unthreaded message. Use these CM IDs for threading.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/email/" method="post" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/email/{email\_id}" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/email/event" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

Inbound bodies and attachments need a separate read. Confirm that the configured content URLs are accessible from your deployment before relying on email replies.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/email/{email\_id}/body" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/email/{email\_id}/attachment/{filename}" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

### Send and Reconcile iMessage

Send text, one media file, or both. The service selects an available line from the use case's pool. Delivery can fall back to SMS; do not use this route when your program requires iMessage-only delivery. Read the message's `service`, `was_downgraded`, and delivery/error fields after sending.

Replies do not carry a parent outbound reference. Use your own session mapping, as described in the [receiver walkthrough](/developer-guide/guides/integrate-channel-manager.md#3-connect-your-inbound-receiver). Reconcile an uncertain send before retrying; this endpoint has no client-supplied idempotency key.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/imessage/" method="post" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/imessage/{message\_id}" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/imessage/{message\_id}/media" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

### Reply Through WhatsApp

Use the business-scoped recipient ID (BSUID) from the inbound event's `caller_ref`, not a phone number. The recipient must have sent a message to this use case within the last 23 hours. The current send operation supports free-form messages; the presence of a template-creation API does not provide template sending or cold outreach through this route.

Save the returned `message_id`, which is a Channel Manager-issued message ID. The current public schema has no WhatsApp message-read or delivery-event-list operation. Before launch, ask your channel operator how to investigate delivery and uncertain sends. The messaging receiver does not provide a delivery-event feed.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/whatsapp/" method="post" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

### Control Voice Calls

Voice separates call control from audio. Creating a call reserves a call reference and sender number; it does not dial anyone. Add a `media` participant to connect your runtime's `wss://` audio endpoint, and a `dial` participant to call the recipient. The audio endpoint must implement the provisioned media protocol; a generic agent WebSocket is not sufficient.

Participant creation returns before the person answers or joins. Read the call and participants to track those transitions. Keep `call_ref` and each `participant_ref` with your application's session ID. Ending a connected call requests teardown; read it again to confirm `ended_at`.

These APIs also support human participants and participant redirection. Design and test the handoff with your voice integration owner before using it with customers. Confirm recording, retention, and disclosure requirements during provisioning.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/voice-call/" method="post" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/voice-call/{call\_ref}/participant" method="post" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/voice-call/{call\_ref}" method="get" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/voice-call/{call\_ref}" method="delete" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

The participant media-stream operation below forks that participant's incoming audio in one direction. It is separate from the `media` participant used to connect the agent's audio.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/voice-call/{call\_ref}/participant/{participant\_ref}/media-stream" method="post" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

For inbound calls, have Concurrence configure the number's routing and your call-event receiver. The receiver starts or resumes your agent and attaches its media participant to the received call. Voice events are separate from messaging `inbound-turn` callbacks; the public schema does not currently define their payloads. Obtain and test the event contract during voice onboarding.

### Messaging Webhook Secrets

Messaging callbacks use a per-use-case signing secret. Explicit rotation returns the new secret once and an `old_secret_lasts_until` boundary. During the overlap, a callback can carry signatures from both secrets. Changing the receiver URL also rotates the secret, without the same overlap. Coordinate either change with the receiver owner.

See the [receiver walkthrough](/developer-guide/guides/integrate-channel-manager.md#3-connect-your-inbound-receiver) for signature checks, duplicate handling, and session mapping.

{% openapi src="<https://channels.platform.amigo.ai/openapi.json>" path="/v1/use-case/{use\_case\_id}/webhook-secret" method="post" %}
<https://channels.platform.amigo.ai/openapi.json>
{% endopenapi %}

## Service Binding

A service binding is needed when a Concurrence workspace service handles the conversation. It links the provisioned use case to that workspace and service. Rebinding can select another service in the same workspace; it cannot claim a use case already bound elsewhere.

A customer-owned application using the standalone API does not need this binding. See [Use Cases & Bindings](/developer-guide/platform-api/workspaces/use-cases.md#service-binding) for the workspace API operations.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.concurrence.com/developer-guide/platform-api/conversations/channel-manager.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
