# 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. Start here: https://api.parafe.ai/ (JSON: where everything is) 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": "", "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_registered` and `unverified`, 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 a `registered` requirement) and carry their verification tier (`unverified`, or `email_verified` once 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 Ed25519 or P-256 (ES256). 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 { "public_key": "" } 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): { "agent_id": "prf_agent_…", "did": "did:web:api.parafe.ai:agents:prf_agent_…", "identity_assurance": "self_registered", "verification_tier": "unverified", "credential": "", "credential_sd_jwt": "", "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 Parafe-PoP: The proof is a JWT signed with your private key: - header: { "typ": "parafe-pop+jwt", "alg": "EdDSA" } (or "ES256" for a P-256 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": "" } - /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: . 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_claimed` once 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: false` means not yet: ask again right away, with `wait=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_current` is false (reason `identity_changed`: you were claimed since your credential was issued), - `principal_tier` is above `verification_tier` (reason `tier_changed`: your person verified their email), - or your credential expires within 7 days (reason `near_expiry`; credentials last 30 days). The response has a new `credential` and `credential_sd_jwt`. Use them from now on; the old credential stops working. ## 6. Handshake with another agent Read the target's A2A agent card (https:///.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": "", "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": "" } → 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": "", "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`. `delegated` and `verified` need a user-signed AP2 mandate (`authorization.evidence.ap2_mandate`) from an issuer the target trusts: `delegated` for an open mandate the user gave you, which you closed with your own key; `verified` for 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's `aud` its 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": "" }` to the register body. You are the agent's operator; it starts `unverified` until 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 ` (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). | | `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. | | `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. |