Documentation
Guides for every role in the attested payments ecosystem.
Payment Brokers
Implement a payment servicer that processes transactions and writes attestation proofs.
Read guide →App Developers
Integrate payment verification into your ATProtocol application.
Read guide →Recipients
Set up your identity to accept attested payments from supporters.
Read guide →Payers
Understand how your payments are recorded, verified, and portable.
Read guide →Scenarios
Real-world examples showing attested payments in action.
Browse scenarios →Overview
Payment proofs replace centralized payment databases with cryptographic attestation records distributed across ATProtocol repositories. Any application can independently verify a payment—no single platform controls the relationship.
Three-party attestation
Every payment creates records across three independent repositories. The supporter declares intent, the creator confirms receipt, and a broker—which facilitates and witnesses the exchange—writes its own proof. Each record is content-addressed and bound to its repository, preventing replay attacks.
graph LR
subgraph SR["Supporter's Repo"]
PAY["Payment Record
oneTime | recurring | scheduled"]
SIG["signatures: [strongRef, ...]"]
end
subgraph CR["Creator's Repo"]
CP["payment.proof
Attests via CID"]
end
subgraph BR["Broker's Repo"]
BP["payment.proof
Attests via CID"]
end
SIG -- "strongRef" --> CP
SIG -- "strongRef" --> BP
Data Ownership
Each party controls their own records in their own repository. No single entity holds the full picture.
Cryptographic Integrity
CID-based content addressing ensures records cannot be modified after attestation. Change the data, break the hash.
Portable Relationships
Support relationships survive platform changes. Records live on ATProtocol’s network, not in proprietary databases.
Open Broker Model
Any entity can serve as a broker because brokers facilitate payment. A broker might process Stripe transactions, handle peer-to-peer cash, or simply witness a virtual high-five. The role is to facilitate and witness the exchange between payer and recipient.
This spec builds on badge.blue’s CID-first attestation framework. Every attestation CID is computed from the record content, metadata, and the repository DID—meaning a record copied to a different repository automatically invalidates all its attestations.
Public & private proofs. Payment records and proofs can live in a user’s public repository for open verification, or within a Permissioned Data Space for private access. The implementation is effectively identical in both cases—the same attestation mechanics, CID binding, and strongRef signatures apply. The only difference is that the payment servicer additionally manages creation of the permissioned space in the payer’s repository and ensures all parties (creator, broker) have appropriate read access and permissions.
Payment Intent
When a recipient wants to accept a payment, a structured discovery and initiation flow connects payer, recipient, and payment servicer:
- The recipient signals which payment servicers they use by publishing an ordered list of DIDs that have a
#AttestedNetworkservice endpoint in their DID document - The payer’s client resolves these DIDs and presents the available servicers. The payer selects which one to use
- The payer’s client makes an authenticated request (using inter-service authentication) to
network.attested.payment.initiateon the selected servicer, passing the product identifier. The response includes a token and a URL - The payer is directed through a browser to the URL to complete the payment process (entering payment details, confirming terms, etc.)
- The payer’s client polls
network.attested.payment.statuswith the token. The response is either a strongRef pointing to the completed payment record, or a failed status
sequenceDiagram
participant P as Payer's Client
participant R as Recipient
participant S as Payment Servicer
participant B as Browser
P->>R: Resolve DID document
R-->>P: Service endpoints with #AttestedNetwork DIDs
P->>P: Select servicer from list
P->>S: network.attested.payment.initiate (authenticated)
Note right of P: Sends product identifier
S-->>P: { token, url }
P->>B: Redirect payer to URL
B->>S: Complete payment flow
S-->>B: Payment confirmed
P->>S: network.attested.payment.status (token)
S-->>P: { strongRef } or { failed }
Verification
Any application can verify a payment by checking the cryptographic chain:
- Strip the
signaturesarray from the payment record - Prepare attestation metadata—add the repository DID, strip
cidandsignaturefields - Insert metadata as the
$sigfield, serialize to DAG-CBOR - Hash with SHA-256 and wrap as CIDv1 (codec
0x71) - Fetch proof records via
strongRefURIs and confirm CIDs match
Trust Models
Applications can implement different validation strategies:
- Strict Require proofs from both the creator and a specific trusted broker. Highest assurance.
- Creator-Trusted Accept any payment the creator has attested. Simpler, trusts creators to vouch for supporters.
- Federated Accept attestations from a set of trusted brokers. Enables regional verification networks.
Lexicon
Four record types and three XRPC methods define the payment specification. Payment records live in the supporter’s repository and carry a signatures array referencing proof records from creators and brokers.
A one-time payment from a supporter to a creator. Represents a single, non-recurring financial transaction attested by one or more parties.
| Field | Type | Description |
|---|---|---|
| subject | string (did) | DID of the creator receiving payment |
| amount | integer | Payment amount in smallest currency unit (e.g. cents). Min: 1 |
| currency | string | ISO 4217 currency code (e.g. USD, EUR) |
| txnid | string | Unique transaction identifier for deduplication |
| memo | string | Optional note from the supporter. Max 256 chars |
| createdAt | string (datetime) | Timestamp of record creation |
| entitlements | array(com.atproto.repo.strongRef) | Optional list of strong references to records representing any goods or services the payer is entitled to as a result of this payment |
| signatures | array | Attestation entries (inline or com.atproto.repo.strongRef) |
Entitlements. The entitlements field is an optional array of com.atproto.repo.strongRef objects. Each reference points to a record representing a good or service the payer is entitled to as a result of this payment. The referenced records can use any lexicon—they are not defined by this spec. This allows brokers, recipients, or third parties to define their own product or access records and link them directly to the payment that granted them.
A recurring payment commitment. Immutable once created—supporters cancel and create new subscriptions to change terms. Bills on anniversary dates with automatic retry on failure.
| Field | Type | Description |
|---|---|---|
| subject | string (did) | DID of the creator receiving payment |
| amount | integer | Amount per billing period in smallest currency unit. Min: 500, Max: 25000 (monthly equivalent) |
| currency | string | ISO 4217 currency code |
| unit | string | Billing period: monthly | quarterly | semiannual | yearly |
| frequency | integer | Billing frequency multiplier. Default: 1, Min: 1 |
| txnid | string | Unique transaction identifier for deduplication |
| createdAt | string (datetime) | Timestamp of initial subscription creation |
| entitlements | array(com.atproto.repo.strongRef) | Optional list of strong references to records representing any goods or services the payer is entitled to as a result of this payment |
| signatures | array | Attestation entries (inline or com.atproto.repo.strongRef) |
A fixed series of payments. Unlike recurring payments that continue indefinitely, scheduled payments specify a total count and terminate automatically upon completion.
| Field | Type | Description |
|---|---|---|
| subject | string (did) | DID of the creator receiving payment |
| amount | integer | Amount per payment in smallest currency unit |
| currency | string | ISO 4217 currency code |
| unit | string | Interval: monthly | quarterly | semiannual | yearly |
| count | integer | Total number of scheduled payments. Min: 2, Max: 60 |
| txnid | string | Unique transaction identifier for deduplication |
| createdAt | string (datetime) | Timestamp of schedule creation; first payment date |
| entitlements | array(com.atproto.repo.strongRef) | Optional list of strong references to records representing any goods or services the payer is entitled to as a result of this payment |
| signatures | array | Attestation entries (inline or com.atproto.repo.strongRef) |
A remote attestation record stored in the attestor’s repository (creator or broker). Referenced via com.atproto.repo.strongRef in the payment record’s signatures array.
| Field | Type | Description |
|---|---|---|
| cid | string (cid) | Attestation CID computed per the badge.blue spec from the payment record, metadata, and repository DID |
| status | string | Optional status indicator for the attestation |
Look up verified payment records between a payer and recipient. Returns only records whose attestations have been cryptographically verified. Results may include any combination of oneTime, recurring, and scheduled payment records.
Parameters
| Field | Type | Description |
|---|---|---|
| payer | string (did, required) | DID of the payer (supporter) |
| recipient | string (did, required) | DID of the recipient (creator) |
| paymentType | string | Optional filter by payment collection. Must be a full NSID: network.attested.payment.oneTime, network.attested.payment.recurring, or network.attested.payment.scheduled |
| brokers | array(string) (did, optional, repeating) | Optional list of broker DIDs. When provided, only returns payments that have at least one validating signature from an identity in this list |
| entitlements | array(string) (at-uri, optional, repeating) | Optional list of entitlement AT-URIs. When provided, only returns payments whose entitlements array contains one or more of the given references |
Response
| Field | Type | Description |
|---|---|---|
| payments | array(union) | List of verified payment records. Each element is a union of network.attested.payment.oneTime, network.attested.payment.recurring, or network.attested.payment.scheduled |
Begin a payment flow. Called by the payer’s client against the selected payment servicer using inter-service authentication. Returns a token for status polling and a URL to direct the payer through the payment process.
Input
| Field | Type | Description |
|---|---|---|
| product | string (required) | Product identifier for the payment being initiated |
Response
| Field | Type | Description |
|---|---|---|
| token | string | Opaque token used to poll payment status via payment.status |
| url | string (uri) | URL to direct the payer to in a browser to complete the payment |
Check the status of a payment initiated via payment.initiate. Returns either a com.atproto.repo.strongRef pointing to the completed payment record, or a failed status.
Parameters
| Field | Type | Description |
|---|---|---|
| token | string (required) | Token returned from payment.initiate |
Response
| Field | Type | Description |
|---|---|---|
| status | string | pending | completed | failed |
| ref | com.atproto.repo.strongRef | Present when status is completed. Points to the attested payment record in the payer’s repository |
Examples
Complete record examples showing how attested payments work in practice, using badge.blue remote attestations with com.atproto.repo.strongRef entries.
One-time tip with dual attestation
A supporter sends a $25 tip to a creator. Both the creator and broker independently attest the payment by writing proof records to their own repositories.
{ "$type": "network.attested.payment.oneTime", "subject": "did:plc:v5jkrb2oncmnhc7rtqhrdzwi", "amount": 2500, "currency": "USD", "txnid": "01J5K9P3XQHV7WNBCM2G8RFAT", "memo": "Great stream, keep it up!", "createdAt": "2025-07-14T19:22:00.000Z", "signatures": [ { "$type": "com.atproto.repo.strongRef", "uri": "at://did:plc:v5jkrb2oncmnhc7rtqhrdzwi/network.attested.payment.proof/3la7qxz2vbc2s", "cid": "bafyreigyh7s6lqf5n3xke4jt6r2x3mqkzf4wpgicbqhqg5k3vdjn7aomfe" }, { "$type": "com.atproto.repo.strongRef", "uri": "at://did:plc:broker-payments-xyz/network.attested.payment.proof/3la7qy2kbrc2t", "cid": "bafyreih7wwfa3tcoqd2ne5gqkvrulzpfnwjmcc5fsgqjdx4huswnhzaehqu" } ] }
{ "$type": "network.attested.payment.proof", "cid": "bafyreifdw3gy6ef4mcep5forx72cilu6wbsvuajkgsxnluqemkpa5xvzrwu" }
How the CID binds to the repo: The proof’s cid field is computed from the payment record with the supporter’s DID baked into the $sig metadata. If someone copies the payment record to a different repository, recomputing the CID produces a different value—verification fails automatically.
Monthly recurring subscription
A $10/month recurring support commitment. The record is immutable—to change the amount, the supporter cancels and creates a new subscription.
{ "$type": "network.attested.payment.recurring", "subject": "did:plc:v5jkrb2oncmnhc7rtqhrdzwi", "amount": 1000, "currency": "USD", "unit": "monthly", "frequency": 1, "txnid": "01J5KBR4WMHV8XNBDM3G9SFBT", "createdAt": "2025-08-01T00:00:00.000Z", "entitlements": [ { "$type": "com.atproto.repo.strongRef", "uri": "at://did:plc:v5jkrb2oncmnhc7rtqhrdzwi/com.example.product/3miemuswqbyiw", "cid": "bafyreik3xxgb5tcoqd3ne6gqkvrulzpfnwjmcc5fsgqjdx4huswnhzbekaa" } ], "signatures": [ { "$type": "com.atproto.repo.strongRef", "uri": "at://did:plc:v5jkrb2oncmnhc7rtqhrdzwi/network.attested.payment.proof/3lb2rxz3vdc3t", "cid": "bafyreigyh7s6lqf5n3xke4jt6r2x3mqkzf4wpgicbqhqg5k3vdjn7aomfe" }, { "$type": "com.atproto.repo.strongRef", "uri": "at://did:plc:broker-payments-xyz/network.attested.payment.proof/3lb2ry4ldsc3u", "cid": "bafyreih7wwfa3tcoqd2ne5gqkvrulzpfnwjmcc5fsgqjdx4huswnhzaehqu" } ] }
Scheduled payment series
Six monthly payments of $50 each, terminating automatically after the final installment. Useful for project-based sponsorships or fixed commitments.
{ "$type": "network.attested.payment.scheduled", "subject": "did:plc:v5jkrb2oncmnhc7rtqhrdzwi", "amount": 5000, "currency": "USD", "unit": "monthly", "count": 6, "txnid": "01J5KCS5XRHW9YOCEN4H0TGCU", "createdAt": "2025-09-01T00:00:00.000Z", "signatures": [ { "$type": "com.atproto.repo.strongRef", "uri": "at://did:plc:broker-payments-xyz/network.attested.payment.proof/3lc3syz4wec4u", "cid": "bafyreif4xxgb5tcoqd3ne6gqkvrulzpfnwjmcc5fsgqjdx4huswnhzbekru" } ] }
Broker proof record
The broker’s independent attestation, stored in their own repository. This is the record referenced by the second strongRef in the examples above.
{ "$type": "network.attested.payment.proof", "cid": "bafyreih7wwfa3tcoqd2ne5gqkvrulzpfnwjmcc5fsgqjdx4huswnhzaehqu" }
Verification flow
How an application verifies a one-time payment with dual attestation:
sequenceDiagram
participant App as Verifying App
participant S as Supporter's Repo
participant C as Creator's Repo
participant B as Broker's Repo
App->>S: Fetch payment record
S-->>App: payment.oneTime + signatures[]
par Verify creator attestation
App->>C: Fetch proof via strongRef URI
C-->>App: payment.proof (cid)
App->>App: Recompute CID from payment + supporter DID
App->>App: Confirm CID matches proof.cid
and Verify broker attestation
App->>B: Fetch proof via strongRef URI
B-->>App: payment.proof (cid)
App->>App: Recompute CID from payment + supporter DID
App->>App: Confirm CID matches proof.cid
end
App->>App: Payment verified ✓