Runs
Status: Current (Scale plan). A run is one unit of automation work — awarm_up or a post — that your own connected iPhone picks up and executes. The API enqueues and reads; the airlock keeps the API session off the device itself.
All paths are relative to <baseUrl>/v1 and require Authorization: Bearer. Reads need runs:read; writes need runs:write.
Endpoints
Enqueue a run — POST /v1/runs
Returns
202 with { "run": Run }.
Idempotency
Send anIdempotency-Key header (recommended for a post): a duplicate key returns the original run (200) instead of creating a second post. The key must be a UUID — any other format is rejected up front with 422 invalid_request.
Target app resolution
The platform a run executes against is derived from your org’s account roster at dispatch — the runner looks upaccount_username and resolves the target app from that account’s platform. TikTok and Instagram accounts always resolve; X accounts additionally need the operator to set the target app in Warmr’s Settings on the Mac before runs execute. The target app is deliberately not settable over /v1 — the platform comes from the roster, never from the API caller.
Bulk-enqueue — POST /v1/runs/batch
Stages N explicit clips as post runs for a single account, spreading them over time per cadence (each scheduled_at is folded in at enqueue — one clip becomes one run). Each item’s upload_id is its per-row idempotency key, so a replayed batch is a no-op; enqueued may be less than requested when some uploads were already staged. The per-account/day cap returns 413. The airlock is unchanged — this only enqueues.
Returns
202 (200 on an idempotent replay) with { "batch": BatchResult, "runs": Run[] }. BatchResult = { account_username, type, requested, enqueued, first_at, last_at }.
List runs — GET /v1/runs
Query: status (public status), account (exact account_username), limit (1–200, default 50). Returns { "runs": Run[] }, newest first, org-scoped by RLS.
Get a run — GET /v1/runs/{id}
Returns { "run": Run }, or 404 not_found.
Cancel a run — POST /v1/runs/{id}/cancel
Only a queued run can be canceled via the API. A claimed / running / terminal run returns 409 conflict — a device owns it, and the airlock blocks releasing it. Returns { "run_id", "status": "canceled", "result" }.
Retry a run — POST /v1/runs/{id}/retry
Requeue a failed or canceled run. Body: { "reschedule_now": boolean } (optional). Returns { "run_id", "status": "queued", "result" }. If the post’s clip already went out, returns 409 source_consumed; if the run is not in a requeueable state, 409 conflict.
The Run object
Status mapping: internally
done is surfaced as succeeded; every other state (queued / claimed / running / failed / canceled) passes through unchanged.Next
- Schedules — standing recurring cadences instead of one-off runs.
- Webhooks — get notified on
run.completed/run.failed. - SDK quickstart — the posting flow that produces an
upload_id.