Parafé · Docs · Same guide as plain text: parafe.ai/llms.txt
Parafé
Parafé is a neutral trust broker for agent-to-agent interactions. It gives an AI agent a verifiable identity, runs mutual authentication between two agents, issues scoped consent tokens, and signs receipts of what happened. This file tells an AI agent how to get a Parafé identity on its own, get it claimed by the person it acts for, keep it renewed, and use it in a handshake.
This guide as a web page: https://parafe.ai/docs/agents
Start here: https://api.parafe.ai/ (JSON: where everything is; a web page in a browser)
Broker API: https://api.parafe.ai (OpenAPI: https://api.parafe.ai/openapi.json)
Portal (for people): https://platform.parafe.ai
A2A extension spec: https://parafe.ai/extensions/a2a/v2
Reference (every field on accounts, organizations, agents and passes, in plain language): https://parafe.ai/docs/reference
SDK (Node/TypeScript): https://www.npmjs.com/package/@getparafe/sdk (covers everything below; waitForClaim() waits for step 4)
Every API error is JSON: { "error": "<code>", "message": "…" }. The table at the end says what to do for each code.
Before you start
- You need no API key. Without one you register yourself: you start
self_registeredandunverified, with no operator (who runs you) or principal (who you act for) on record. - The person you act for makes you trustworthy by claiming you. After that you are
claimed(it meets aregisteredrequirement) and carry their verification tier (unverified, oremail_verifiedonce they verify their email). - Keep your private key secret. Never send it anywhere, never show it to the person, never put it in a prompt or a log. Your credential may be shown; the key may not.
1. Make a key pair
P-256 (ES256) is recommended: it is the only type that can sign AP2 payment receipts, which shops may ask for. Ed25519 is also accepted. Keep the private key. Send the public key as base64 SPKI (DER) or as a public JWK.
2. Register
POST https://api.parafe.ai/agents/register Content-Type: application/json Parafe-PoP: <proof JWT signed with your new private key>
{ "public_key": "<base64 SPKI or JWK>" }
The proof shows you hold the key you're registering (nobody can register someone else's key). It's the same kind of proof as in step 3: header { "typ": "parafe-pop+jwt", "alg": "ES256" } (or "EdDSA" for Ed25519), claims "htm": "POST", "htu": "https://api.parafe.ai/agents/register", "iat" (now), "jti" (random, new each time). The SDK and MCP server sign it for you.
That is all you need. Your public name is your agent_id. Optional:
agent_name: what you call yourself (3 to 100 characters, lowercase letters, digits and hyphens).principal_name: who you say you act for (at most 100 characters).
Both are shown only to the person on the claim page, marked as what you said. They are not verified, not public, and not in your credential. So the 201 response shows agent_name as your agent_id and principal_name as null: that's expected, what you sent is kept for the claim page.
201 response (store all of it). If this key already has an agent you registered without an API key, you get that agent back instead (200, "existing": true, with its pending claim link). A key another agent uses is refused (409 key_in_use), and so is a revoked agent's key (409 key_revoked): make a new key pair.
{
"agent_id": "prf_agent_…",
"did": "did:web:api.parafe.ai:agents:prf_agent_…",
"identity_assurance": "self_registered",
"verification_tier": "unverified",
"credential": "<JWT>",
"credential_sd_jwt": "<SD-JWT VC>",
"expires_at": "…",
"claim": { "claim_url": "https://platform.parafe.ai/claim?code=7KQ2-M9XD-4H", "code": "7KQ2-M9XD-4H", "expires_at": "…" }
}
3. Authenticate as yourself: credential plus proof of possession
Every request you make as the agent sends two headers:
Authorization: Bearer <credential> Parafe-PoP: <proof JWT>
The proof is a JWT signed with your private key:
- header: { "typ": "parafe-pop+jwt", "alg": "ES256" } (or "EdDSA" for an Ed25519 key)
- claims: "htm" (HTTP method), "htu" (the full request URL, no query), "iat" (now, seconds), "jti" (random, 16+ characters, new for every request), plus the route's claims:
- /agents/{agent_id}/claim-link, /claim-status, /renew, /revoke, and PUT /scope-policies: { "agent_id": "<your agent_id>" }
- /handshake/initiate: { "target_agent_id": "…", "requested_scope": "…" } (and "session_id" when escalating)
- /session/close, /sessions/{id}/action-receipts, /sessions/{id}/receipt: { "session_id": "…" }
A proof is valid for 5 minutes and only once.
4. Get claimed by the person you act for
Show the person the claim_url and tell them its code, for example: "To let shops trust me with orders, please open this link and approve me: <claim_url>. The page will show the code 7KQ2-M9XD-4H." They sign in to Parafé (or create an account), check that the page shows the same code, and approve or reject. The page says "Platform: Unknown (self-registered)" (Parafé names a platform only when it authenticated it) and shows what you said about yourself. The person can give you a name that only they see.
- The link works once and expires after 30 minutes.
- New link (replaces the previous one): POST https://api.parafe.ai/agents/{agent_id}/claim-link → 201 { "claim_url", "code", "expires_at" }. Each link has its own code. 409
already_claimedonce a person has claimed you. - You tell the person the link's code; never ask them for a password, a code, or anything else. The approval happens in their browser.
Then wait for the approval. Ask, and the broker answers the moment they approve (or after wait seconds):
GET https://api.parafe.ai/agents/{agent_id}/claim-status?wait=25 (credential + a new proof each time)
→ { "claimed": true, "identity_assurance": "claimed", "verification_tier": "unverified", "principal_tier": "unverified", "credential_current": false, "principal_type": "personal", "operator_type": null, … }
claimed: falsemeans not yet: ask again right away, withwait=25(up to 60). Stop after 30 minutes, when the link expires; get a new link and show it to the person.- When the person tells you they approved, ask once (no
wait) to confirm. - Don't ask every few seconds without
wait: it's many more requests for the same answer. - With the SDK:
await parafe.waitForClaim().
Your new identity counts in handshakes from the moment they approve. Renew next (step 5): credential_current is false after a claim.
5. Renew
POST https://api.parafe.ai/agents/{agent_id}/renew (credential + proof, no body)
Renew when:
credential_currentis false (reasonidentity_changed: you were claimed since your credential was issued),principal_tieris aboveverification_tier(reasontier_changed: your person verified their email),- or your credential expires within 7 days (reason
near_expiry; credentials last 30 days).
If your credential has already expired (less than a year ago), renew anyway with the expired credential and a proof (reason expired): renewal is the one thing an expired credential can still do.
The response has a new credential and credential_sd_jwt. Use them from now on, and save them where you keep your identity: the old credential stops working.
6. Handshake with another agent
Read the target's A2A agent card (https://<domain>/.well-known/agent-card.json) for its Parafé agent_id and each scope's requirements. Then:
POST https://api.parafe.ai/handshake/initiate (proof claims: target_agent_id, requested_scope)
{ "initiator_credential": "<credential>", "target_agent_id": "prf_agent_…", "requested_scope": "place-order",
"authorization": { "modality": "autonomous" } }
Send the returned challenge to the target as described in the A2A extension spec. The target completes the handshake and you get a consent token for the scope.
If the target requires more than you have, the broker answers 403 with a code (table at the end). For identity_insufficient or tier_insufficient, while no person has claimed you, that answer includes:
{ "claim": { "claim_url": "…", "code": "…", "expires_at": "…" }, "hint": "Ask the person you act for to open this link to verify you, and tell them its code." }
Pass the link to your person, wait for approval (step 4), renew if needed (step 5), and try again. If a person already claimed you and you are refused for tier, your person needs to verify their email (https://platform.parafe.ai/verification).
For authorization_insufficient, the answer says what the scope needs: required_modality, provided_modality and a hint; for delegated and verified, also trusted_issuers (the mandate issuers the target trusts) and mandate_requirements (what the mandate must name). Never claim a modality you can't back: attested means the person you act for asked for this; delegated and verified need a mandate they signed.
7. Receipt what you do
When the other agent returns action receipts for what it did or refused for you (action_receipts in its reply, A2A extension spec section 4.5), file your copies so it is on the session's record even if that agent doesn't:
POST https://api.parafe.ai/sessions/{session_id}/action-receipts (credential + proof, claims: session_id)
{ "receipt": "<one action receipt JWS, exactly as received>" }
→ 201 { "seq", "acknowledgment", … }, or 409 `duplicate_receipt` with the same acknowledgment if it is already filed.
When you are the one acting, sign your own action receipt (spec section 5.2) and file it the same way. File before anyone closes the session: the session receipt lists every filed receipt. The @getparafe/sdk does both: recordActionReceipt(), fileActionReceipt().
8. Close the session and keep the receipt
When you're done, close the session. Either participant can:
POST https://api.parafe.ai/session/close (credential + proof, claims: session_id)
{ "session_id": "sess_…" }
→ 200 { "format_version": 2, "receipt_id", "session_id", "receipt": "<JWS>", "claims": { … } }
The receipt is a JWS the broker signed: who took part, what was consented to, and every filed action receipt. Keep it, and give it to your person if they ask what happened. Verify it with the broker keys (kid in its header). Either participant can fetch it again later:
GET https://api.parafe.ai/sessions/{session_id}/receipt (credential + proof, claims: session_id)
Reference
- Identity assurance, weakest to strongest:
self_registered<registered=claimed. - Verification tiers:
unverified<email_verified<domain_verified<org_verified. - Authorization modalities:
autonomous<attested<delegated<verified.delegatedandverifiedneed a user-signed AP2 mandate (authorization.evidence.ap2_mandate) from an issuer the target trusts:delegatedfor an open mandate the user gave you, which you closed with your own key;verifiedfor one the user signed for this exact purchase. Each mandate works once. The mandate's merchant (or payee) must be the target's agent ID or DID (or a website on its verified domain), and a hop'saudits DID or agent ID; the mandate must be under 5 minutes old when you present it. If the target gives you a nonce (e.g. its quote ID), put it in the mandate: the target checks it. Merchants need a P-256 key to sign AP2 receipts. - The person can revoke you at any time from the portal. If a platform runs you (it is your operator), the person can disconnect you instead: you go back to acting for the platform's reference, unverified.
- Platforms registering their users' agents: with your API key, add
"acts_for": { "ref": "<your opaque user ID>" }to the register body. You are the agent's operator; it startsunverifieduntil the person claims it. Don't use emails as refs (@is refused): counterparties see the ref in consent tokens and receipts. - Broker keys (to verify anything Parafé signed): https://api.parafe.ai/.well-known/jwks.json
- The public registry (https://api.parafe.ai/registry/agents) lists you once a person has claimed you. Before that, anyone with your agent_id can still look you up.
What to do when refused
| Code | What it means | What to do |
|---|---|---|
unauthorized | No credential sent | Send Authorization: Bearer <credential> (step 3). |
invalid_credential | Credential expired, revoked or not yours | Renew (step 5); if renewal is refused too, you may have been revoked: register again and get claimed. |
proof_required | No Parafe-PoP proof | Sign one for this request (step 3; for registration, step 2). |
proof_invalid | The proof doesn't match the request | Check htm, htu (full URL, no query), iat (now), the route's claims, and that you signed with your registered key. |
proof_replayed | That proof was already used | Sign a new proof (new jti) for every request. |
agent_inactive | You were revoked or suspended | Stop. Tell your person; they can register a new agent. |
key_in_use | Another agent already uses this key | Make a new key pair and register again. |
key_revoked | This key belonged to a revoked agent | Make a new key pair and register again. |
already_claimed | A person already claimed you | Nothing to do: check claim-status. |
identity_insufficient | The scope needs a stronger identity | Show your person the claim link in the answer (step 4). |
tier_insufficient | The scope needs a more verified person | Not claimed: the claim link. Claimed: your person verifies their email, then you renew. |
authorization_insufficient | The scope needs a stronger authorization | Read required_modality, hint, trusted_issuers, mandate_requirements (step 6). |
initiator_proof_insufficient | The scope needs a proof of possession | Send the Parafe-PoP header with the handshake. |
tenure_insufficient, completion_rate_insufficient, denied_requests_exceeded, counterparties_insufficient, handshake_success_rate_insufficient | Your track record is below the scope's floor | Use a scope with lower requirements, or build history first. The answer has required and actual. |
permissions_not_in_scope | You asked for permissions the scope doesn't grant | Ask only for scope_permissions from the answer. |
scope_not_found | The target doesn't offer that scope | Read its agent card or GET /agents/{id}/scope-policies. |
no_trusted_issuers | The target trusts no mandate issuer for this scope | You can't get delegated or verified here; tell your person. |
mandate_invalid, mandate_mode_mismatch, mandate_payee_mismatch, mandate_agent_mismatch, mandate_signed_by_agent | The mandate doesn't fit | Read message (and reason); get a new mandate from your person that meets mandate_requirements. |
mandate_already_redeemed | That mandate was used | Get a new one. |
expired | The handshake took too long | Start a new handshake. |
not_participant | That session isn't yours | Use your own session_id. |
already_closed | The session is closed | Fetch the receipt (step 8). |
validation_error | The request is malformed | Read message. |
not_found | No such route, agent or session | Check the ID; for routes, start at https://api.parafe.ai/. |
rate_limited / HTTP 429 | Too many requests | Wait, then retry less often. |