§ 06 · DELEGATION DEMO · SCOPED AUTHORITY

Grant a weekly cap, spend within it, then pause, revoke, and verify.

Six steps that show Paphwey's delegation lifecycle end to end: a signed Verifiable Credential for authority, fine-grained spend caps that deny out-of-policy purchases with a structured reason, mid-lifecycle pause and revoke controls, and a daily signed audit anchor so any party can prove the story was not edited.

Illustrative walk-through — fixture data, no database writes. The payload shapes mirror the real gateway responses.

§ 07 · Delegation Lifecycle Six steps

Lifecycle

Read each card top-to-bottom — the structured payload underneath is what the gateway actually emits.

Delegation credentials are signed JWS envelopes carrying the W3C Verifiable Credential v2 shape. Policy caps run before the wallet is invoked, and the audit chain ties every decision into a daily Merkle root signed with the active key.

Step 01 · OK

Grant weekly cap

Principal approves a scoped £25/week delegation.

The wallet holder taps Approve on a structured grant card. Paphwey writes an AgentDelegation and packs a Verifiable Credential JWT. The credential carries the exact caps that will be re-checked on every downstream challenge.

Payload shape
{'agent_id': 'assistant-demo',
 'allowed_scopes': ['payments:approve'],
 'credential_jwt_preview': 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InJzYS0yMDI2LTA0LXJvdGF0aW9uLTAxIn0.eyJzdWIiOiJwYXBod2V5OmRlbGVnYXRpb246ZDAwZjI3YTQiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvbnMvY3JlZGVudGlhbHMvdjIiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlBhcGh3ZXlEZWxlZ2F0aW9uQ3JlZGVudGlhbCJdfX0.T3RLPNDiO4...',
 'credential_kid': 'rsa-2026-04-rotation-01',
 'delegation_id': 'd00f27a4-9c1a-41d3-b33e-2f6c4df5e2b1',
 'principal': 'alex@example.com',
 'provider': 'openai',
 'spend_cap': {'amount_minor': 2500, 'currency': 'GBP', 'window': 'weekly'}}

Recorded at 2026-05-14T09:43:13.312048+00:00

Step 02 · OK

Buy £15 passes

Challenge approved inside cap.

The agent presents a £15.00 purchase. Policy evaluation checks the delegation cap first, then the relying-party policy. Both pass. An attestation is issued and the RP is told why.

Payload shape
{'amount_minor': 1500,
 'cap_check': {'currency': 'GBP',
               'remaining_minor': 1000,
               'spent_this_window_minor': 1500},
 'challenge_type': 'PAYMENT_STEPUP_REQUIRED',
 'currency': 'GBP',
 'decision': 'APPROVED'}

Recorded at 2026-05-14T09:48:13.312048+00:00

Step 03 · DENIED

Buy £40 denied

SPEND_CAP_EXCEEDED — the cap blocks the action before the wallet is ever shown.

The agent attempts a £40.00 purchase. Cap evaluation sees the request exceeds the weekly allowance and stops the flow with a structured reason. Nothing reaches the relying party.

Payload shape
{'amount_minor': 4000,
 'cap_check': {'currency': 'GBP',
               'remaining_minor': 1000,
               'requested_minor': 4000},
 'challenge_type': 'PAYMENT_STEPUP_REQUIRED',
 'currency': 'GBP',
 'decision': 'DENIED',
 'reasons': ['SPEND_CAP_EXCEEDED']}

Recorded at 2026-05-14T09:51:13.312048+00:00

Step 04 · PAUSED

Pause delegation

Mid-lifecycle control — the user hits pause from the wallet.

Pause is a soft halt. The delegation row stamps paused_at and the status endpoint begins returning status="paused". Unpause later restores the same credential without reissuing.

Payload shape
{'delegation_id': 'd00f27a4-9c1a-41d3-b33e-2f6c4df5e2b1',
 'paused_at': '2026-05-14T09:54:13.312048+00:00',
 'status': 'paused',
 'unpause_available': True}

Recorded at 2026-05-14T09:54:13.312048+00:00

Step 05 · REVOKED

Revoke delegation

Hard stop — the credential is invalidated and surfaced on the revocation list.

Revocation stamps revoked_at and invalidates the credential. The signed GET /api/v1/delegations/revocations.json snapshot now lists this delegation. Any relying party can self-serve the check.

