Deadlines

A deadline is a dated obligation your agent has committed to — "renew the certificate by March 14", "file the report before the quarter closes". It is durable memory for the thing agents forget most: a future commitment made in one session that a later session has to honour. You hand us a due_at and a short summary, and we hold onto it, escalating through reminders as the date approaches and passes, until you mark it done.

A deadline looks superficially like a schedule, but they answer different questions. A schedule fires once at its moment and forgets; a deadline tracks whether the work got done and escalates if it doesn't. Reach for a deadline when any of these hold: you want reminders before and after the due date, not a single callback; some recipients are humans (reminder emails, not just a webhook); or the thing has a completion state you would want to mark "done". If you would never mark it done, it's a schedule.

How deadlines behave

Every deadline carries a due_at and a required summary — the human-readable label every dashboard row and reminder is keyed on. (A webhook_url is optional and can't stand in for the summary.) Around the due date, an escalation policy drives a series of reminders; see the next section.

A deadline's status is stored, not derived on read:

Status Meaning
open Live and not yet past due.
overdue Past its due_at and still not done — a periodic scan flips open rows to overdue.
completed You marked the obligation done (terminal).
cancelled You called it off (terminal).

open and overdue together are the active set — the deadlines still on your plate, and the set the stock limit counts (see Limits, below).

Reminders go to destinations that live on the deadline itself, not inside the policy: an optional webhook_url (same shape as a schedule's) and notify_emails, a list of zero or more human recipients. A deadline may also carry an optional cron recurrence + IANA recurrence_tz; completing a recurring deadline spawns its successor (see below).

Escalation policies

An escalation policy is a small JSON document listing the reminder steps, each an offset relative to due_at and the channels it fires on. Offsets are signed ISO-8601 durations: negative is before due, P0D is the due date itself, positive is after. Channels are webhook, email, or both. One step may carry "repeat": "daily" to keep nagging once a day (usually the post-due step). This is the system default — warn at 30, 7, and 1 day out, on the due day, then daily once it lapses:

{
  "steps": [
    {"offset": "-P30D", "channels": ["webhook"]},
    {"offset": "-P7D",  "channels": ["webhook"]},
    {"offset": "-P1D",  "channels": ["webhook"]},
    {"offset": "P0D",   "channels": ["webhook"]},
    {"offset": "P1D",   "channels": ["webhook"], "repeat": "daily"}
  ]
}

When you create the deadline, the policy is materialised up front into one concrete reminder per (step × channel), anchored on the current due_at. Any step whose computed time is already in the past is skipped — a deadline you remember two days before it's due simply won't get its 30-day warning.

Two rules to internalise:

  • A referenced channel must have a destination. If any step lists webhook, webhook_url is required; if any lists email, notify_emails must be non-empty. A channel with nowhere to send is a create-time validation error, never a silently dropped reminder.
  • You can preview a policy before committing. POST /v1/deadlines/escalation-preview/ (and the live preview on the dashboard create form) returns the exact reminder fire-times for a due_at + policy. It's read-only: it creates nothing and costs no quota.

Remember a deadline

Every create is idempotent: pass an idempotency key and a retried create returns the original deadline instead of a duplicate. See Idempotency for choosing keys.

Go to Deadlines → New deadline. Enter the summary and due_at, pick the destinations (a webhook URL, one or more notification emails, or both), and edit the escalation policy — the form seeds the default policy and shows a live preview of every reminder it will schedule, so you can confirm the cadence before saving. Choose Recurring to attach a cron rule and timezone.

POST /v1/deadlines/ with your API key as a Bearer token. summary and a future due_at are required; escalation_policy, webhook_url, notify_emails, tags, metadata, and recurrence + recurrence_tz are optional (omit the policy to accept the system default). Pass an Idempotency-Key header to make retries safe.

curl https://aigears.example.com/v1/deadlines/ \
  -H "Authorization: Bearer aigears_live_..." \
  -H "Idempotency-Key: renew-cert-2026" \
  -H "Content-Type: application/json" \
  -d '{
    "summary": "Renew the TLS certificate",
    "due_at": "2026-03-14T00:00:00Z",
    "webhook_url": "https://hooks.example.com/aigears",
    "notify_emails": ["[email protected]"],
    "escalation_policy": {
      "steps": [
        {"offset": "-P7D", "channels": ["webhook", "email"]},
        {"offset": "P0D",  "channels": ["webhook", "email"]},
        {"offset": "P1D",  "channels": ["email"], "repeat": "daily"}
      ]
    }
  }'

Dry-run the policy first with POST /v1/deadlines/escalation-preview/ ({"due_at": ..., "escalation_policy": ...}) — it returns the reminder fire-times and creates nothing.

Ask the agent in plain language; it maps the phrasing onto a deadline_remember call.

"Remember to renew the TLS cert by March 14 — warn me a week ahead and again every day if it lapses."

deadline_remember(summary="Renew the TLS certificate", due_at="2026-03-14T00:00:00Z", notify_emails=["[email protected]"], escalation_policy={"steps": [{"offset": "-P7D", "channels": ["email"]}, {"offset": "P1D", "channels": ["email"], "repeat": "daily"}]})

deadline_remember takes the same fields as the HTTP body — summary, due_at, webhook_url, notify_emails, escalation_policy, tags, metadata, recurrence/recurrence_tz, and an optional idempotency_key. A policy naming a channel with no destination comes back as a tool error the agent can read and fix.

Query what's due

This is the payoff for agents that span sessions. A fresh agent instance, holding none of the context that created a deadline, can ask "what did past-me commit to?" and get back everything still active. That is the whole point of making deadlines durable: the commitment outlives the conversation that made it.

The Deadlines list shows one row per deadline (recurring ones collapse to a single series row) with its due date and status. Filter by status, by a due-date window, or by tags, or search the summary. Open a row for the detail page: the full reminder timeline in fire order, each reminder's delivery status, and the audit log.

GET /v1/deadlines/ lists the team's deadlines; narrow it with ?status=open. For the "what's on my plate" question use GET /v1/deadlines/query/, which returns only the active set and takes a forward ?window= (a positive ISO-8601 duration such as P30D, upper-bounding due_at) plus repeatable ?tags= (AND semantics). Already-overdue deadlines always come back, regardless of the window. GET /v1/deadlines/{id}/ returns one deadline.

# What have I committed to in the next 30 days (plus anything already overdue)?
curl "https://aigears.example.com/v1/deadlines/query/?window=P30D" \
  -H "Authorization: Bearer aigears_live_..."

deadline_query is the cross-session tool: it returns the active deadlines, optionally bounded by a window and filtered by tags. deadline_list lists everything (optionally by status); deadline_get(deadline_id=...) returns a single deadline.

"Before I start — what did past-me leave on my plate for this month?"

deadline_query(window="P30D")

Complete, snooze, or cancel

Three actions move a live deadline. All three act only on an active (open or overdue) deadline; calling any of them on one that is already completed or cancelled returns a 409 — there's nothing left to act on.

  • Complete — mark the obligation done. It's terminal, and it cancels any pending reminders. For a recurring deadline, completing the current occurrence advances the series: we spawn its successor at the next cron occurrence, sharing the rule, destinations, and policy.
  • Cancel — call the obligation off. Also terminal; for a recurring deadline, cancelling terminates the series — no successor is spawned. This is the only way to stop a recurring deadline; there is no separate delete-series action.
  • Snooze — push due_at forward to a new time. This returns an overdue deadline to open and rebuilds its reminders from the policy against the new date (past-due steps skipped, exactly as at create).

Open a deadline's detail page; Complete, Snooze, and Cancel controls are offered while it is active. Snooze prompts for the new due time.

POST /v1/deadlines/{id}/complete/ (optional resolution_note), POST /v1/deadlines/{id}/snooze/ ({"until": "..."}, a future time), and POST /v1/deadlines/{id}/cancel/. Each returns the updated deadline, or a 409 if it was already terminal.

curl -X POST https://aigears.example.com/v1/deadlines/01J9Z8XKQR.../complete/ \
  -H "Authorization: Bearer aigears_live_..."

deadline_complete(deadline_id=...), deadline_snooze(deadline_id=..., until=...), and deadline_cancel(deadline_id=...).

"I renewed the certificate — mark that deadline done."

deadline_complete(deadline_id="..."), which for a recurring deadline advances the series to the next occurrence.

Notifications you receive

Each reminder that fires POSTs a deadline.escalation event to your webhook_url (and/or sends a reminder email to notify_emails, for email steps). Inside the standard signed envelope, the webhook data identifies the deadline and the specific reminder:

{
  "event_type": "deadline.escalation",
  "data": {
    "deadline_id": "01J9Z8XKQR...",
    "notification_id": "01J9Z8ABCD...",
    "due_at": "2026-03-14T00:00:00Z",
    "fire_at": "2026-03-07T00:00:00Z",
    "tags": [],
    "metadata": {}
  }
}

One event fires per materialised reminder — a step listing two channels yields two notifications, one per channel. The envelope shape, the X-AIGears-Signature header, and the retry/dead-letter rules are the same for every primitive and are documented once in Webhooks & signing; for standing up a receiver and verifying the signature, see Receiving webhooks. Delivery is at-least-once, so make your handler idempotent on event_id.

Limits

The stock you're metered on is active deadlines — the count of open plus overdue deadlines at any moment. Completing or cancelling a deadline frees the slot; the reminders a deadline fires along the way are not separately metered, and reads and previews are never metered. The per-tier caps, and what happens when you reach one, live in Limits & tiers.