Passports API
Passports are cryptographically signed JWTs that prove an agent's identity and authorization. They are the core trust primitive in STACK — any service can verify a passport without calling STACK's API by using the public JWKS endpoint. Passports are signed with EdDSA (Ed25519), have a default TTL of 15 minutes (hard max: 1 hour), and support delegation chains up to 4 hops deep.
All endpoints except POST /v1/passports/verify and the GET /v1/.well-known/jwks.json JWKS endpoint require an Authorization: Bearer sk_live_... header.
Issue Passport
POST /v1/passports/issue
Issue a new passport JWT for a registered agent. The passport embeds the agent's identity, service scopes, identity claims, and accountability settings in a stk namespace within the JWT payload.
Request Body
{
"agent_id": "agt_7kx9m2nq4p",
"ttl_seconds": 900,
"scopes": [
{
"service_connection_id": "svc_conn_abc123",
"scopes": ["read:messages", "write:messages"]
}
],
"identity_claim_ids": ["clm_9xm3kR7wL2"],
"intent": {
"summary": "Process and respond to customer support emails",
"services": ["slack", "gmail"],
"will_delegate": false,
"estimated_duration_seconds": 1800
},
"checkpoint_interval_seconds": 300
}- agent_id (string, required) — The agent ID to issue the passport for. Must be registered under your account.
- ttl_seconds (number, optional) — Time-to-live in seconds. Min: 60, max: 3600. Default: 900 (15 minutes).
- scopes (array, optional) — Array of {service_connection_id, scopes} objects. If omitted, uses all granted scopes.
- identity_claim_ids (string[], optional) — Specific identity claim IDs to embed. If omitted, all active claims are included.
- intent (object, optional) — Declaration of what the agent intends to do with this passport.
- intent.summary (string, required within intent) — Plain-text description of intended activity. Max 500 characters.
- intent.services (string[], required within intent) — Services the agent plans to use.
- intent.will_delegate (boolean, optional) — Whether the agent plans to delegate this passport.
- intent.estimated_duration_seconds (number, optional) — How long the agent expects to use the passport. Min 60, max 86400.
- checkpoint_interval_seconds (number, optional) — How often the agent should submit checkpoints. Min 60, max 3600.
Request Example
curl -X POST https://api.getstack.run/v1/passports/issue \
-H "Content-Type: application/json" \
-H "Authorization: Bearer sk_live_op_abc123" \
-d '{
"agent_id": "agt_7kx9m2nq4p",
"ttl_seconds": 600
}'Response — 201 Created
{
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
"jti": "ppt_9xm3kR7wL2",
"expires_at": "2026-04-15T10:40:00.000Z"
}JWT Payload Structure (stk namespace)
The decoded JWT uses a stk namespace for all STACK-specific claims. The sub field is the agent ID.
{
"iss": "https://api.getstack.run",
"sub": "agt_7kx9m2nq4p",
"iat": 1713174600,
"exp": 1713175200,
"jti": "ppt_9xm3kR7wL2",
"stk": {
"operator_id": "op_abc123",
"agent_id": "agt_7kx9m2nq4p",
"agent_name": "invoice-processor",
"services": [
{
"service_id": "svc_123",
"service_name": "slack",
"scopes": ["read:messages"],
"credential_ref": "cred_ref_abc"
}
],
"identity_claims": [
{
"claim_id": "clm_9xm3kR7wL2",
"provider": "bankid_se",
"layer": "humanity",
"claim_type": "verified_human",
"assurance_level": "high",
"verified_at": 1713174000,
"expires_at": 1744710000,
"humanity_verified": true
}
],
"delegation_depth": 0,
"session_id": "sess_abc123",
"accountability": "enforced",
"intent_summary": "Process customer support emails",
"intent_services": ["slack", "gmail"],
"checkpoint_interval": 300
}
}Verify Passport
POST /v1/passports/verify
Verify a passport token and return its decoded claims. This endpoint is unauthenticated — no API key is required. It checks the EdDSA signature, expiration, and revocation status. Optionally pass a service_id to verify the passport grants access to a specific service.
Request Body
{
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
"service_id": "svc_123"
}- token (string, required) — The passport JWT to verify.
- service_id (string, optional) — If provided, verification also checks that the passport includes scopes for this service.
Request Example
curl -X POST https://api.getstack.run/v1/passports/verify \
-H "Content-Type: application/json" \
-d '{
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9..."
}'Response — 200 OK
{
"valid": true,
"jti": "ppt_9xm3kR7wL2",
"agent_id": "agt_7kx9m2nq4p",
"expires_at": "2026-04-15T10:40:00.000Z",
"claims": {
"operator_id": "op_abc123",
"agent_id": "agt_7kx9m2nq4p",
"agent_name": "invoice-processor",
"services": [],
"identity_claims": [],
"delegation_depth": 0,
"session_id": "sess_abc123"
}
}The verify endpoint does not require authentication. Any service can verify a passport by posting the token. For fully offline verification, use the JWKS endpoint to fetch STACK's public keys and verify the EdDSA signature locally.
Revoke Passport
POST /v1/passports/revoke
Immediately revoke an active passport. Revocation propagates via Redis within 60 seconds.
Request Body
{
"jti": "ppt_9xm3kR7wL2",
"reason": "Agent compromised"
}- jti (string, required) — The passport JTI (unique identifier) to revoke.
- reason (string, optional) — Human-readable reason for revocation. Default: "Revoked by operator".
Response — 200 OK
{
"success": true,
"jti": "ppt_9xm3kR7wL2"
}Delegate Passport
POST /v1/passports/delegate
Create a delegated passport from an existing one. The child passport inherits the parent's identity chain but can only have equal or narrower scopes. Maximum delegation depth is 4 hops.
Request Body
{
"parent_passport_token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
"child_agent_id": "agt_3fh8j1kw6r",
"scopes": [
{
"service_connection_id": "svc_conn_abc123",
"scopes": ["read:messages"]
}
],
"ttl_seconds": 300
}- parent_passport_token (string, required) — A valid, non-expired passport JWT to delegate from.
- child_agent_id (string, required) — The target agent receiving the delegated passport.
- scopes (array, required) — Array of {service_connection_id, scopes}. Must be a subset of the parent passport scopes.
- ttl_seconds (number, optional) — TTL for the child passport. Min 60, max 3600. Cannot exceed parent expiry.
Response — 201 Created
{
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
"jti": "ppt_5nK2xW8mR4",
"expires_at": "2026-04-15T10:35:00.000Z"
}Refresh Passport
POST /v1/passports/refresh
Refresh an existing passport token. Returns a new JWT with a fresh TTL while preserving the same scopes, claims, and delegation chain.
Request Body
{
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
"ttl_seconds": 900
}- token (string, required) — The passport JWT to refresh.
- ttl_seconds (number, optional) — TTL for the new passport. Min 60, max 3600. Default: 900.
Response — 201 Created
{
"token": "eyJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9...",
"jti": "ppt_8rW4mK9xN1",
"expires_at": "2026-04-15T10:55:00.000Z"
}Submit Checkpoint
POST /v1/passports/:jti/checkpoint
Submit a checkpoint for an active passport. Checkpoints report what the agent has been doing — which services were used, how many actions taken, and whether any delegation occurred. Required at regular intervals for agents in enforced accountability mode.
Request Body
{
"services_used": ["slack", "gmail"],
"tool_calls": [
{ "service": "slack", "method": "postMessage", "target": "#support" },
{ "service": "gmail", "method": "send" }
],
"actions_count": 5,
"delegated_to": [],
"summary": "Responded to 3 support tickets, sent 2 follow-up emails"
}- services_used (string[], required) — List of service identifiers accessed since last checkpoint.
- tool_calls (array, optional) — Detailed tool call log. Each entry has service, method, and optional target.
- actions_count (number, required) — Total number of actions performed. Min 0.
- delegated_to (string[], optional) — Agent IDs that this passport was delegated to.
- summary (string, optional) — Human-readable summary of activity. Max 1000 characters.
Response — 201 Created
Returns the checkpoint record with any flags raised by the accountability engine.
Submit Checkout
POST /v1/passports/:jti/checkout
Submit a final checkout report when the agent is done using the passport. This is the end-of-session report that triggers the review process for agents in enforced mode. The checkout compares the agent's declared intent against its actual activity.
Request Body
{
"services_used": ["slack", "gmail"],
"tool_calls": [
{ "service": "slack", "method": "postMessage" }
],
"actions_count": 12,
"delegated_to": [],
"summary": "Completed support ticket processing session"
}- services_used (string[], required) — All services accessed during the passport session.
- tool_calls (array, optional) — Complete tool call log for the session.
- actions_count (number, required) — Total actions performed. Min 0.
- delegated_to (string[], optional) — Agent IDs this passport was delegated to.
- summary (string, optional) — Session summary. Max 2000 characters.
For agents in enforced mode, the checkout triggers an automated review. If critical flags are raised (e.g., undeclared services, missed checkpoints), the agent may be blocked from receiving new passports until an operator approves the review.
Get Passport Report
GET /v1/passports/:jti/report
Retrieve the full accountability report for a passport, including all checkpoints, the checkout summary, any raised flags, and the review status.
Request Example
curl https://api.getstack.run/v1/passports/ppt_9xm3kR7wL2/report \
-H "Authorization: Bearer sk_live_op_abc123"Review Queue
GET /v1/passports/reviews
List passport checkout reviews that need operator attention. Supports filtering by status and pagination.
Query Parameters
- status (string, optional) — Filter by review status.
- page (number, optional) — Page number, starting at 1.
- limit (number, optional) — Results per page, max 100.
Request Example
curl "https://api.getstack.run/v1/passports/reviews?status=pending&limit=10" \
-H "Authorization: Bearer sk_live_op_abc123"Decide Review
POST /v1/passports/reviews/:checkoutId/decide
Submit an approval or block decision for a passport checkout review.
Request Body
{
"decision": "approved",
"notes": "Reviewed activity log, all actions within expected scope",
"block_future": false
}- decision (string, required) — Either "approved" or "blocked".
- notes (string, optional) — Operator notes about the decision. Max 1000 characters.
- block_future (boolean, optional) — If true and decision is "blocked", prevents the agent from getting new passports.
List Active Passports
GET /v1/passports/active
List all currently active (non-expired, non-revoked) passports for your operator account. Supports filtering by agent or session.
Query Parameters
- agent_id (string, optional) — Filter to passports for a specific agent.
- session_id (string, optional) — Filter to passports in a specific session.
Response — 200 OK
[
{
"jti": "ppt_9xm3kR7wL2",
"agent_id": "agt_7kx9m2nq4p",
"session_id": "sess_abc123",
"delegation_depth": 0,
"parent_passport_id": null,
"identity_claim_ids": ["clm_9xm3kR7wL2"],
"issued_at": "2026-04-15T10:30:00.000Z",
"expires_at": "2026-04-15T10:45:00.000Z"
}
]Bulk Revocation
Revoke All for Agent
POST /v1/passports/revoke-agent/:agentId — Revoke all active passports for a specific agent.
curl -X POST https://api.getstack.run/v1/passports/revoke-agent/agt_7kx9m2nq4p \
-H "Authorization: Bearer sk_live_op_abc123" \
-H "Content-Type: application/json" \
-d '{ "reason": "Agent compromised" }'Revoke by Session
POST /v1/passports/revoke-session/:sessionId — Revoke all active passports within a specific session.
curl -X POST https://api.getstack.run/v1/passports/revoke-session/sess_abc123 \
-H "Authorization: Bearer sk_live_op_abc123" \
-H "Content-Type: application/json" \
-d '{ "reason": "Session terminated" }'Revoke All (Emergency)
POST /v1/passports/revoke-all — Revoke every active passport for your entire operator account. Requires explicit confirmation.
{
"confirm": true,
"reason": "Emergency: suspected key compromise"
}Bulk Revocation Response
{
"success": true,
"revoked_count": 7
}The revoke-all endpoint is an emergency action that immediately invalidates every active passport in your organization. Use with extreme caution.
JWKS Endpoint
GET /v1/.well-known/jwks.json
Public endpoint (no authentication required) that returns STACK's Ed25519 public keys in JWKS format. Use this to verify passport signatures offline without calling the verify endpoint.
Request Example
curl https://api.getstack.run/v1/.well-known/jwks.jsonSigning and Verification
- Algorithm: EdDSA with Ed25519 curve
- Default TTL: 900 seconds (15 minutes)
- Maximum TTL: 3600 seconds (1 hour)
- Minimum TTL: 60 seconds
- Revocation propagation: under 60 seconds via Redis
- Verification: local via JWKS or POST /v1/passports/verify
- Custom claims namespace: stk (all STACK claims nested under this key)