# Protocol check & merchant verify

> Send AP2, VI, ACP, or SPT to Warrant — KYI normalizes and verifies on the API, then ALLOW/DENY with a receipt.

**Normalization and crypto verification run on the KYI API** (`kyi/`). The SDK only posts the protocol envelope.

## Flow

1. User seals an Abstraxn **mandate** (authority ceiling).
2. Agent builds a cart and may create AP2 / VI / ACP+SPT credentials.
3. Agent calls **`POST /v1/warrant/decisions`** with a protocol envelope (or legacy `NormalizedAction`).
4. KYI **decodes → verifies → Cedar check** → ALLOW/DENY + signed receipt.
5. Agent **stops on DENY** (KYI does not sit in the payment path).
6. Merchant/PSP calls **`POST /v1/warrant/verify`** with the same cart + `mandate_id`.

## Agent: protocol check

```
POST /v1/warrant/decisions
x-api-key: <mandate API key>
```

### ACP example

```json
{
  "protocol": "acp",
  "agent_id": "agent_chatgpt_01",
  "domain": "commerce.checkout",
  "session": {
    "currency": "usd",
    "merchant_id": "acct_nike",
    "line_items": [{ "name": "Nike Air", "amount": 9900, "quantity": 1 }]
  },
  "spt": {
    "merchant_id": "acct_nike",
    "allowance": { "max_amount": 9900, "currency": "usd" },
    "expires_at": 1893456000
  }
}
```

Amounts in ACP/SPT/AP2/VI payment claims are **minor units** (cents). KYI converts to major units before Cedar (e.g. `9900` → `99.00` USD).

### AP2 / VI example

Send a compact SD-JWT and issuer keys:

```json
{
  "protocol": "ap2",
  "agent_id": "agent_chatgpt_01",
  "domain": "commerce.checkout",
  "credential": "<issuer-jwt>~<disclosure>~…",
  "checkout": {
    "checkout_hash": "<same as payment transaction_id>",
    "line_items": [{ "name": "Nike Air", "category": "footwear" }]
  },
  "verification": {
    "jwks": { "keys": [/* issuer JWK */] }
  }
}
```

Or use `"verification": { "jwks_url": "https://issuer.example/.well-known/jwks.json" }` (HTTPS only; private hosts blocked).

**Unsigned AP2/VI payment claims alone are rejected** — a verifiable credential string is required.

### SDK

```ts
import { Warrant } from '@abstraxn/warrant';

const warrant = new Warrant({ apiKey: process.env.WARRANT_MANDATE_API_KEY! });

const { decision, protocol_evidence } = await warrant.checkProtocol({
  protocol: 'acp',
  agent_id: 'agent_chatgpt_01',
  domain: 'commerce.checkout',
  session: { /* … */ },
  spt: { /* … */ },
});

if (decision.verdict !== 'ALLOW') {
  // do not create SPT / complete checkout
}
```

## Merchant: verify (shield)

```
POST /v1/warrant/verify
x-api-key: <application API key>
```

Body = same protocol envelope **plus** `mandate_id`.

```ts
const merchant = new Warrant({ apiKey: process.env.ABSTRAXN_API_KEY! });

const { decision } = await merchant.verify({
  mandate_id: 'mnd_sha256:…',
  protocol: 'acp',
  agent_id: 'agent_chatgpt_01',
  domain: 'commerce.checkout',
  session: { /* same cart */ },
  spt: { /* … */ },
});
```

## What each protocol does today

| Protocol | Decode | Crypto |
|----------|--------|--------|
| `raw` | Passthrough `NormalizedAction` | n/a |
| `ap2` | SD-JWT → payment/checkout claims | Issuer JWT (+ optional KB-JWT) via JWKS |
| `vi` | Same claim map as AP2 | Same as AP2 |
| `acp` | Session line items + optional SPT | Structural + expiry / amount / merchant match |
| `spt` | SPT allowance as proposed charge | Expiry + required fields |

## Sample files in repo

See `kyi/examples/protocol-samples/` for copy-paste curl bodies.

## Related

- [How it works](/guides/kyi/how-it-works)
- [SDK quickstart](/guides/kyi/sdk-quickstart)
- [API reference](/guides/kyi/api-reference)
