HTTP API
Publish events, query channels, read history, mutate messages, manage annotations, and operate push notifications.
Sockudo's HTTP API is a trusted server interface. Every production request must be signed with app credentials. Use a server SDK unless you are writing a custom integration.
Signing model
Signed requests include:
| Parameter | Purpose |
|---|---|
auth_key | App key. |
auth_timestamp | Unix timestamp used to reject stale requests. |
auth_version | Signing protocol version. |
body_md5 | MD5 of the JSON body for body-bearing requests. |
auth_signature | HMAC SHA-256 signature over method, path, and sorted query. |
Publish an event
POST /apps/{app_id}/events
Content-Type: application/json{
"name": "order.created",
"channel": "orders",
"data": {
"id": "ord_123"
},
"idempotency_key": "order-created-ord_123"
}Use socket_id to suppress echo back to the originating connection.
When ai_transport and versioned messages are enabled for the target channel,
AI events and publishes with message_id return serial acknowledgements under
the channel entry. Retries with the same message_id or X-Idempotency-Key
return the original serials without publishing a duplicate event.
If the publishing node exits after durable persistence but before recording the
idempotency acknowledgement, a retry reconstructs and atomically repairs the
receipt from native history/version storage. Raw idempotency keys are not stored
in message envelopes; recovery uses an app/channel-scoped hash plus the canonical
payload fingerprint.
Idempotent publishes fail closed when their shared coordination backend is
unavailable: Sockudo returns 503 service_unavailable instead of publishing
without a proven claim. Concurrent retries whose first request is still active also
return 503 backpressure with Retry-After: 1. Retry either response with the same
idempotency key; a load balancer may route the retry to a healthy node.
{
"channels": {
"ai:session-1": {
"message_serial": "00000000000000000001:test:00000000000000000001",
"history_serial": 1,
"delivery_serial": 1,
"version_serial": "00000000000000000001:test:00000000000000000001"
}
}
}Batch publish
POST /apps/{app_id}/batch_events
Content-Type: application/json{
"batch": [
{
"channel": "orders",
"name": "order.created",
"data": { "id": "ord_123" }
},
{
"channel": "orders",
"name": "order.paid",
"data": { "id": "ord_123" }
}
]
}Batching reduces HTTP overhead, but do not use it to hide unbounded payloads. Keep per-event data compact.
When Cargo feature and runtime option ably-compat are enabled, the root REST
router also exposes Ably-compatible batch operations:
POST /messages
GET /presence?channels=channel-a,channel-b
POST /keys/{keyName}/revokeTokensPOST /messages accepts the raw object used by Rest.request() and the array of
batch specs used by batchPublish(). Results preserve spec and channel order.
The raw form returns 201 plus a flat channel result array on full success, or
400/40020 plus { error, batchResponse } when one or more channels fail.
The array form, batch presence, and token revocation return BatchResult envelopes
with exact successCount, failureCount, and ordered item results. Publish
success is reported only after the normal native publish pipeline commits; item
failures do not roll back unrelated successful items.
The compatibility batch edge runs no more than eight channel/target operations
at once and rejects requests exceeding 100 specs, 1,000 results, 10,000 publish
operations, or 10 MiB. Native event channel/message limits can lower those
ceilings. Bodies and responses negotiate JSON or MsgPack through the same REST
codec, and error responses include X-Ably-ErrorCode and
X-Ably-ErrorMessage.
Channel state
GET /apps/{app_id}/channels
GET /apps/{app_id}/channels/{channel_name}
GET /apps/{app_id}/channels/{channel_name}/usersUse state APIs for dashboards and admin tooling. Do not poll them as a substitute for realtime subscription events.
For channels matched by [ai_transport], GET /apps/{app_id}/channels/{channel_name}
also includes:
{
"ai": {
"active_streams": 1,
"last_history_serial": 42,
"message_count": 7
}
}History
GET /apps/{app_id}/channels/{channel_name}/history?limit=50&direction=newest_firstHistory responses use opaque cursors. Store and replay cursors as strings; do not parse them.
Use direction=oldest_first/forwards or direction=newest_first/backwards
with start, end, and the policy-capped limit to bound a stable page. A
cursor is opaque and must be replayed with the same query shape that produced
it. The Ably-compatible /channels/{channel}/messages projection additionally
returns credential-free first and optional next Link relations that preserve
that query shape.
Versioned messages
Protocol V2 mutable messages expose latest visible state and preserved versions:
GET /apps/{app_id}/channels/{channel_name}/messages/{message_serial}
GET /apps/{app_id}/channels/{channel_name}/messages/{message_serial}/versions
POST /apps/{app_id}/channels/{channel_name}/messages/{message_serial}/update
POST /apps/{app_id}/channels/{channel_name}/messages/{message_serial}/delete
POST /apps/{app_id}/channels/{channel_name}/messages/{message_serial}/appendThe optional Ably compatibility route is
PATCH /channels/{channel_name}/messages/{message_serial}. Its encoded message
body selects message.update, message.delete, or message.append through the
action field and carries operation metadata in version. It is a projection
over the same native mutation service and VersionStore; it does not maintain
a compatibility-only message chain. Request and response formats are negotiated
independently as JSON or MsgPack.
{
"data": {
"body": "edited message"
},
"description": "user edit",
"op_id": "edit-msg-1-v2"
}Mutation responses preserve the existing fields and include serials for SDK ordering and retries:
{
"channel": "ai:session-1",
"message_serial": "msg_123",
"action": "append",
"accepted": true,
"version_serial": "ver_456",
"history_serial": 12,
"delivery_serial": 34,
"status": "applied"
}Annotations
Annotations attach secondary state to a message without rewriting the message itself.
POST /apps/{app_id}/channels/{channel_name}/messages/{message_serial}/annotations
GET /apps/{app_id}/channels/{channel_name}/messages/{message_serial}/annotations
DELETE /apps/{app_id}/channels/{channel_name}/messages/{message_serial}/annotations/{annotation_serial}{
"type": "reactions:distinct.v1",
"name": "like",
"client_id": "user-1",
"count": 1
}Use annotations for reactions, read receipts, moderation signals, and summary projections.
With Cargo feature ably-compat, the compatibility router also exposes
GET|POST /channels/{channel}/messages/{messageSerial}/annotations. The POST
body is an Ably annotation array and supports both create and delete actions;
both operations call the same native annotation service shown above. Responses
negotiate JSON or MsgPack. List pages return credential-free relative Link
headers with an opaque app/channel/message-scoped cursor. Realtime action 21
is delivered only to an attachment that negotiated annotation_subscribe,
while message.summary uses the original message serial for ordinary
subscribers. annotation-publish, annotation-subscribe,
annotation-delete-own, and annotation-delete-any are evaluated independently
from message mutation permissions.
Push notifications
Push APIs are first-class HTTP APIs, not separate infrastructure. They cover device registration, channel subscriptions, credential management, publish admission, status inspection, scheduling, cancellation, and provider callbacks.
When the server is built with ably-compat, the root Ably REST projection also
provides /stats, /push/publish, /push/deviceRegistrations,
/push/channelSubscriptions, and /push/channels. These routes authenticate
with Ably-compatible credentials but call a bounded typed compatibility stats
store plus the native push domain services; they do not call Sockudo HTTP
handlers internally. Stats pages use opaque cursors and stable Link query
propagation. Direct push and extras.push enter the native durable publish log,
queue, planner, provider-dispatch, feedback, retry, status, scheduler, and
retention pipeline. The realtime provider delivers __ably_push__ through the
ordinary MessageService fanout path and records the actual outcome. External
providers continue through their configured native workers and fail truthfully
when the required feature or credentials are unavailable. Restricted Ably keys
must carry push-admin or channel-scoped push-subscribe; device-owned requests
also verify the stored hashed device identity token.
The same projection exposes GET /channels/{channel}/presence and
GET /channels/{channel}/presence/history. Current presence supports
clientId, connectionId, limit, and opaque cursor pagination. Presence
history additionally supports forward/backward direction and millisecond
start/end bounds. Both routes use the native presence service and return
credential-free first/next Link relations. History reads fail with Ably code
50003 when durable continuity is degraded or reset-required.
Register a device
POST /apps/{app_id}/push/deviceRegistrations
Content-Type: application/json{
"device_id": "ios-device-1",
"client_id": "user-42",
"platform": "apns",
"provider_token": "provider-token",
"metadata": {
"app_version": "4.5.0"
}
}Subscribe a device to a channel
POST /apps/{app_id}/push/channelSubscriptions
Content-Type: application/json{
"device_id": "ios-device-1",
"channel": "orders",
"client_id": "user-42"
}Publish push
POST /apps/{app_id}/push/publish
Content-Type: application/json{
"recipients": [
{ "type": "channel", "channel": "orders" }
],
"payload": {
"title": "Order updated",
"body": "Order ord_123 moved to packing",
"data": { "order_id": "ord_123" }
},
"sync": false,
"idempotency_key": "push-order-ord_123-packing"
}Async push returns 202 Accepted with a publish_id.
Inspect publish status
GET /apps/{app_id}/push/publish/{publish_id}/statusUse this endpoint for operator tools, not user-facing busy loops.
Complete endpoint reference
This page explains the common server workflows. The complete route matrix, including health probes, stats, metrics, all push credential/template/device/subscription endpoints, presence history state, durable history repair, and annotation filters, is in the HTTP endpoint reference.
Error handling
Treat HTTP status codes as authoritative:
| Status | Meaning |
|---|---|
202 | Async work accepted, usually push delivery. |
400 | Invalid request shape or unsupported option. |
401 | Signature or key failure. |
403 | App or feature disabled. |
404 | Resource does not exist. |
413 | Payload too large. |
429 | Rate limited. |
5xx | Server or backend dependency failure. |