Client service reference

Build with Thodari

Use tenant-scoped data, scheduling, workflow, and messaging services from your application. Administration and platform operations are intentionally excluded from this reference.

✦Tenant isolation is automatic. Every resource you create, read, or change belongs to the tenant assigned to your client integration.

What you can build

Choose the service that fits the work your application needs to perform.

Key-value data

Store strings and structured collections with TTLs and atomic counters.

Scheduled delivery

Deliver public HTTP webhooks now, later, or on a recurring schedule.

Durable workflows

Track idempotent steps, sleeps, and external signals.

Topics and events

Publish messages to webhooks, SSE consumers, and WebSocket subscribers.

Conventions

iAuthenticate API requests with an API key. Send X-API-Key: <key> or Authorization: Bearer <key> on every /v1/* request. Browser console sessions use an HttpOnly cookie and are not a substitute for an integration API key.

Successful responses wrap their payload in result. Paginated list responses include total, page, limit, and pages alongside the endpoint's list field. Errors use {"detail":"..."}.

All routes are relative to your Thodari host, for example https://your-host/v1/…. Request and response bodies use JSON unless an endpoint specifies otherwise.

Verify an integration key and discover its tenant context with GET /v1/admin/me. Key issuance, reset, revocation, and audit records are administrator operations available in the Console.

iUse only resource IDs returned for your own client integration. Cross-tenant targeting and platform administration are not client API operations.

Key-value store

Store application state, cache entries, counters, and compact collections.

POST/v1/kv/setStore a value with optional expiration.
Field Purpose
key Key to store.
value Value to store.
ex / px Optional expiration in seconds or milliseconds.
nx / xx Optional create-only or update-only condition.
POST /v1/kv/set {"key":"cart:42","value":"open","ex":3600}
GET/v1/kv/get/{key}Read a value.

Returns the tenant-scoped value or null when it is absent or expired.

POST/v1/kv/incrAtomically change an integer counter.
POST /v1/kv/incr {"key":"orders:processed","delta":1}
POST/v1/kv/del · /mget · /msetDelete or process values in batches.
POST /v1/kv/del {"keys":["cart:42","cart:43"]}
POST /v1/kv/mget {"keys":["cart:42","cart:43"]}
POST /v1/kv/mset {"items":{"cart:42":"open"},"ex":3600}
GET/v1/kv/keys · /scan · /ttl · /type · /existsDiscover and inspect stored keys.

/scan supports cursor-based paging. TTL returns -1 for persistent keys and -2 for missing keys.

POST/v1/kv/hash/* · /list/* · /sets/* · /zset/*Work with hashes and collections.
POST /v1/kv/hash/hset {"key":"profile:42","field":"name","value":"Ada"}
GET  /v1/kv/hash/hget/{key}/{field} · /hash/hgetall/{key}
POST /v1/kv/hash/hdel {"key":"profile:42","fields":["name"]}

POST /v1/kv/list/push {"key":"jobs","values":["a"],"direction":"left"}
POST /v1/kv/list/pop {"key":"jobs","direction":"left"}
GET  /v1/kv/list/range/{key}?start=0&stop=-1

POST /v1/kv/sets/sadd {"key":"roles","members":["editor"]}
POST /v1/kv/sets/srem {"key":"roles","members":["editor"]}
GET  /v1/kv/sets/smembers/{key}

POST /v1/kv/zset/zadd {"key":"scores","score":10,"member":"ada"}
POST /v1/kv/zset/zrem {"key":"scores","member":"ada"}
GET  /v1/kv/zset/zrange/{key}?start=0&stop=-1&withscores=true
GET  /v1/kv/zset/zscore/{key}/{member}

Scheduling

Schedule outbound delivery to public HTTP(S) URLs. Deliveries retry with backoff and appear in your dead-letter queue if they cannot be delivered.

POST/v1/publish · /v1/publish/{destination_url}Create an immediate or scheduled delivery.
Field Purpose
url Public HTTP(S) destination.
delay Optional delay in seconds.
scheduled_at Optional ISO 8601 date and time.
cron Optional five-field cron expression.
timezone IANA time zone for cron or scheduled dates.
body Optional request body.
POST /v1/publish {"url":"https://example.com/hooks/order","delay":300,"retries":3}
GET / PUT/v1/schedules · /v1/schedules/{job_id}List, inspect, or update your deliveries.

Use optional page and limit parameters for paged results. Send PUT /v1/schedules/{job_id} to update a job.

POST/v1/schedules/{job_id}/pause · /resume · /triggerPause, resume, or trigger a delivery.

These actions apply only to jobs owned by your tenant.

DELETE/v1/schedules/{job_id}Cancel a delivery.

Cancellation permanently removes the scheduled job.

GET / POST/v1/dlq · /v1/dlq/{job_id}/resendInspect or resend failed deliveries.

Use resend after correcting a destination or temporary downstream issue.

Durable workflows

Create long-running executions and record idempotent progress as your application completes work.

POST/v1/workflows/submitStart an execution.
POST /v1/workflows/submit {"name":"fulfil-order","url":"https://example.com/workflows/fulfil","initial_data":{"order_id":"ORD-123"}}
GET/v1/workflows · /v1/workflows/{execution_id}List or inspect your executions.

An execution response includes its current state and recorded steps.

POST/v1/workflows/{execution_id}/step · /parallel · /resetRecord or replay workflow steps.

Step names are idempotent within an execution: resubmitting the same step returns its recorded outcome.

POST/v1/workflows/{execution_id}/signalResume an execution waiting for an external event.

Wait steps can specify signal_timeout_seconds. If a signal does not arrive before the deadline, the execution fails.

POST / DELETE/v1/workflows/{execution_id}/cancel · /v1/workflows/{execution_id}Cancel or remove an execution.

Use cancellation to halt work; deletion removes the durable execution record.

Messaging

Publish tenant-scoped topic events to webhook subscribers and real-time consumers.

POST/v1/topics · /v1/topics/{topic}/publishCreate a topic or publish an event.
POST /v1/topics/orders/publish {"payload":{"order_id":"ORD-123","status":"ready"}}
GET / DELETE/v1/topics · /v1/topics/{topic} · /historyList, delete, or inspect a topic.

Use GET /v1/topics to list topics, DELETE /v1/topics/{topic} to remove one, and GET /v1/topics/{topic}/history for message history. Topic names may be reused by other tenants without sharing data.

POST / GET / DELETE/v1/topics/{topic}/subscriptionsManage topic webhooks.

Add or list subscriptions for a topic. Remove one with DELETE /v1/topics/subscriptions/{sub_id}.

GET/v1/events/streamOpen a server-sent event stream.

The stream contains events visible to your tenant.

WS/v1/wsOpen a real-time topic connection.

Authenticate with an Authorization: Bearer <api-key> header (server clients) or a console session cookie (same-origin browser). Browser clients without a session must first create a one-time POST /v1/ws-ticket ticket and use ?ticket=....

{"action":"subscribe","topic":"orders"}
{"action":"unsubscribe","topic":"orders"}
GET / POST/v1/deliveries · /v1/deliveries/{delivery_id}/replayInspect or replay durable webhook deliveries.

List with optional status (pending, running, retry, delivered, or dlq), page, and limit. Replay requeues a delivery using its stable ID.

Health checks

These unauthenticated operational endpoints are for reverse proxies and monitoring.

GET/livez · /readyzCheck process liveness or traffic readiness.

/livez confirms that the process is running. Use /readyz for traffic admission: it verifies database access, the event bus, and required background health. It returns 503 when the instance should not receive traffic.

Errors and limits

Use conventional HTTP status handling and retry only when a response tells you to wait.

400Invalid request
401Not authenticated
403Not permitted
404Resource unavailable
429Rate limited

For 429, wait for the number of seconds in the Retry-After response header before retrying. A 413 response indicates that the request body exceeds the service limit.