Payload shape
{'delegation_id': 'd00f27a4-9c1a-41d3-b33e-2f6c4df5e2b1',
 'revocations_snapshot': '/api/v1/delegations/revocations.json',
 'revoked_at': '2026-05-14T09:56:13.312048+00:00',
 'status': 'revoked',
 'status_endpoint': '/api/v1/delegations/status/d00f27a4-9c1a-41d3-b33e-2f6c4df5e2b1'}

Recorded at 2026-05-14T09:56:13.312048+00:00

Step 06 · ANCHORED

Verify audit anchor

All five steps land on the signed daily Merkle root.

Every audit event above was hash-chained at write time. The daily anchor Merkle-roots the day's chain and signs it with the active key. External auditors poll the anchor endpoint; any tampering breaks both the chain and the root.

Payload shape
{'date': '2026-05-14',
 'endpoint': '/.well-known/paphwey-audit-anchor',
 'event_count': 17,
 'kid': 'rsa-2026-04-rotation-01',
 'merkle_root': '2b9e1c9f3a0d7e5b6f8c9d5a74e1b3c6d08f2a4b6c7d8e9f0a1b2c3d4e5f6071',
 'published_at': '2026-05-14T09:57:13.312048+00:00',
 'signature_preview': 'MEUCIQDh2V7r...truncated...dK8c='}

Recorded at 2026-05-14T09:57:13.312048+00:00

Policy contract

Delegation cap first, policy cap second, structured reason always.

  • SPEND_CAP_EXCEEDED — the requested amount exceeds the delegation's remaining window.
  • DOMAIN_NOT_ALLOWED — the audience domain is not in the per-RP allowlist.
  • API_NOT_ALLOWED — the route is not in the per-RP allowlist.
  • APPROVAL_THRESHOLD_REQUIRED — the amount triggers a step-up requirement.

These reasons are machine-readable. Relying parties can surface them directly to their own risk tooling without having to re-interpret a freeform string.

What the RP sees

A signed attestation — or a signed denial — never a silent failure.

  • Credential JWT kid = rsa-2026-04-rotation-01 is discoverable via the JWKS endpoint.
  • Status endpoint /api/v1/delegations/status/d00f27a4-9c1a-41d3-b33e-2f6c4df5e2b1 returns a signed envelope.
  • Revocation snapshot /api/v1/delegations/revocations.json is pollable and signed.
  • Daily anchor /.well-known/paphwey-audit-anchor?date=YYYY-MM-DD covers the whole chain.
§ 08 · Under The Hood Payload trace

Full payload

The structured record for this walk-through.

A single JSON document describing every step. Open your downstream tooling against the real endpoints and you will receive documents with the same shape — hash-chained, signed, and repeatable.

