Skip to main content

Claim, Heartbeat & Submit

Work an Agent task end to end: exclusive claims with execution epochs, lease renewal via heartbeat, and submitting outputs with value, confidence and reasoning.

Working a task is a three-step protocol, all with write scope: claim it for exclusive execution, heartbeat while you work to keep the lease alive, and submit the outputs. Claiming is genuinely exclusive — the claim runs under a row lock, flips the task to claimed, and increments its execution_epoch. A second claim while the first lease is live gets 409 ("Task is not available to claim"); a claim after timeout_at gets 409 ("Task has timed out").

The claim response is the full task payload and includes the incremented execution_epoch — keep it, because every subsequent heartbeat and submit must echo it. The epoch is what makes reclaim safe: if your agent dies and its lease (lease_expires_at) lapses, another agent may claim the same task, bumping the epoch. The crashed agent's late heartbeat or submit then fails 409 ("Stale or foreign task claim") instead of silently overwriting the new claimant's work.

POST /v1/agent-tasks/:id/heartbeat with {"execution_epoch": n} renews the lease from now, capped at the task's hard timeout_at. Heartbeat before the lease lapses — a heartbeat on an expired lease is a 409 ("Task lease expired"), and the correct recovery is to re-claim (reclaiming your own expired task is allowed and bumps the epoch again). If a Spec pins its stage to a specific agent credential, claim, heartbeat, and submit from any other credential fail 403 ("Task is bound to another agent credential").

POST /v1/agent-tasks/:id/submit carries {"execution_epoch": n, "outputs": {...}, "summary": "..."}. outputs is an object keyed by declared output-contract field: each entry is { value, confidence?, reasoning? }. Values are type-checked against the contract's dataType (a string field must get a string, a date field an ISO date string, and so on — 422 otherwise); confidence must be 0–1; reasoning is a string up to 8,000 characters that lands in the cell's audit trail. An undeclared key is a 422, and a required contract field missing or null is a 422. summary (up to 4,000 characters) is your one-line account of what you did — it surfaces in the run's Agentic Review panel.

One output key is reserved: __anchor_document_id. On a product-grain Agentic Review task whose autonomy contract grants anchorReview, submitting {"__anchor_document_id": {"value": "<document uuid>"}} proposes a different assembly anchor for the group; the platform applies it durably and recomposes the affected record. It is not a schema field — on a task without anchorReview it is rejected 403, and a value that is not a document uuid is a 422.

Product-grain tasks also enforce the Spec's autonomy contract server-side, from the contract frozen on the task at park time — never from client goodwill. In observe mode any value-bearing output is rejected 403 (flag findings in summary instead); in propose mode submitted values land as review holds a human confirms rather than canonical cells; and with a confidenceFloor set, an output below the floor (or with no stated confidence) is demoted to a flag and recorded, never written.

Submit is idempotent per claim: re-submitting with the same credential and the same execution_epoch after a network timeout returns 200 with the already-submitted task instead of double-writing. After a successful submit the pipeline resumes automatically at the next rail stage — there is nothing else to call.
POST/v1/agent-tasks/:id/claim
POST/v1/agent-tasks/:id/heartbeat
POST/v1/agent-tasks/:id/submit

Body fields

execution_epoch*integerThe epoch returned by your claim. A stale or foreign epoch is rejected 409.
outputs*objectKeyed by declared output-contract field: { value, confidence?, reasoning? } per field. Values are type-checked against the contract dataType; confidence must be 0–1; reasoning up to 8000 chars.
outputs.__anchor_document_idobjectReserved key (product-grain only, requires autonomy anchorReview): { value: "<document uuid>" } proposes a different assembly anchor for the group.
summarystringOptional one-line account of the work, up to 4000 chars. Shown in the Agentic Review panel.

curl — claim, then submit

curl -s -X POST https://api.talonic.com/v1/agent-tasks/0b6a2f5e-8c1d-4e7a-9f3b-2d4c6e8a0b1c/claim \
  -H "Authorization: Bearer tlnc_your_api_key"

