Echo — Omnichannel Communications
Status: Available · Spec: openapi/echo.yaml · Base URL: https://echo.dev.apex.reisiger.org
Echo sends and receives messages across WhatsApp, Messenger, Instagram, Telegram, SMS, and email through one API, with conversation threads, provider-approved templates, and conversational forms. Delivery is asynchronous end-to-end.
Auth & tenancy: Bearer token (machine-to-machine JWT or API key — your tenant's accepted strategy is configurable); tenant in the URL path, or as tenant_id in the /send body. Channels and sender identities (verified email domain, WhatsApp business number) are provisioned per tenant at onboarding — sending on an unconfigured channel is a 400.
Sending — the async contract
POST /send accepts {id?, channel, to, content, tenant_id, metadata?, category?, template_name?, template_variables?, locale?} and returns immediately:
{ "task_id": "…", "message_id": "…", "status": "queued", "routed_channel": "whatsapp" }
queued is not delivered. Terminal status arrives one of two ways:
- Poll
GET /messages/{tenant}/{message_id}untilSENT/DELIVERED/FAILED. A message that failed after all retries reportsFAILEDwith an error prefixedDEAD_LETTER:. - Webhook push — Echo delivers
echo.status_update(andecho.inbound_messagefor replies) to your configured endpoints, signed withX-Echo-Signature(HMAC-SHA256 of the body with your tenant's webhook secret). Verify, ack fast, handle idempotently (conventions).
Notable request semantics: supply your own id to make resubmission safe; email subject travels as metadata.subject; category (default utility) drives channel policy. Errors worth handling distinctly: 403 consent denied, 409 reply-quota exceeded (messaging-platform session windows), 422 no usable identifier for the requested channel.
Conversations, history, and media
GET /messages/{tenant}/conversations— threads;…/conversations/{contact_id}— one thread's messages.GET /messages/{tenant}— filterable message log (channel, direction, contact);…/stats— aggregates.- Inbound media: provider download URLs expire within minutes, so Echo captures media at receipt. Fetch it with
GET /messages/{tenant}/media/{channel}/{media_id}(binary, default retention 24 h) and delete with the correspondingDELETE(?purge_provider=truefor burn-after-acknowledge data minimisation).
Contacts, templates, forms
- Contacts — CRUD plus per-channel identifiers and consent state. Echo routes by contact when you address by contact id.
- Templates — managed message templates including the provider approval flow (WhatsApp templates must be approved by the provider before use; submission failures surface as
502with the provider's rejection). Template sends usetemplate_name+template_variableson/send. - Conversational forms — define a form once; Echo conducts it as a chat over any channel and returns structured submissions.
Dashboards & search
Read-only widgets (/widgets/delivery-metrics, /widgets/channel-health, /widgets/recent-activity) and /search support your operational dashboards without you re-aggregating the message log.
Envelope note
Most Echo routes return the standard {ok, data, error, meta} envelope; /send and /health return raw JSON — the spec marks the shape per operation.
Integration notes
- Rate limit is 120 requests/minute per credential (
429+Retry-After) — queue outbound campaigns on your side. - Treat webhooks as the primary status path and polling as the fallback; both are documented in the spec's
webhookssection. - Consent is enforced by Echo (
403), but collecting and recording it is your application's job.