ActionBoxDOCS

Expiration and Timeouts

Separate server-side deadlines from client waits and choose a safe expiration policy.

There are three different clocks in an Action workflow:

ClockControlled byWhat it means
expires_atActionBoxThe server-side deadline after which an open Action becomes terminal.
wait_seconds or an SDK/CLI timeoutYour clientHow long one read or local wait is allowed to block. It never chooses a decision.
Callback attempt timeoutCallback delivery and your receiverHow long a callback attempt may run before it is retried or marked failed.

Set a server-side deadline

Use a future RFC 3339 timestamp, preferably in UTC with an explicit Z:

{
  "title": "Approve the maintenance window",
  "expires_at": "2026-09-01T22:00:00Z",
  "options": [
    {"id": "ship", "label": "Ship it"},
    {"id": "hold", "label": "Hold"}
  ]
}

expires_at may be set when creating or updating an open Action. If it is omitted, the Action has no server expiration. When ActionBox processes a due Action, it transitions to expired; terminal state is immutable and cannot be reopened.

By default, expiration returns an explicit expired result with no invented answer. If the workflow has a known safe fallback, declare it with on_expire.resolve. The fallback must include an interaction-compatible typed response:

{
  "expires_at": "2026-09-01T22:00:00Z",
  "on_expire": {
    "type": "resolve",
    "response": {"type": "single_choice", "value": "hold"},
    "reason": "The maintenance window closed without approval."
  }
}

An expiration fallback follows the same atomic resolution path as a human or machine decision. It produces a resolved Action and a signed decision receipt with resolved_by: "system". A normal expiration produces no decision receipt.

Never guess a default answer

Use on_expire.resolve only when the fallback is explicitly safe for that workflow. Otherwise branch on expired and stop the operation or send it through a new review.

Wait without changing state

Machine clients can long-poll the Source-scoped read endpoint for up to 30 seconds:

curl -fsS "https://api.actionbox.cloud/v1/source/actions/$ACTION_ID?wait_seconds=30" \
  -H "Authorization: Bearer $ACTIONBOX_SOURCE_KEY"

The response is the current Action. If it is still open, the server wait ended without a terminal change; issue another bounded read when appropriate. The SDKs use this route and cap each server wait at 30 seconds, so a worker does not need a tight polling loop.

A local timeout has the same meaning as a read that returned open:

  • Python Action.wait(...) returns None while the Action remains open;
  • TypeScript client.wait(...) returns the current Action, which may still be open;
  • the CLI's local wait timeout does not resolve or cancel the server Action.

After a process restart, re-read by Action ID and branch on the authoritative state. Do not turn a socket timeout into rejection.

State transitions

open ── human or machine response ──> resolved
open ── owning Source cancellation ─> cancelled
open ── deadline, no fallback ──────> expired
open ── deadline, safe fallback ────> resolved (system)

Only open Actions transition. A competing decision, cancellation, or expiration receives a conflict rather than overwriting the first terminal event. See Errors and safe retries and Decisions and outcomes.

Next: Creating Actions, Idempotency and dedupe, or Webhooks and callbacks.

On this page