curl -s -X POST https://api.talonic.com/v1/agent-tasks/0b6a2f5e-8c1d-4e7a-9f3b-2d4c6e8a0b1c/submit \
  -H "Authorization: Bearer tlnc_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "execution_epoch": 1,
    "outputs": {
      "policy_number": {
        "value": "POL-2026-0091",
        "confidence": 0.93,
        "reasoning": "Matched the Policennummer printed on page 1 against the carrier portal record."
      },
      "policy_holder": { "value": "Acme GmbH", "confidence": 0.88 }
    },
    "summary": "Filled policy_number and policy_holder from the cover page, verified in the portal."
  }'

Response (200) — submit

{
  "id": "0b6a2f5e-8c1d-4e7a-9f3b-2d4c6e8a0b1c",
  "customer_id": "7c9e6679-7425-40de-944b-e07fc1f90ae7",
  "pipeline_id": "1a0c681d-ea20-4bb4-8892-01a6d7f834da",
  "pipeline_document_id": "e2b8d4f6-1a3c-5e7f-9b1d-3f5a7c9e1b3d",
  "document_id": "c3d4e5f6-a7b8-9012-cdef-123456789012",
  "stage_id": "agent-1",
  "phase_index": 3,
  "status": "submitted",
  "execution_epoch": 1,
  "claimed_at": "2026-08-29T12:31:04.118Z",
  "lease_expires_at": "2026-08-29T12:46:04.118Z",
  "timeout_at": "2026-08-29T14:30:00.000Z",
  "submitted_at": "2026-08-29T12:33:41.512Z",
  "created_at": "2026-08-29T12:30:00.000Z",
  "updated_at": "2026-08-29T12:33:41.512Z"
}

Errors

Error responses

400VALIDATION_ERRORMaster-view credential, or an outputs body that is not an object keyed by field.
403INSUFFICIENT_PERMISSIONSThe task is bound to another agent credential or OAuth client; anchor re-election without the anchorReview grant; value outputs under autonomy mode "observe".
409RESOURCE_CONFLICTRetryable claim-protocol conflicts: task not available to claim, task timed out, lease expired, or a stale/foreign claim (wrong credential or execution_epoch).
422VALIDATION_ERRORAn undeclared output field, a missing required field, a value that fails the contract dataType, confidence outside 0–1, or reasoning over 8000 chars.

Frequently asked questions

How often should I heartbeat?+
Comfortably inside the lease: at half the lease interval you can miss one beat and still hold the claim. The lease length comes from the Spec's stage configuration (default 15 minutes) — read lease_expires_at from your claim response rather than hardcoding a period, and remember renewal never extends past the task's hard timeout_at.
What happens if my agent crashes mid-task?+
Nothing is lost. When the lease lapses the task becomes reclaimable — by you or another agent — and the claim bumps the execution_epoch, so the crashed run's late writes fail 409 instead of corrupting the new attempt. If nobody reclaims before timeout_at, the task times out and the document proceeds per its timeout_fallthrough.
Can I submit only some of the output fields?+
Yes, as long as every contract field marked required gets a non-null value. Optional fields may be omitted entirely, and submitting an explicit null value records the field as verified-empty. Fields you never mention are simply left to the rest of the pipeline.
Where do my submitted values and reasoning end up?+
Each output writes a versioned cell on the parked record (source mcp_agent for document tasks, agentic_review for product tasks) with your confidence, and an audit reference carrying the task id, epoch, and a note from your reasoning. Product-grain submissions additionally emit per-value audit events showing the old-to-new transition under the "Agentic Review" actor label.
Why did my submit succeed but the value never appeared in the output?+
Check the autonomy contract on the task snapshot. Under propose mode values land as pending review holds until a human confirms them, and under a confidenceFloor an output below the floor is demoted to a flag (listed in the task's result summary as demoted_below_floor) rather than written. Both are recorded outcomes, not silent drops.