Sockudo
Server

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:

ParameterPurpose
auth_keyApp key.
auth_timestampUnix timestamp used to reject stale requests.
auth_versionSigning protocol version.
body_md5MD5 of the JSON body for body-bearing requests.
auth_signatureHMAC 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}/revokeTokens

POST /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}/users

Use 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_first

History 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}/append

The 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}/status

Use 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:

StatusMeaning
202Async work accepted, usually push delivery.
400Invalid request shape or unsupported option.
401Signature or key failure.
403App or feature disabled.
404Resource does not exist.
413Payload too large.
429Rate limited.
5xxServer or backend dependency failure.

On this page