API Reference
BSA/OFAC compliance middleware for AI agent-initiated payments.
Introduction
CLEARAGENT is a single-call compliance layer that sits between an AI agent and a payment rail. Send a KYA credential token and transaction details; receive a verdict — CLEAR, REVIEW, BLOCK, or UNKNOWN — in under 100ms.
The API runs on Cloudflare Workers at the edge. There is no server, no backend, no warm-up time. Every request is processed in the data center closest to the caller.
Authentication
Register at api.clearagent.dev to receive a KYAOperatorCredential — a signed ES256 JWT valid for one year. Pass it as a Bearer token on every request.
curl -X POST https://api.clearagent.dev/v1/screen \
-H "Authorization: Bearer <your-KYAOperatorCredential-JWT>" \
-H "Content-Type: application/json" \
-d '{...}'
The following routes require no authentication: POST /v1/operators/register, POST /v1/demo/screen, GET /v1/health, GET /v1/.well-known/jwks.json, POST /v1/webhooks/stripe (authenticated via Stripe signature), GET /v1/attestation/arc/:txnId (on-chain data is public).
PAYMENT-SIGNATURE header on POST /v1/screen — no Bearer token or billing plan required. See x402 Pay-Per-Call for details.
Questions? [email protected]
Base URL
https://api.clearagent.dev
Verdict Reference
Every POST /v1/screen response includes a verdict field with one of four values. The same format is used across the landing page, demo, and API:
| Verdict | Meaning | Recommended action |
|---|---|---|
| CLEAR | All 25 rules passed. Transaction is clear. | Allow payment to execute. |
| REVIEW | A spend policy, behavioral flag, or soft risk rule triggered. | Route to compliance officer queue before executing. |
| BLOCK | OFAC hit, invalid credential, or hard policy violation. | Reject the transaction. Do not execute. |
| UNKNOWN | No KYA credential presented, or credential is structurally invalid. | Hold transaction. Request credential from agent operator. |
Error Codes
| HTTP Status | Code | Description |
|---|---|---|
| 400 | missing_token | No KYA token provided in the request body. |
| 400 | invalid_json | Request body is not valid JSON. |
| 401 | unauthorized | Missing or invalid API key. |
| 402 | payment_required | No active billing plan. Call POST /v1/billing/checkout to add a payment method. |
| 404 | not_found | Audit record txnId does not exist or has expired (90-day TTL). |
| 405 | method_not_allowed | Wrong HTTP method for this endpoint. |
| 429 | rate_limit_exceeded | Monthly screening limit reached. Check X-RateLimit-* headers or upgrade plan. |
| 500 | internal_error | Worker fault. Contact support with the txnId if available. |
| 503 | billing_not_configured | Stripe billing is not configured on this instance. |
Example error responses
// 401 Unauthorized — missing or invalid Bearer token
{
"error": "unauthorized",
"message": "Missing or invalid Authorization header"
}
// 402 Payment Required — no active billing plan
{
"error": "payment_required",
"message": "No active billing plan",
"checkoutUrl": "https://api.clearagent.dev/v1/billing/checkout"
}
// 429 Rate Limit Exceeded — monthly screening quota hit
{
"error": "rate_limit_exceeded",
"message": "Monthly screening limit reached (100/100). Upgrade to Pro for 10,000 screens.",
"upgradeUrl": "https://api.clearagent.dev/v1/billing/checkout"
}
/v1/screen response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers. Check these to avoid hitting the limit.
Register Operator
No authentication required. Self-service onboarding endpoint. Provide your legal name and email; ClearAgent screens both against the OFAC SDN list and — if clear — issues two signed JWTs: an operatorToken (KYAOperatorCredential, valid 1 year) for the Authorization header, and an agentToken (KYAAgentCredential, valid 90 days) for the token field in /v1/screen requests. This lets you start screening immediately — no separate agent registration step required.
Also available as a web form at api.clearagent.dev. Rate limited to 3 registrations per email per day.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| legalName | string | required | Legal name of the operator entity. Screened against OFAC SDN at registration. |
| string | required | Contact email address. | |
| wallet | string | optional | Operator wallet address. OFAC-screened at registration. Supports EVM (0x… — ETH, Base, Arc), Solana/SVM (base58 — SOL, Rialo), Tron (T…), Sei (sei1…). |
| description | string | optional | Brief description of your use case. |
Example request
curl -X POST https://api.clearagent.dev/v1/operators/register \
-H "Content-Type: application/json" \
-d '{
"legalName": "Acme AI Inc.",
"email": "[email protected]",
"wallet": "0xYourWalletAddress"
}'
Example response — 201 Created
{
"operatorId": "op_m3k9xr2q",
"agentId": "agt_m3k9xr3s",
"status": "ACTIVE",
"ofacAtIssuance": "CLEAR",
"issuedAt": "2026-03-18T12:00:00.000Z",
"expiresAt": "2027-03-18T12:00:00.000Z",
"operatorToken": "eyJhbGciOiJFUzI1NiJ9.operator...",
"agentToken": "eyJhbGciOiJFUzI1NiJ9.agent...",
"token": "eyJhbGciOiJFUzI1NiJ9.operator..."
}
operatorToken (1-year) goes in your Authorization: Bearer header on every call. agentToken (90-day) goes in the "token" field of your /v1/screen request body. token is a legacy alias for operatorToken. Store both securely.Register Agent
Issue a W3C Verifiable Credential (ES256-signed JWT) for an AI agent. The operator legal name is OFAC-screened at issuance; the operator wallet is optional (non-crypto operators can omit it). The credential is valid for 91 days and stored in KV under cred:{agentId}. Note: A default agent credential is already issued at operator registration — you only need this endpoint if you want to register additional agents with different configurations.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| agentName | string | required | Human-readable name for the agent (e.g. "PayrollBot v2"). |
| agentFramework | string | optional | Framework powering the agent: LangChain, CrewAI, OpenAI Assistants, AutoGen, etc. |
| underlyingModel | string | optional | Underlying LLM: claude-sonnet-4-6, gpt-4o, etc. Model change after issuance triggers a REVIEW flag. |
| agentType | string | required | autonomous or supervised. Autonomous agents require human heartbeat attestation every 24h. |
| operatorWallet | string | optional | Operator wallet address (EVM, Solana, Tron, Sei). OFAC-screened at issuance if provided. Omit for non-crypto operators (ACH, wire, etc.). |
| operatorEIN | string | tradfi | Operator EIN for TradFi/ACH payment flows. |
| operatorLegalName | string | required | Legal entity name. Checked against OFAC CONS_ENHANCED name list. |
| purpose | string | required | Plain-language description of the agent's payment purpose. |
| spendPolicy | object | optional | Spend limits and restrictions (see below). |
spendPolicy object
| Field | Type | Description |
|---|---|---|
| maxSingleTxUSDC | number | Maximum single transaction value in USD. Transactions above this trigger REVIEW. |
| dailyLimitUSDC | number | Cumulative daily spend ceiling in USD. Tracked per agent per UTC date. |
| allowedChains | string[] | Permitted payment chains: ETH, BASE, SOL, POLYGON, BSC, TRON, SEI, BTC, ARC, RIALO, ACH, WIRE, SWIFT. |
| allowedRails | string[] | Permitted payment rails: USDC, ACH, WIRE, SWIFT, IBAN. |
| counterpartyScreening | string | Screening level: OFAC, OFAC+CONS. |
| noMixerProtocols | boolean | If true, transactions to Tornado Cash, Railgun, or other mixers are blocked. |
Example request
curl -X POST https://api.clearagent.dev/v1/agents/register \
-H "Authorization: Bearer <your-KYAOperatorCredential-JWT>" \
-H "Content-Type: application/json" \
-d '{
"agentName": "PayrollBot v2",
"agentFramework": "LangChain",
"underlyingModel": "claude-sonnet-4-6",
"agentType": "autonomous",
"operatorLegalName": "Acme Corp",
"operatorWallet": "0xYourWalletAddress",
"purpose": "Automated USDC payroll disbursements to contractors",
"spendPolicy": {
"maxSingleTxUSDC": 10000,
"dailyLimitUSDC": 50000,
"allowedChains": ["ETH", "BASE"],
"counterpartyScreening": "OFAC+CONS",
"noMixerProtocols": true
}
}'
Example response
{
"agentId": "agt_Xk9mQ2Rz",
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
"credential": {
"@context": ["https://www.w3.org/2018/credentials/v1", "https://kya.protocol/v1"],
"type": ["VerifiableCredential", "KYAAgentCredential"],
"issuer": "did:kya:clearagent-alpha",
"credentialSubject": {
"agentId": "agt_Xk9mQ2Rz",
"agentName": "PayrollBot v2",
"agentType": "autonomous",
"operatorLegalName": "Acme Corp",
"spendPolicy": { "maxSingleTxUSDC": 10000, "dailyLimitUSDC": 50000, "allowedChains": ["ETH","BASE"] },
"complianceAttestation": {
"ofacAtIssuance": "CLEAR",
"screenedAt": "2026-03-11T14:22:00Z",
"credentialStatus": "ACTIVE"
}
},
"expirationDate": "2026-06-10T14:22:00Z"
}
}
Screen Transaction
Screen a proposed transaction against the 25-rule compliance engine. Returns a verdict in under 100ms. A txnId is generated for every call and the full result is written to the tamper-evident audit log (90-day retention). Alternatively, you can skip the Bearer token entirely and pay per call via x402.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| token | string | required | KYA credential JWT. Accepts Bearer, VerifiableCredential, ProofPack, or JWT scheme prefixes, or raw token. |
| amount | number | required | Transaction amount in USD. |
| chain | string | optional | Payment chain: ETH, BASE, SOL, POLYGON, BSC, TRON, SEI, BTC, ARC, RIALO, ACH, WIRE, SWIFT. |
| rail | string | optional | Payment rail: USDC, ACH, WIRE, SWIFT, IBAN. |
| cpWallet | string | crypto | Counterparty Ethereum wallet address. OFAC-screened. |
| cpName | string | optional | Counterparty legal name. Checked against OFAC CONS_ENHANCED name list. |
| cpType | string | optional | Counterparty type: individual, business, exchange. |
| protocol | string | optional | Protocol or platform name. Used for mixer/privacy protocol detection. |
Example request
curl -X POST https://api.clearagent.dev/v1/screen \
-H "Authorization: Bearer <your-KYAOperatorCredential-JWT>" \
-H "Content-Type: application/json" \
-d '{
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9...",
"amount": 4500,
"chain": "ETH",
"rail": "USDC",
"cpWallet": "0xRecipientWalletAddress",
"cpName": "Contractor LLC",
"cpType": "business"
}'
Example response — CLEAR
{
"verdict": "CLEAR",
"txnId": "txn_A8bKmQ3x",
"riskScore": 2,
"reason": "All checks passed — transaction approved",
"ruleTriggered": null,
"signals": {
"ofac": "CLEAR",
"spendPolicy": "CLEAR",
"credential": "CLEAR"
},
"agentId": "agt_Xk9mQ2Rz",
"agentName": "PayrollBot v2",
"agentMode": "autonomous",
"credentialStatus": "ACTIVE",
"screenedAt": "2026-03-11T14:23:01Z",
"responseTimeMs": 38,
"receipt": "eyJhbGciOiJFUzI1NiIsInR5cCI6Imt5YS1yZWNlaXB0K2p3dCJ9...",
"log": [
{ "ok": true, "code": "CREDENTIAL_VALID", "msg": "Agent credential verified (ES256)" },
{ "ok": true, "code": "OFAC_CP_WALLET", "msg": "Counterparty wallet not on SDN list" },
{ "ok": true, "code": "ALL_CLEAR", "msg": "All KYA screening checks passed" }
]
}
receipt is an ES256-signed proof of this verdict, verifiable offline against the public JWKS with no API key. See Signed Screening Receipts.Example response — BLOCK (OFAC hit)
{
"verdict": "BLOCK",
"txnId": "txn_B7cNpR4y",
"riskScore": 40,
"reason": "Counterparty wallet matches OFAC SDN list: Lazarus Group / DPRK",
"ruleTriggered": "OFAC_CP_WALLET",
"signals": {
"ofac": "HIT",
"spendPolicy": "CLEAR",
"credential": "CLEAR"
},
"agentId": "agt_Xk9mQ2Rz",
"agentName": "PayrollBot v2",
"agentMode": "autonomous",
"credentialStatus": "ACTIVE",
"screenedAt": "2026-03-11T14:23:44Z",
"responseTimeMs": 29
}
25-Rule Screening Precedence
Rules are evaluated in order — first match wins. BLOCK rules take precedence over REVIEW rules.
| # | Rule ID | Verdict | Trigger |
|---|---|---|---|
| 1 | R1_NO_CREDENTIAL | UNKNOWN | No token or structurally invalid JWT |
| 2 | R2_CREDENTIAL_STATUS | BLOCK | Credential status is REVOKED, EXPIRED, or SUSPENDED |
| 3 | R3_OPERATOR_OFAC_ISSUANCE | BLOCK | Operator wallet matched OFAC at issuance |
| 4 | R4_OPERATOR_WALLET_LIVE | BLOCK | Operator wallet matches live OFAC SDN (re-screened every call) |
| 5 | R5_OPERATOR_NAME_OFAC | BLOCK | Operator legal name matches OFAC CONS_ENHANCED entity list |
| 6 | R6_COUNTERPARTY_WALLET_OFAC | BLOCK | Counterparty wallet matches OFAC SDN |
| 7 | R7_COUNTERPARTY_NAME_OFAC | BLOCK | Counterparty name matches OFAC entity list |
| 8 | R8_OWNERSHIP_TRANSFER | BLOCK | Ownership transfer flag set — credential must be re-issued |
| 9 | R9_CHAIN_POLICY | REVIEW | Transaction chain not in allowedChains |
| 10 | R10_SINGLE_TX_LIMIT | REVIEW | Transaction amount exceeds maxSingleTxUSDC |
| 11 | R11_DAILY_LIMIT | REVIEW | Transaction would exceed dailyLimitUSDC |
| 12 | R12_FLEET_DAILY_LIMIT | REVIEW | Fleet aggregate daily limit would be exceeded |
| 13 | R13_COUNTERPARTY_WHITELIST | REVIEW | Counterparty not on approved whitelist (when whitelist is set) |
| 14 | R14_MIXER_PROTOCOL | BLOCK | Protocol field matches known mixer/privacy protocol |
| 15 | R15_MODEL_CHANGED | REVIEW | Underlying model has changed since credential issuance |
| 16 | R16_VELOCITY_FLAG | REVIEW | Unusual transaction velocity detected |
| 17 | R17_UNUSUAL_HOUR | REVIEW | Transaction outside normal operating hours |
| 18–25 | R18–R25 | — | Additional rail, counterparty type, and policy rules |
| — | ALL_CLEAR | CLEAR | No rules triggered |
x402 Pay-Per-Call
Screen a transaction by paying per call with x402 micropayments instead of a monthly subscription. Send a PAYMENT-SIGNATURE header containing a base64-encoded x402 payment payload alongside your normal /v1/screen request. The payment is verified through Coinbase CDP, and on success the screen executes without requiring a Bearer token or billing plan.
Cost: $0.005 USD Coin per screen on Base (Chain ID 8453).
How it works
The x402 protocol uses HTTP 402 status codes for machine-to-machine payments. Here is the full flow:
| Step | Actor | Action |
|---|---|---|
| 1 | Client | Send POST /v1/screen without a Bearer token or payment header. |
| 2 | ClearAgent | Return 402 Payment Required with a PAYMENT-REQUIRED header describing the price, network, and payTo address. |
| 3 | Client | Construct a payment using any x402-compatible wallet or SDK. Base64-encode the payment payload. |
| 4 | Client | Resend the same POST /v1/screen request with the PAYMENT-SIGNATURE header attached. |
| 5 | ClearAgent | Decode the header, check for replay (SHA-256 hash dedup), verify the payment via Coinbase CDP, then run the screen. Return the normal screening response. |
| 6 | ClearAgent | Settle the payment asynchronously via Coinbase CDP (non-blocking). |
Request headers
| Header | Type | Required | Description |
|---|---|---|---|
| PAYMENT-SIGNATURE | string | required | Base64-encoded x402 payment payload. Max 16 KB. Must be a valid JSON object when decoded. |
| Content-Type | string | required | application/json |
PAYMENT-SIGNATURE header is present, the auth gate is bypassed. You do not need a KYAOperatorCredential or billing plan. However, a KYA agent token is still required in the request body for screening.
PAYMENT-REQUIRED response (step 2)
If you call /v1/screen without auth or payment, or if your payment is invalid, you receive a 402 with a PAYMENT-REQUIRED header. Decode the base64url value to get:
{
"x402Version": 2,
"resource": {
"url": "https://api.clearagent.dev/v1/screen",
"description": "OFAC compliance screening - one API call",
"mimeType": "application/json"
},
"accepts": [{
"scheme": "exact",
"network": "eip155:8453",
"amount": "5000",
"payTo": "0xEf81bF91e7Af1922103C3B6a9b2549Bb0824b72a",
"asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"maxTimeoutSeconds": 300,
"extra": { "name": "USD Coin", "version": "2" }
}]
}
resource is a top-level object and each accepts entry uses amount (not maxAmountRequired). The asset field is the USD Coin contract address on Base, not the ticker string.
Example request (with payment)
curl -X POST https://api.clearagent.dev/v1/screen \
-H "Content-Type: application/json" \
-H "PAYMENT-SIGNATURE: eyJhbGciOiJFUzI1NiIs..." \
-d '{
"token": "eyJhbGciOiJFUzI1NiJ9.agent...",
"amount": 4500,
"chain": "BASE",
"cpWallet": "0xRecipientWalletAddress",
"cpName": "Contractor LLC"
}'
On success, you get the same screening response format as a subscription-based call (see Screen Transaction above).
Security & rate limits
| Protection | Detail |
|---|---|
| Replay prevention | Each payment payload is SHA-256 hashed and stored in KV with a 24-hour TTL. Resubmitting the same payment returns 400 Payment already used. |
| Per-IP rate limit | 60 x402 payment attempts per IP per minute. Exceeding returns 429. |
| Facilitator timeout | Payment verification times out after 8 seconds. Settlement (async) times out after 15 seconds. |
| Fail-closed | If the X402_PAY_TO address is not configured, all x402 payments are rejected. If verification fails for any reason, the screen does not execute. |
Error responses
// 400 — Invalid or missing payment header
{
"error": "Invalid PAYMENT-SIGNATURE header — could not decode"
}
// 400 — Replay attempt
{
"error": "Payment already used — each x402 payment is single-use"
}
// 402 — Payment verification failed
{
"error": "payment_invalid",
"detail": "Payment verification failed",
"x402": { "x402Version": 2, "resource": {...}, "accepts": [...] }
}
// 429 — x402 rate limit exceeded
{
"error": "Too many x402 payment attempts — wait 60 seconds"
}
Audit trail
x402 screens are logged identically to subscription screens. The audit record includes an x402Network field and the operator is recorded as x402:anonymous. Settlement status is logged server-side for reconciliation.
Create Fleet
Create a fleet spend envelope under an operator. Agents can be assigned to a fleet, which enforces an aggregate daily spend limit across all agents in the fleet. Useful for organizations running multiple agents under a shared budget.
curl -X POST https://api.clearagent.dev/v1/fleets/create \
-H "Authorization: Bearer <your-KYAOperatorCredential-JWT>" \
-H "Content-Type: application/json" \
-d '{
"fleetName": "Finance Operations Fleet",
"operatorLegalName": "Acme Corp",
"dailyLimitUSDC": 200000
}'
Create Delegate
Mint a child agent credential from a parent agent credential. The child credential inherits the parent's policy and can only narrow it — it cannot expand spend limits or add chains beyond what the parent allows. Delegation depth is capped at 3 levels.
Register User
Issue a 30-day individual user credential. Valid for supervised agent contexts only — autonomous agents cannot be assigned user credentials. User credentials are stored under user:{userId} in KV with a 31-day TTL.
Human Attestation
Record a human heartbeat re-attestation for an autonomous agent. Autonomous agents must be re-attested every 24 hours. If attestation is stale, transactions over $10,000 are automatically escalated to REVIEW. Attestation records are stored under attest:{agentId} with a 25-hour TTL.
curl -X POST https://api.clearagent.dev/v1/agents/agt_Xk9mQ2Rz/attest \
-H "Authorization: Bearer <your-KYAOperatorCredential-JWT>" \
-H "Content-Type: application/json" \
-d '{
"attestedBy": "[email protected]",
"note": "Routine daily attestation"
}'
Screening Context (priorScreenings)
When a screen returns REVIEW, the response carries a priorScreenings object so whoever dispositions it has history before deciding — instead of judging a counterparty in isolation. It is present on REVIEW verdicts only (CLEAR and BLOCK omit it to avoid noise), and appears in the POST /v1/screen, GET /v1/audit/:txnId, and POST /v1/reviews/:txnId responses. The current transaction is excluded — counts reflect strictly prior screenings.
{
"verdict": "REVIEW",
"priorScreenings": {
"counterparty": {
"totalScreenings": 14,
"firstSeen": "2026-05-12T08:30:00Z",
"lastSeen": "2026-07-10T14:22:00Z",
"verdictHistory": { "CLEAR": 12, "REVIEW": 2, "BLOCK": 0 },
"distinctAgents": 3,
"distinctOperators": 2
},
"agent": {
"totalScreenings": 89,
"firstSeen": "2026-04-01T10:00:00Z",
"reviewRate": 0.04,
"blockRate": 0.0,
"avgDailyScreenings": 3.2,
"todayScreenings": 7
}
}
}
| Field | Meaning |
|---|---|
| counterparty.totalScreenings | Prior screenings matching this counterparty by name or wallet. |
| counterparty.verdictHistory | Prior verdict counts: { CLEAR, REVIEW, BLOCK }. A first-time counterparty with a clean history reads differently from one with prior REVIEWs. |
| counterparty.distinctAgents / distinctOperators | How many distinct agents / operators have paid this counterparty — breadth of exposure. |
| agent.reviewRate / blockRate | Fraction of this agent's prior screenings that were REVIEW / BLOCK. A normally-clean agent hitting REVIEW is a weaker signal than a repeat offender. |
| agent.avgDailyScreenings / todayScreenings | Baseline daily volume vs. today's count — a velocity cue. |
null. A first-ever counterparty returns counterparty: null; the whole object is omitted if there is no history at all. Agent history is forward-looking — it counts screenings recorded after this feature shipped (July 2026); older screenings did not capture the agent identifier.Review Disposition
Post a BSA officer's disposition on a transaction that received a REVIEW verdict. This is the human-in-the-loop endpoint: when the screening engine flags a transaction for review (spend policy, velocity anomaly, model change, etc.), a compliance officer calls this endpoint to record their decision.
Path Parameters
| Name | Type | Required | Description |
|---|---|---|---|
txnId | string | Yes | Transaction ID from the /v1/screen response (format: txn_XXXXXXXX) |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
disposition | string | Yes | APPROVED | REJECTED | ESCALATE_SAR |
reviewedBy | string | Yes | Reviewer name or employee ID (BSA audit trail) |
notes | string | No | Free-text rationale for the decision |
Example Request
curl -X POST https://api.clearagent.dev/v1/reviews/txn_a1b2c3d4 \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{
"disposition": "APPROVED",
"reviewedBy": "J. Smith, BSA Officer",
"notes": "Reviewed velocity flag — within normal operating pattern for this agent."
}'
Response (200)
Returns the full updated audit record with disposition fields appended:
{
"txnId": "txn_a1b2c3d4",
"verdict": "REVIEW",
"ruleTriggered": "VELOCITY_SPIKE",
"reason": "Transaction velocity exceeded 3x rolling average",
"disposition": "APPROVED",
"reviewedBy": "J. Smith, BSA Officer",
"reviewedAt": "2026-03-15T14:22:01Z",
"notes": "Reviewed velocity flag — within normal operating pattern for this agent.",
"sarEscalated": false,
...
}
Dispositions
| Value | Meaning | Effect |
|---|---|---|
APPROVED | Transaction cleared by reviewer | Audit record updated. Payment can proceed. |
REJECTED | Transaction denied by reviewer | Audit record updated. Payment should not proceed. |
ESCALATE_SAR | Suspicious activity — SAR candidate | Sets sarEscalated: true on the audit record. Route to SAR filing workflow. |
Error Responses
| Status | Condition |
|---|---|
| 400 | Transaction verdict is not REVIEW (CLEAR/BLOCK/UNKNOWN cannot be dispositioned) |
| 404 | Transaction ID not found (expired or never written) |
| 409 | Disposition already recorded (idempotency guard — prevents double-disposition) |
| 422 | Invalid disposition value or missing reviewedBy |
Update Operator Wallet
Update the wallet address on your operator record after registration. The new wallet is OFAC-screened before it is saved — a match returns 403 and the update is rejected. Authenticated as the operator (KYAOperatorCredential bearer token).
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| wallet | string | required | New operator wallet. EVM (0x+40 hex), Solana (base58), Tron (T…), or Sei (sei1…). Screened against OFAC SDN before saving. |
curl -X PATCH https://api.clearagent.dev/v1/operators/me \
-H "Authorization: Bearer <operatorToken>" \
-H "Content-Type: application/json" \
-d '{ "wallet": "0xYourNewWalletAddress" }'
Update Agent Spend Policy
Change an agent's spend policy without re-issuing its credential. Merge semantics: an omitted field keeps its current value; an explicit null clears it. The operator must own the agent. Fields match the spendPolicy object in Register Agent.
| Field | Type | Description |
|---|---|---|
| maxSingleTxUSDC | number | Per-transaction ceiling. Above this ⇒ REVIEW. |
| dailyLimitUSDC | number | Cumulative daily ceiling (atomic, enforced per UTC day). |
| allowedChains | string[] | Permitted chains, upper-cased. |
| allowedPaymentRails | string[] | Permitted TradFi rails. |
curl -X PATCH https://api.clearagent.dev/v1/agents/agt_Xk9mQ2Rz/spend-policy \
-H "Authorization: Bearer <operatorToken>" \
-H "Content-Type: application/json" \
-d '{ "dailyLimitUSDC": 100000, "allowedChains": ["ETH","BASE"] }'
Get Audit Record
Retrieve a tamper-evident audit record by its txnId. Every /v1/screen call generates a txnId (format: txn_XXXXXXXX) and writes the full result plus request inputs to KV. Records are retained for 90 days.
curl https://api.clearagent.dev/v1/audit/txn_A8bKmQ3x \
-H "Authorization: Bearer <your-KYAOperatorCredential-JWT>"
{
"txnId": "txn_A8bKmQ3x",
"verdict": "CLEAR",
"riskScore": 2,
"reason": "All checks passed — transaction approved",
"ruleTriggered": null,
"agentId": "agt_Xk9mQ2Rz",
"agentName": "PayrollBot v2",
"credentialStatus": "ACTIVE",
"screenedAt": "2026-03-11T14:23:01Z",
"responseTimeMs": 38,
"_request": {
"amount": 4500,
"chain": "ETH",
"cpWallet": "0xRecipient...",
"cpName": "Contractor LLC",
"rail": "USDC"
}
}
/v1/screen response includes the full verdict and audit fields inline — most integrations persist this at call time. Export monthly to your compliance archive for BSA retention. A bulk export endpoint (GET /v1/audit/export) is on the near-term roadmap for batch retrieval.
Get Re-Screening Alerts
Returns alerts raised by the daily counterparty re-scrub. Previously screened counterparties are re-checked on a schedule; if a prior result changes (e.g. a new sanctions or PEP hit appears), an alert is recorded here with a 90-day retention. This surfaces a counterparty that was clean at screening time but flagged afterward, without you re-submitting it. Alerts start in PENDING_REVIEW status.
curl https://api.clearagent.dev/v1/alerts \
-H "Authorization: Bearer <operatorToken>"
{
"count": 1,
"alerts": [
{
"alertId": "alert_a1b2c3",
"counterparty": "Example Entity",
"wallet": "0x…",
"previousResult": "CLEAR",
"newResult": {
"pepHits": [],
"sanctionHits": [ { "list": "eu_fsf", "score": 0.91 } ],
"adverseMediaHits": []
},
"detectedAt": "2026-07-22T02:00:31.000Z",
"status": "PENDING_REVIEW"
}
]
}
{ "count": 0, "alerts": [] }.Watchlist Coverage & Scope
CLEARAGENT screens against three distinct layers. They are reported as separate signals because they carry different legal weight — collapsing them would imply obligations that may not apply.
| Layer | Signal | Verdict on match | Source |
|---|---|---|---|
| OFAC SDN (US) | ofac | BLOCK | US Treasury, synced nightly |
| UN / EU / UK | globalSanctions | REVIEW | UN Security Council Consolidated List, EU Consolidated Financial Sanctions List, UK OFSI Consolidated List — synced nightly |
| Senior public officials | publicOfficial | REVIEW | Current US federal legislators (public domain) |
Matching behaviour
OFAC name screening uses exact, substring, Levenshtein and homoglyph matching to catch evasion. The UN/EU/UK and public-official layers use exact normalised matching only. Those lists total roughly 40,000 names including many common personal names and single-word aliases; substring or fuzzy matching across them would generate false positives on ordinary counterparties. Exact matching still produces occasional reviews on short single-word aliases — acceptable because the outcome is REVIEW, not a blocked payment.
Live counts and sync timestamps for every list are published at GET /v1/health.
Signed Screening Receipts
Every POST /v1/screen response includes a receipt field: a compact ES256 JWS restating the compliance-material fields of the verdict. It lets a counterparty, auditor, or examiner confirm what CLEARAGENT decided without an API key and without trusting this service — the signature verifies offline against the public key already published at /v1/.well-known/jwks.json.
Receipt claims
| Claim | Description |
|---|---|
| iss | Issuer DID — did:kya:clearagent-alpha |
| txnId | Screening transaction ID this receipt attests to |
| verdict | CLEAR / REVIEW / BLOCK / UNKNOWN |
| riskScore | Weighted score at time of screening |
| ruleTriggered | Rule that determined the verdict, or null |
| signals.globalSanctions | UN / EU / UK screen: HIT, CLEAR, or NOT_SCREENED |
| signals.publicOfficial | Senior public official match: MATCH, CLEAR, or NOT_SCREENED |
| signals.agentScore | Behavioural risk heuristic for the agent: { "score": 0–100, "grade": "A"–"F" } |
| signals.erc8004 | ERC-8004 on-chain identity anchoring status. Currently PENDING (roadmap). |
| signals | Per-signal breakdown (ofac, pep, adverseMedia, spendPolicy, credential) |
| authorizedBy | For supervised-mode screens, the human user credential that authorised the agent ({ userId, userLegalName, userRole }). null for autonomous agents. |
| priorScreenings | REVIEW verdicts only. Screening history for the reviewer — see Screening Context. Absent on CLEAR/BLOCK. |
| agentId | Agent credential the screening was performed under |
| screenedAt | ISO 8601 UTC timestamp |
| requestHash | SHA-256 binding the receipt to what was screened — see below |
What the receipt is bound to
requestHash ties the receipt to the specific transaction screened, so it proves more than "a verdict was issued for this txnId" — it proves which counterparty, amount and chain that verdict covers. The inputs are hashed rather than embedded, so counterparty identifiers never appear in a document you hand to a third party.
Recompute it by canonicalising the request and taking SHA-256:
// cpName: trim, lowercase, collapse internal whitespace
// cpWallet: trim, lowercase
// amount: String(Number(amount) || 0)
// chain: trim, uppercase
const canonical = `${cpName}|${cpWallet}|${amount}|${chain}`;
const digest = await crypto.subtle.digest('SHA-256', new TextEncoder().encode(canonical));
const requestHash = [...new Uint8Array(digest)].map(b => b.toString(16).padStart(2,'0')).join('');
// must equal payload.requestHash
The JWS header carries alg: ES256, typ: kya-receipt+jwt, and the kid identifying which published key signed it. Verifiers should reject any receipt whose alg or typ differs — this prevents algorithm-confusion attacks and stops a credential JWT being replayed as a receipt.
Verify offline (recommended)
Any standard JOSE library works. This is the real trust path — nothing below contacts CLEARAGENT except to fetch the public key:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const JWKS = createRemoteJWKSet(
new URL('https://api.clearagent.dev/v1/.well-known/jwks.json')
);
const { payload, protectedHeader } = await jwtVerify(receipt, JWKS, {
issuer: 'did:kya:clearagent-alpha',
});
if (protectedHeader.typ !== 'kya-receipt+jwt') throw new Error('not a receipt');
console.log(payload.verdict, payload.txnId, payload.screenedAt);
Verify via API (convenience)
A hosted convenience check. No authentication — the whole point of a receipt is that anyone can validate it.
curl -X POST https://api.clearagent.dev/v1/receipts/verify \
-H "Content-Type: application/json" \
-d '{"receipt": "eyJhbGciOiJFUzI1NiIsInR5cCI6Imt5YS1yZWNlaXB0K2p3dCJ9..."}'
{
"valid": true,
"kid": "cc-mmkpk80t",
"claims": {
"iss": "did:kya:clearagent-alpha",
"txnId": "txn_6dhjWsYR",
"verdict": "CLEAR",
"riskScore": 0,
"screenedAt": "2026-07-22T10:17:21.356Z"
}
}
A tampered receipt returns {"valid": false, "reason": "Signature does not verify against the published key"}. A ready-made offline verifier ships in the repo at scripts/verify-receipt.mjs (exit 0 = valid, 1 = invalid, suitable for CI or an audit pipeline).
Arc On-Chain Attestation
Attestation status progresses pending → broadcast → confirmed. It reaches confirmed only once a receipt with on-chain status 0x1 has been observed; a transaction that reverts is marked reverted and re-queued. Broadcasting is deliberately not treated as success. Read a hash-only compliance attestation from the ClearAgentAttestation contract on Arc testnet. Attestations are written automatically after each screening when Arc attestation is enabled. No PII is stored on-chain — only an opaque hash, verdict enum, and timestamp.
Example request
curl https://api.clearagent.dev/v1/attestation/arc/txn_A8bKmQ3x
Example response
{
"txnId": "txn_A8bKmQ3x",
"txnIdHash": "0x8a3f...",
"hash": "0x4b2c...",
"verdict": "CLEAR",
"status": "confirmed",
"blockNumber": 53109187,
"timestamp": 1721347200,
"screenedAt": "2026-07-19T00:00:00.000Z",
"chain": "ARC",
"chainId": 5042002,
"contract": "0xE27Eded092B8048eaF81482e8A5f2eb3E9891EF7",
"explorer": "https://testnet.arcscan.app/address/0xE27Eded092B8048eaF81482e8A5f2eb3E9891EF7",
"network": "testnet"
}
Get Billing Usage
Returns the operator's current plan, monthly usage, remaining screens, and reset date.
curl https://api.clearagent.dev/v1/billing/usage \
-H "Authorization: Bearer <your-KYAOperatorCredential-JWT>"
{
"plan": "starter",
"planName": "Starter",
"limit": 100,
"used": 23,
"remaining": 77,
"resetsAt": "2026-04-01T00:00:00.000Z",
"subscriptionStatus": "active"
}
/v1/screen response includes X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset headers so agents can track budget inline.
"plan": "free" for 25 screens/month at no cost, or call POST /v1/billing/checkout for paid plans.
Get API Keys
Retrieve the operator and agent tokens for an operator whose plan is active. Returns 402 Payment required if the operator has not yet completed checkout. Used by the billing flow to release credentials once payment is confirmed.
curl https://api.clearagent.dev/v1/billing/keys \
-H "Authorization: Bearer <operatorToken>"
Create Checkout Session
Creates a Stripe Checkout session for paid plans. Pass "plan": "starter" for Starter ($5/month, 100 screens) or "plan": "pro" for Pro ($49/month, 10,000 screens). Free plan operators (25 screens/month) do not need a checkout session.
Request body
| Field | Type | Required | Description |
|---|---|---|---|
| plan | string | No | "starter" (default for new operators) or "pro". |
| successUrl | string | No | URL to redirect after successful payment. Defaults to https://clearagent.dev?checkout=success. |
| cancelUrl | string | No | URL to redirect if the operator cancels. Defaults to https://clearagent.dev?checkout=canceled. |
# New operator — start with Starter
curl -X POST https://api.clearagent.dev/v1/billing/checkout \
-H "Authorization: Bearer <your-KYAOperatorCredential-JWT>" \
-H "Content-Type: application/json" \
-d '{}'
# Upgrade to Pro
curl -X POST https://api.clearagent.dev/v1/billing/checkout \
-H "Authorization: Bearer <your-KYAOperatorCredential-JWT>" \
-H "Content-Type: application/json" \
-d '{"plan": "pro"}'
{
"checkoutUrl": "https://checkout.stripe.com/c/pay/cs_live_...",
"sessionId": "cs_live_..."
}
Billing Portal
Creates a Stripe Billing Portal session for operators to manage their subscription (update payment method, cancel, view invoices). Only available for operators with an active Stripe subscription.
curl -X POST https://api.clearagent.dev/v1/billing/portal \
-H "Authorization: Bearer <your-KYAOperatorCredential-JWT>" \
-H "Content-Type: application/json" \
-d '{"returnUrl": "https://example.com/settings"}'
{
"url": "https://billing.stripe.com/p/session/..."
}
400 if the operator has no Stripe customer ID (still inactive).
Health Check
Returns API version, OFAC list status, KV connectivity, and signing key state. No authentication required.
curl https://api.clearagent.dev/v1/health
{
"status": "ok",
"version": "0.4.0",
"ofac": {
"source": "kv:live",
"count": 77,
"syncedAt": "2026-03-11T02:00:14Z"
},
"kv": "connected",
"signingKey": "production"
}
JWKS Public Key
Returns the P-256 public key in standard JWKS format. Use this endpoint to independently verify any KYA credential JWT without calling the CLEARAGENT API — any standard JWT library that supports ES256 can verify the signature.
curl https://api.clearagent.dev/v1/.well-known/jwks.json
{
"keys": [{
"kty": "EC",
"crv": "P-256",
"use": "sig",
"alg": "ES256",
"kid": "cc-v1",
"x": "...",
"y": "..."
}]
}
@clearagent/mpp
OFAC sanctions screening for the Machine Payments Protocol (Stripe/Tempo). Wraps any Fetch-compatible function to automatically screen 402 payment challenges before your agent authorizes payment.
npm install @clearagent/mpp
import { withKYAScreening } from "@clearagent/mpp";
const safeFetch = withKYAScreening(fetch, {
operatorToken: process.env.CLEARAGENT_OPERATOR_TOKEN,
agentToken: process.env.CLEARAGENT_AGENT_TOKEN,
});
// Every 402 is screened. OFAC-sanctioned recipients are blocked
// before funds leave your wallet.
const res = await safeFetch("https://openai.mpp.tempo.xyz/v1/chat/completions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ model: "gpt-4o", messages: [...] }),
});
Also exports screenMPPChallenge() for standalone screening and withKYAScreeningFromEnv() for environment variable configuration. See npm package for full API.
@clearagent/x402
Compliance hook for x402 payment servers. Screens every payment request against ClearAgent before processing on Base mainnet with confirmed settlement.
npm install @clearagent/x402
import { kyaComplianceHook } from "@clearagent/x402";
const httpServer = new x402HTTPResourceServer(resourceServer, routes)
.onProtectedRequest(kyaComplianceHook({
apiKey: process.env.CLEARAGENT_API_KEY,
}));
See npm package for full API, REVIEW handling options, and fail-open/fail-closed configuration. x402 settlement is confirmed working on Base mainnet.