HTTP API
REST endpoints for reading and writing Durable Objects from any language.
Every Durable Object operation is available over REST — useful for server-to-server calls, scripts, and languages without an SDK.
Base path:
/api/v1/objects/{app_id}/{type}/{key}{app_id} accepts either your App Key or your App ID. {type} and
{key} are arbitrary non-empty strings.
The pingerchips-js-server SDK wraps all of this — see the
Server SDK. Use the raw API only when you have no SDK.
Authentication
Every request carries two headers — see Authentication.
| Header | Value |
|---|---|
X-App-Key | App Key |
X-App-Secret | App Secret |
No request signing, no timestamp. The {app_id} path segment must be the App
Key or App ID of the authenticated app. Failure →
401 { "errors": [{ "status": "401", "title": "Unauthorized" }] }.
curl -X PUT https://queue.pingerchips.com/api/v1/objects/$APP_KEY/order/order-42/status \
-H "X-App-Key: $APP_KEY" \
-H "X-App-Secret: $APP_SECRET" \
-H "Content-Type: application/json" \
-d '{"value":"shipped"}'Endpoints
Read full state
GET /api/v1/objects/{app_id}/{type}/{key}{
"state": {
"status": "processing",
"assigned_to": "agent-7",
"retry_count": 2
},
"log_id": 17
}A never-written object returns { "state": {}, "log_id": 0 }.
Read one slot
GET /api/v1/objects/{app_id}/{type}/{key}/{slot}{ "value": "processing" }Returns { "value": null } if the slot is unset.
Set one slot
PUT /api/v1/objects/{app_id}/{type}/{key}/{slot}
Content-Type: application/json
{ "value": "shipped" }{ "log_id": 18 }Set multiple slots (atomic)
The body is a plain object — its keys are the slots. Broadcasts a single
batch event.
PATCH /api/v1/objects/{app_id}/{type}/{key}
Content-Type: application/json
{
"status": "shipped",
"shipped_at": 1718884800000
}{ "log_id": 19 }Increment
POST /api/v1/objects/{app_id}/{type}/{key}/increment
Content-Type: application/json
{ "slot": "retry_count", "by": 1 }by defaults to 1. An unset slot is treated as 0.
{ "log_id": 20, "value": 3 }Append
Appends one item to the list stored at slot (initialised to [] if unset).
The full updated list is stored.
POST /api/v1/objects/{app_id}/{type}/{key}/append
Content-Type: application/json
{
"slot": "history",
"value": { "event": "shipped", "at": 1718884800000 }
}{ "log_id": 21 }Delete a slot
DELETE /api/v1/objects/{app_id}/{type}/{key}/{slot}{ "log_id": 22 }Transaction
Atomic multi-write. The body is a bare JSON array of operations. All
operations commit together in one log entry and broadcast a single batch
event.
POST /api/v1/objects/{app_id}/{type}/{key}/transaction
Content-Type: application/json
[
{ "op": "set", "key": "status", "value": "shipped" },
{ "op": "increment", "key": "retry_count", "by": 1 },
{ "op": "append", "key": "history", "value": { "event": "shipped" } },
{ "op": "delete", "key": "temp_lock" }
]Valid op values: set, delete, increment, append. Each op names the
slot with key.
{ "log_id": 23 }The HTTP transaction is write-only — there is no get op, so it cannot
express "only write if the current value is X". For conditional writes, use the
Server SDK transaction(ops) under a read you performed
first, or gate the call in your own code.
Replay the log
GET /api/v1/objects/{app_id}/{type}/{key}/log?after=17Returns every log entry with log_id > after (default 0), oldest first.
increment and append are materialised as set entries.
{
"items": [
{ "op": "set", "key": "status", "value": "shipped", "log_id": 18 },
{ "op": "set", "key": "shipped_at", "value": 1718884800000, "log_id": 19 }
]
}Purge
Permanently deletes all state and log entries for the object.
DELETE /api/v1/objects/{app_id}/{type}/{key}204 No ContentError responses
| Status | Body | Cause |
|---|---|---|
401 | {"errors":[{"status":"401","title":"Unauthorized"}]} | Missing / wrong X-App-Key / X-App-Secret, or the route app_id isn't the authenticated app |
422 | {"error": "..."} | Operation failed (e.g. bad transaction op) |
Real-time
Every write broadcasts a change / batch event to subscribers of the
object's channel. Subscribe from the browser with
pingerchips.object(), or from another language over the
durable:{appKey}:{type}:{key} channel directly.