{
  "delegation_id": "d00f27a4-9c1a-41d3-b33e-2f6c4df5e2b1",
  "credential_kid": "rsa-2026-04-rotation-01",
  "principal_email": "alex@example.com",
  "relying_party_name": "Demo Merchant",
  "steps": [
    {
      "index": 1,
      "slug": "grant",
      "status": "ok",
      "title": "Grant weekly cap",
      "subtitle": "Principal approves a scoped \u00a325/week delegation.",
      "at": "2026-05-14T09:43:13.312048+00:00",
      "description": "The wallet holder taps Approve on a structured grant card. Paphwey writes an AgentDelegation and packs a Verifiable Credential JWT. The credential carries the exact caps that will be re-checked on every downstream challenge.",
      "payload": {
        "delegation_id": "d00f27a4-9c1a-41d3-b33e-2f6c4df5e2b1",
        "principal": "alex@example.com",
        "provider": "openai",
        "agent_id": "assistant-demo",
        "allowed_scopes": [
          "payments:approve"
        ],
        "spend_cap": {
          "amount_minor": 2500,
          "currency": "GBP",
          "window": "weekly"
        },
        "credential_kid": "rsa-2026-04-rotation-01",
        "credential_jwt_preview": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6InJzYS0yMDI2LTA0LXJvdGF0aW9uLTAxIn0.eyJzdWIiOiJwYXBod2V5OmRlbGVnYXRpb246ZDAwZjI3YTQiLCJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvbnMvY3JlZGVudGlhbHMvdjIiXSwidHlwZSI6WyJWZXJpZmlhYmxlQ3JlZGVudGlhbCIsIlBhcGh3ZXlEZWxlZ2F0aW9uQ3JlZGVudGlhbCJdfX0.T3RLPNDiO4..."
      }
    },
    {
      "index": 2,
      "slug": "buy-pass",
      "status": "ok",
      "title": "Buy \u00a315 passes",
      "subtitle": "Challenge approved inside cap.",
      "at": "2026-05-14T09:48:13.312048+00:00",
      "description": "The agent presents a \u00a315.00 purchase. Policy evaluation checks the delegation cap first, then the relying-party policy. Both pass. An attestation is issued and the RP is told why.",
      "payload": {
        "challenge_type": "PAYMENT_STEPUP_REQUIRED",
        "amount_minor": 1500,
        "currency": "GBP",
        "decision": "APPROVED",
        "cap_check": {
          "spent_this_window_minor": 1500,
          "remaining_minor": 1000,
          "currency": "GBP"
        }
      }
    },
    {
      "index": 3,
      "slug": "buy-denied",
      "status": "denied",
      "title": "Buy \u00a340 denied",
      "subtitle": "SPEND_CAP_EXCEEDED \u2014 the cap blocks the action before the wallet is ever shown.",
      "at": "2026-05-14T09:51:13.312048+00:00",
      "description": "The agent attempts a \u00a340.00 purchase. Cap evaluation sees the request exceeds the weekly allowance and stops the flow with a structured reason. Nothing reaches the relying party.",
      "payload": {
        "challenge_type": "PAYMENT_STEPUP_REQUIRED",
        "amount_minor": 4000,
        "currency": "GBP",
        "decision": "DENIED",
        "reasons": [
          "SPEND_CAP_EXCEEDED"
        ],
        "cap_check": {
          "requested_minor": 4000,
          "remaining_minor": 1000,
          "currency": "GBP"
        }
      }
    },
    {
      "index": 4,
      "slug": "pause",
      "status": "paused",
      "title": "Pause delegation",
      "subtitle": "Mid-lifecycle control \u2014 the user hits pause from the wallet.",
      "at": "2026-05-14T09:54:13.312048+00:00",
      "description": "Pause is a soft halt. The delegation row stamps paused_at and the status endpoint begins returning status=\"paused\". Unpause later restores the same credential without reissuing.",
      "payload": {
        "delegation_id": "d00f27a4-9c1a-41d3-b33e-2f6c4df5e2b1",
        "status": "paused",
        "paused_at": "2026-05-14T09:54:13.312048+00:00",
        "unpause_available": true
      }
    },
    {
      "index": 5,
      "slug": "revoke",
      "status": "revoked",
      "title": "Revoke delegation",
      "subtitle": "Hard stop \u2014 the credential is invalidated and surfaced on the revocation list.",
      "at": "2026-05-14T09:56:13.312048+00:00",
      "description": "Revocation stamps revoked_at and invalidates the credential. The signed GET /api/v1/delegations/revocations.json snapshot now lists this delegation. Any relying party can self-serve the check.",
      "payload": {
        "delegation_id": "d00f27a4-9c1a-41d3-b33e-2f6c4df5e2b1",
        "status": "revoked",
        "revoked_at": "2026-05-14T09:56:13.312048+00:00",
        "status_endpoint": "/api/v1/delegations/status/d00f27a4-9c1a-41d3-b33e-2f6c4df5e2b1",
        "revocations_snapshot": "/api/v1/delegations/revocations.json"
      }
    },
    {
      "index": 6,
      "slug": "anchor",
      "status": "anchored",
      "title": "Verify audit anchor",
      "subtitle": "All five steps land on the signed daily Merkle root.",
      "at": "2026-05-14T09:57:13.312048+00:00",
      "description": "Every audit event above was hash-chained at write time. The daily anchor Merkle-roots the day's chain and signs it with the active key. External auditors poll the anchor endpoint; any tampering breaks both the chain and the root.",
      "payload": {
        "date": "2026-05-14",
        "merkle_root": "2b9e1c9f3a0d7e5b6f8c9d5a74e1b3c6d08f2a4b6c7d8e9f0a1b2c3d4e5f6071",
        "event_count": 17,
        "kid": "rsa-2026-04-rotation-01",
        "signature_preview": "MEUCIQDh2V7r...truncated...dK8c=",
        "published_at": "2026-05-14T09:57:13.312048+00:00",
        "endpoint": "/.well-known/paphwey-audit-anchor"
      }
    }
  ]
}

Keep reading

See the trust guarantees behind the flow.