# Proxy HOLD and resubmission

Use the correct approval contract for held requests and generated tool calls.

Updated: 2026-09-22

Canonical: https://docs.kastra.ai/proxy/approvals

## Two approval boundaries

A pre-request HOLD can stop the request before forwarding it to the model. A post-inference tool-call HOLD concerns a generated action after a provider response exists. Their replay rules are different; do not treat a retried model request as deterministic tool execution.

## Async pre-request HOLD

Send these headers on the original request:

```http
X-Kastra-Hold-Mode: async
X-Kastra-Operation-Id: one-stable-id-per-user-action
```

A matching supported HOLD returns HTTP `202`, `X-Policy-Decision: HOLD`, and a provider-shaped error with code `hold_pending`, checkpoint information, and resubmission guidance. Retain the checkpoint ID. The action has not been authorized by the fact that the server accepted the request.

Read checkpoint status using the original authorized credential. An authorized human resolves the checkpoint. Proceed only after its effective decision permits it; handle denial, expiry, cancellation, and abandonment explicitly.

## Resubmit the identical action

Keep the original request bytes, API key, environment, request path, effective principal/customer, canonical policy attributes, and operation ID unchanged. JSON reformatting can change the replay identity. Assign a new operation ID to each distinct user action, and reuse that ID only for its post-approval resubmission.

The current implementation binds approval replay to those request dimensions and consumes an approval once. This does not authenticate a caller-supplied principal; credential identity binding remains a separate concern. `Idempotency-Key` and `X-Kastra-Operation-Id` serve different purposes and are not substitutes.

## Waiting and post-inference holds

Without async mode, a supported request HOLD may wait within the server's bounded surface timeout. A wait timeout is not necessarily the checkpoint's final outcome. Inspect checkpoint status before retrying.

Post-inference tool-call holds authorize a specific generated call and use a separate replay identity. The upstream model can generate a different call on a retry; that call needs its own evaluation. Do not claim that async request headers make every output HOLD resumable under the same contract.

## Verify before rollout

Test two identical prompts from different users sharing an API key, two distinct actions from the same user, changed context on retry, and duplicate resubmission. Confirm that approval for one action cannot silently authorize another. These behaviors require a server version containing the current replay-identity implementation.

## Self-hosted execution journal profile

The assisted appliance workflow can enable a durable execution journal for bounded non-streaming requests, one pre-inference approval stage, timeout denial and no post-inference approval. In this profile, `X-Kastra-Operation-Id` must be a canonical UUID. Preserve it and the exact request bytes across the original pending review and its resume.

Inspect `X-Kastra-Execution-State` and the operation record. Current OWNER/ADMIN members can inspect `GET /api/v1/proxy-executions/{operationID}` and reconcile uncertain outcomes with `POST /api/v1/proxy-executions/{operationID}/reconcile`, supplying `outcome` and `evidence_reference`. The supported outcomes are `confirmed_executed` and `confirmed_not_executed`. Reconciliation records the operator's evidence; it does not authorize replay.

Terminal, uncertain, reconciled and restored operations never dispatch again under the same operation identity. `completed` means the server observed a complete successful upstream response and recorded the governed response; it does not attest delivery to the client. This profile is opt-in and self-hosted only, not a guarantee for ordinary SaaS proxy retries. [Appliance status and qualification](https://docs.kastra.ai/reference/self-hosted).
