Zitadel Preview Docs

Complete authentication and create handoff token

Completes the authentication attempt and mints a `handoff_token`. Call this after all required factors have been verified and the attempt is in `completed` state. The handoff token is short-lived (≤60 seconds) and must be exchanged at POST /sessions/exchange to receive the final session and session_token. The handoff token is: - Single-use (atomic exchange, no retry) - Audience-bound (requires matching project key for exchange) - Idempotency-safe within a 5-minute window (see conventions)

POST
/auth_attempts/{attempt_id}/handoff

Completes the authentication attempt and mints a handoff_token.

Call this after all required factors have been verified and the attempt is in completed state. The handoff token is short-lived (≤60 seconds) and must be exchanged at POST /sessions/exchange to receive the final session and session_token.

The handoff token is:

  • Single-use (atomic exchange, no retry)
  • Audience-bound (requires matching project key for exchange)
  • Idempotency-safe within a 5-minute window (see conventions)

Authorization

oauth2 auth_attempts.write
AuthorizationBearer <token>

In: header

Scope: auth_attempts.write

Path Parameters

attempt_id*string

The unique identifier of the authentication attempt

Header Parameters

Idempotency-Key?string

Optional retry key for safe retries of the handoff creation (Category B idempotency). Retries carrying the same key within the exchange window return the same handoff token without re-completing the attempt or minting a new token.

Response Body

application/json

application/json

curl -X POST "https://example.com/auth_attempts/abc123def456/handoff"
{  "handoff_token": "handoff_abc123def456xyz",  "expires_at": "2026-04-28T15:31:00Z"}

{  "code": "att.not_found",  "message": "The auth attempt was not found."}