Skip to main content
A Proof of Resolution is the signed result of a decided action. It is a JWT signed by BotShield with ES256, issued when a human gives a Confirm or a Deny with their device biometric. You verify it locally against BotShield’s public keys, check that it belongs to your agent and your request, and only then execute.
Both decisions are signed: a Confirm carries verdict: "approve" and a Deny carries verdict: "denied". An Expired or cancelled proposal produces no proof at all. Absence of a proof is never permission.

Token format

Header:
Payload:
The proof carries no personal data and does not include the Adaptive Card body.

Public keys

The JWK Set holds EC P-256 public keys with alg: "ES256", use: "sig", and a kid. It is served with a 5-minute cache lifetime, so cache it for 5 minutes and refetch when you meet an unknown kid. The same key set signs BotShield Gate attestation tokens.

Verify before you execute

Verification is local. There is no BotShield endpoint to call and nothing to trust except the signature. Install jose and verify:
Use it as the gate in front of the action:
Run this check in deterministic code on your server, at the point where the action executes. A language model cannot verify a signature, and a status string is not proof. Compare against your own stored request_id, opaque_id, and category, not against values the agent hands you alongside the token.

Find your agent’s id

aud is the Agent ID: the UUID BotShield assigned to your agent, not its name. Copy it from the BotShield Console:
  1. Go to Agents Ask → Trusted Agents and select the environment with the Development | Production toggle.
  2. Find the agent’s row. The Agent ID is shown under the agent name.
  3. Click the copy button next to it.
Store the value in configuration next to the agent key (the example above reads it from BOTSHIELD_AGENT_ID), and pin audience to it. Treat it as configuration, not as a value you read from the token or from the agent at run time: the check is only worth something when the expected audience comes from you.
  • A development agent and a production agent have different Agent IDs, so configure each environment with its own.
  • The Agent ID stays the same when you rotate the agent’s key. A proof issued before a rotation still verifies against the same audience.
  • An agent you register after revoking an old one has a new Agent ID, even if it reuses the name. Update the audience when you replace an agent.
The same value appears in the webhooks your organization receives, as agent_id in every agents_ask.card.proposed payload and as metadata.agent_id in every agents_ask.resolution.* payload. Use it there to tell which agent an event belongs to.

How the proof reaches you

The same token is delivered two ways. Verify it the same way on both, and make execution idempotent on jti, because you may receive it twice.
GET /agentlink/check-status returns the token as resolution_jwt once the status is approved or denied. It is null for queued, expired, and cancelled.
See Wait for the decision.
Verify the webhook signature and the proof_token. The webhook signature proves BotShield sent the delivery to you. The proof proves what the human decided, and it stays verifiable after you store it or pass it to another system.

Outcomes at a glance

Keep the proof

Store the token with the record of the action it authorized. It is a self-contained, independently verifiable record that a verified human confirmed that exact request, which is useful for disputes and audits. Verify it when it arrives and record the result and the time next to it: after exp the token no longer passes a standard JWT verification.

Next steps

Propose an action

Where request_id, verdict, and resolution_jwt come from.

Webhook events

Full payloads for the agents_ask.* events.

Privacy boundary

Why sub is an opaque, per-agent id.

agentgateway

Use BotShield with agentgateway in front of your agent’s tools.