Taskbox

Docs

Step-by-step walkthroughs for working with Taskbox.

Consuming Taskbox callbacks

When a workspace member approves or rejects a task, Taskbox POSTs a JSON callback to the callback_url you supplied at task creation. This doc covers the wire shape your endpoint receives, how to verify the signature, and how to handle retries cleanly.

How delivery works

Each resolution produces a single delivery event with a stable event ID. Taskbox sends the request as POST with Content-Type: application/json. Your endpoint should respond as quickly as it can - a 2xx response confirms receipt; anything else is treated as a delivery failure and retried.

Headers your endpoint receives

POST /your/webhook HTTP/1.1
Content-Type: application/json
User-Agent: taskbox-callback/1
X-Event-Id: 0193c8e2-7e44-7b21-9f31-3a6b8d4e0a10
X-Signature: sha256=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08
  • X-Signature - HMAC-SHA256 of the raw request body, hex-encoded, prefixed with sha256=. Computed with the signing secret you received when the API key was created.
  • X-Event-Id - unique ID for this delivery event. Stable across retries of the same resolution; use it to dedup.
  • User-Agent - taskbox-callback/1.

Request body

{
  "version": "1",
  "event_id": "0193c8e2-7e44-7b21-9f31-3a6b8d4e0a10",
  "task_id": "0193c8e1-1aa4-7b21-9f31-2c6b8d4e0a10",
  "task_title": "Approve refund for order #4821",
  "status": "approved",
  "comment": "Within policy.",
  "resolved_by": "[email protected]",
  "resolved_at": "2026-05-03T14:42:11Z",
  "metadata": {"order_id": "4821", "amount_usd": 240}
}
version string Required

Wire contract version. Currently "1". Bumped if the shape ever changes.

event_id UUID string Required

Same value as the X-Event-Id header. Persist it on your side and ignore replays.

task_id UUID string Required

The task that was resolved. Distinct from event_id.

task_title string Required

Title at the time of resolution.

status approved | rejected Required

Terminal status the resolver chose.

resolved_by string (email) Required

Email of the workspace member who resolved the task.

resolved_at RFC3339 timestamp Required

When the resolution happened.

comment string Optional

Optional note left by the resolver. Omitted from the body when empty.

metadata JSON object Optional

The metadata object you supplied at task creation, echoed back verbatim. Omitted from the body when the original task had no metadata.

Verifying the signature

Always verify X-Signature before trusting the body. Compute HMAC-SHA256 of the raw request bytes (do not re-serialize the parsed JSON) using the signing secret for the API key that created the task, hex-encode it, and compare it - in constant time - to the value after sha256=.

// Pseudocode - works in any language with HMAC-SHA256.
raw            = read_request_body_as_bytes()
expected_hex   = hex(hmac_sha256(signing_secret, raw))
received       = request.header("X-Signature")           // "sha256=<hex>"
ok             = constant_time_equal(received, "sha256=" + expected_hex)
if !ok { return 401 }
event = json_parse(raw)

Reject any request whose signature does not match. If you rotate or revoke an API key, callbacks already in flight stay signed with the secret that was active when the task was created - keep old secrets verifiable for long enough to drain pending deliveries.

Idempotency

A delivery may be retried, so the same event_id can arrive more than once. Treat the first event_id you see as authoritative and discard later ones - typically by recording it in a deduplication table or cache before doing any side effects.

Retries and failures

A 2xx response ends delivery. Connection errors, timeouts, 408, 429, and 5xx responses trigger a retry; other non-2xx responses are considered permanent failures and stop further attempts. Deliveries that exhaust their retry budget surface on the in-app Monitoring page once you sign in.

Practical guidance: respond fast, do real work asynchronously, and make your handler safe to call repeatedly with the same event_id.

Next steps

Need to push tasks in the first place? See Create tasks via API.