Use these steps to declare data flows, limit credentials, bind a Mission, route calls through the proxy, and select an accountability mode.
Decide which connected services may supply data to each destination. STACK refuses an undeclared cross-service movement, including one caused by prompt injection. Reading and writing within the same service needs no additional declaration.
Set the maximum on the operator's service grant. This example permits guestbook data to enter Resend:
curl -X POST https://api.getstack.run/v1/services/grant \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agt_digest_bot",
"service_connection_id": "scon_resend",
"scopes": ["email:send"],
"allow_data_from": ["guestbook"]
}'Passport issuance inherits this list unless you supply a narrower one. An omitted or empty grant permits no cross-service flow. ["*"] on the grant means every service currently granted to that agent, but STACK replaces it with exact names before signing. Connecting another service later does not expand an existing Passport. Adding a source to a grant requires a human-approved governance request when called through an API key, MCP, or the CLI.
Give the agent only the operations the job needs, and tell STACK which recipients are normal for it. A mission is where you write that down: the services it may touch, the counterparties it talks to, and hard caps on how much it can do. With the counterparties declared, the undeclared-target check only fires on a genuine stranger, not on the addresses your agent uses every run.
curl -X POST https://api.getstack.run/v1/missions \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"intent_summary": "Answer new support tickets and reply to the sender",
"intent_services": ["slack", "resend"],
"expected_counterparties": ["support@yourco.com"],
"max_actions": 20,
"max_actions_per_service": { "resend": 5 },
"max_duration_seconds": 900
}'Keep intent_services to what the job uses and set caps you would be surprised to hit. A tight max_actions and a per-service cap turn a runaway loop into a stopped mission instead of a large bill.
Opening a Mission counts as one metered action. Completing or revoking it does not.
Opening a mission does nothing on its own. Bind it to the passport your agent runs under, and STACK enforces the Mission scope and caps on each call. Mission completion records the terminal Mission state. Submit Passport checkout separately when you want the checkout review. Revoking the Mission stops its bound Passports.
curl -X POST https://api.getstack.run/v1/passports/issue \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"agent_id": "agt_support_bot",
"mission_ids": ["ms_abc123"],
"intent": {
"summary": "Answer new support tickets and reply to the sender",
"services": ["slack", "resend"]
},
"ttl_seconds": 900,
"checkpoint_interval_seconds": 300
}'The passport's services must cover what the mission narrows to, never less. The mission tightens; it never grants a service the agent was not already allowed.
Detectors and the scope wall inspect calls that go through STACK. They cannot inspect calls made directly to Slack, Stripe, or another provider. Route governed calls through the proxy. STACK injects the credential and checks the outbound request.
curl -X POST https://api.getstack.run/v1/proxy \
-H "Authorization: Bearer sk_live_your_key" \
-H "X-Passport-Token: eyJhbGciOiJFZERTQSIs..." \
-H "Content-Type: application/json" \
-d '{
"service": "slack",
"method": "POST",
"url": "https://slack.com/api/chat.postMessage",
"body": { "channel": "C0123456789", "text": "On it." }
}'Pass the full target URL, not a relative path. The proxy is on every tier, including Free, and a valid X-Passport-Token is required on every call. If your agent framework calls providers directly, point governed calls at the proxy.
Proxy scope and constraint checks deny invalid calls in every mode. Logged mode records detector results without the enforced detector block. Enforced mode can also block a critical detector result. Start in logged mode to calibrate your detector settings. Then move to enforced mode.
curl -X PATCH https://api.getstack.run/v1/agents/agt_support_bot \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{ "accountability_mode": "logged" }'For a long job, submit checkpoints at the declared interval. Each checkpoint records progress and advances the stored monitoring deadline. It does not change the signed JWT expiry. Refresh the Passport to continue after that expiry. See passport lifecycle for the full issue, checkpoint, checkout, review flow, and enforced mode for what changes when you enable enforcement.
Available checks depend on the agent's actions:
A one-call agent does not exercise every detector. Configure the controls that apply to the agent's actual work.
/attack is our public demo: a live inbox a STACK-governed agent reads and replies to, open for anyone to try to break. Send it an attack and view the resulting checks on the board.
Related: proxy, detectors, passport lifecycle, enforced mode.