Identity

The trust foundation the protocol builds on. A participant's identity is self-assigned by publishing a DID document on their own HTTPS domain. The DID is independently strengthened by third-party issued credentials that attest real-world properties of the entity, such as legal entity, licences, or KYC attestations. A DID is a node in the network, allowing participants to authenticate and securely communicate with each other.

Digital Identity — did:webvh

A participant establishes its digital identity using did:webvh — a DID method that resolves to /.well-known/did.json on their HTTPS domain and additionally maintains a cryptographic version log at /.well-known/did.jsonl. The DID includes a Self-Certifying Identifier (SCID) component derived from the genesis document hash, making domain-level tampering detectable without a central registry.

Trust model. Domain control establishes cryptographic identity within the network. The version log provides a tamper-evident history: historical DID states can be verified offline without trusting the live domain at the time of verification. It does not attest the legal entity behind the domain — that is the role of the Attribution layer.

Required @context entries
Context URIPurpose
https://www.w3.org/ns/did/v1Base DID Core vocabulary
https://w3id.org/security/suites/ed25519-2020/v1Ed25519VerificationKey2020 key type for signing
https://w3id.org/security/suites/x25519-2020/v1X25519KeyAgreementKey2020 key type for encryption
https://didcomm.org/messaging/contexts/v2DIDComm v2 service and messaging vocabulary
Required document fields
FieldRequirementNotes
verificationMethod At least one key of type Ed25519VerificationKey2020 Used for signing out-of-band DIDComm messages. Multiple keys supported for rotation.
authentication References to active signing keys from verificationMethod Remove a key ID from this array to revoke it without deleting the key.
keyAgreement Two keys of type X25519KeyAgreementKey2020 #key-agreement - outer encrypted envelope (JWE) for all DIDComm messages and mediator routing key for PII. #pii-key - final-recipient key for IVMS101 messages routed via the #pii-didcomm service entry; held by whichever infrastructure the participant designates for compliance traffic.
service / DIDCommMessaging At least one entry of type DIDCommMessaging serviceEndpoint is an object with uri, accept, and optional routingKeys / recipientKeys. The #pii-didcomm entry uses routingKeys and recipientKeys to route IVMS101 via the mediator forward pattern, ensuring PII is decryptable only by the designated compliance key holder.
service / LinkedVerifiablePresentation Entry with id #whois Points to the participant's publicly discoverable Verifiable Presentation - the machine-readable equivalent of a "who is this entity" record. Used in the Attribution layer for counterparty validation.

Key & infrastructure delegation. Multiple DIDCommMessaging service entries enable a participant to segregate duties across infrastructure boundaries. Each entry's accept array scopes it to a subset of protocols, and recipientKeys binds it to a specific encryption key — allowing one endpoint to natively handle a subset of communication on behalf of the participant while remaining cryptographically excluded from others. A separate entry using the DIDComm mediator forward pattern (with routingKeys and recipientKeys) ensures that PII-carrying messages are decryptable only by the key holder designated for compliance traffic, regardless of which infrastructure receives and forwards the outer envelope.

{ "@context": [ "https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/ed25519-2020/v1", "https://w3id.org/security/suites/x25519-2020/v1", "https://didcomm.org/messaging/contexts/v2" ], "id": "did:webvh:participant.example", "verificationMethod": [{ "id": "did:webvh:participant.example#key-1", "type": "Ed25519VerificationKey2020", "controller": "did:webvh:participant.example", "publicKeyMultibase": "<base58btc-Ed25519-public-key>" }], "authentication": ["did:webvh:participant.example#key-1"], "keyAgreement": [ { "id": "did:webvh:participant.example#key-agreement", "type": "X25519KeyAgreementKey2020", "controller": "did:webvh:participant.example", "publicKeyMultibase": "<base58btc-X25519-public-key>" }, { "id": "did:webvh:participant.example#pii-key", "type": "X25519KeyAgreementKey2020", "controller": "did:webvh:participant.example", "publicKeyMultibase": "<base58btc-X25519-public-key>" } ], "service": [ { "id": "did:webvh:participant.example#didcomm", "type": "DIDCommMessaging", "serviceEndpoint": { "uri": "https://participant.example/didcomm", "accept": [ "didcomm/v2", "otl:application/payment-request", "otl:application/payment-channel" ] } }, { "id": "did:webvh:participant.example#pii-didcomm", "type": "DIDCommMessaging", "serviceEndpoint": { "uri": "https://participant.example/didcomm", "routingKeys": ["did:webvh:participant.example#key-agreement"], "recipientKeys": ["did:webvh:participant.example#pii-key"], "accept": ["otl:application/ivms101"] } }, { "id": "did:webvh:participant.example#whois", "type": "LinkedVerifiablePresentation", "serviceEndpoint": "https://participant.example/.well-known/whois.json" } ] } // Version log: GET https://participant.example/.well-known/did.jsonl // SCID is derived from the genesis document hash and embedded in the DID path
Legal Entity Identity — Verifiable Credentials

The attribution layer links a participant's digital identity to real-world assertions about the entity controlling it. While a did:webvh DID is self-assigned, attributions are issued by trusted third-party Credential Issuers - entities that digitally sign claims and make them verifiable by other network participants. A claim might attest that a DID holder is a licensed bank, a regulated exchange, or a specific legal entity registered with a government authority.

Each network participant independently defines their own trust perimeter - the set of claim types and issuers they choose to recognise. A participant may require counterparties to hold a specific claim before engaging in a protocol interaction. When a counterparty falls outside the trust perimeter, the participant sends an ABORT message to terminate the interaction and signal the reason.

Extensible by design. The credential layer is open-ended — GLEIF is not central to the protocol. The LEI credential is the first of many; any third party can define and issue claim types, and each participant chooses which issuers their trust perimeter recognises. Credentials are optional data enrichment, not protocol gatekeeping.

Claim types
Claim typeStatusDescription
GLEIF Level 1 Supported Legal Entity Identifier (LEI, ISO 17442) - 20-character code linking the DID to a legal entity in the GLEIF Global LEI Index. Attests entity name, registration jurisdiction, and LEI status. Issued by GLEIF-accredited Local Operating Units.
Licensing & Regulatory Status Planned Claims attesting regulatory licences (e.g. VASP registration, banking licence) issued by authorised regulatory bodies or accredited auditors.

whois & Linked Verifiable Presentations. A participant's attributions are discoverable via the #whois service entry in their DID document. This endpoint serves a W3C Verifiable Presentation containing all claims the participant chooses to disclose publicly. Counterparties resolve this presentation during counterparty validation to evaluate the entity without a separate credential exchange. Individual (NCW) participants do not use a #whois endpoint; their entity type is signalled directly via the entityType property in the DID document.

Credential lifecycle. LEI registrations must be renewed annually; the corresponding VC should be re-issued on renewal. Verifiers should treat a credential whose underlying registration has lapsed as equivalent to no credential. Trust decisions remain at the verifier's discretion even for valid credentials.

// LEICredential - W3C VC 1.1 { "@context": ["https://www.w3.org/2018/credentials/v1"], "type": ["VerifiableCredential", "LEICredential"], "issuer": "did:webvh:lei-issuer.gleif.org", "issuanceDate": "2024-01-01T00:00:00Z", "credentialSubject": { "id": "did:webvh:participant.example", "lei": "5493001KJTIIGC8Y1R12", "legalName": "Example Participant Ltd." }, "proof": { "type": "DataIntegrityProof", "cryptosuite": "eddsa-jcs-2022", "verificationMethod": "did:webvh:lei-issuer.gleif.org#key-1" } } // Served at: GET did:webvh:participant.example#whois // → https://participant.example/.well-known/whois.json
Anonymous Participant

Anonymous participants interact with the network without a registered did:webvh identity. They can fetch and cryptographically verify signed data published by network participants — reading DID documents, checking JWS proofs, resolving payment details — and can notify the beneficiary of payment execution (e.g. submitting a transaction hash). They have no registered service endpoint and cannot send authenticated protocol messages; they cannot be reached or identified by the network.

CapabilityAnonymous participantNetwork participant (did:webvh)
Fetch public payment details & channel offers
Verify data integrity (JWS proof field)
Resolve DID documents & counterparty identity
Sign & broadcast on-chain transactions
Receive DIDComm messages
Send authenticated protocol messages
Participate in IVMS101 / counterparty validation exchanges

Examples of anonymous participants:

  • Non-custodial wallet (NCW). A self-custody wallet without a registered did:webvh identity, identified solely by its on-chain address. The Natural Person Identity layer below describes an opt-in extension (currently in design) that can promote an NCW to a first-class network participant; absent that, it participates anonymously.
  • AI agent (MPP otl-pay). An automated agent — LLM assistant, script, or autonomous service — that pays for a resource via the otl-pay protocol. It has no persistent network identity; its wallet's on-chain address is the only identifier.
  • Browser dApp / light client. A web application that reads public payment data and submits transactions via a connected wallet. The dApp itself has no identity.
Natural Person Identity Coming Soon

An opt-in extension that will let natural persons as non-custodial wallet holders participate in compliance flows without giving up self-custody. The detailed design is in progress; the principles below frame the direction.

Privacy by default. Identity claims about a natural person are held and presented by the wallet itself, not republished by a network endpoint. A counterparty receiving a credential learns only the specific fields the wallet chooses to disclose for a given transaction (selective disclosure), with the rest cryptographically committed but never revealed.

Device-bound credentials. Credentials are bound at issuance to a non-exportable key held in the device's secure element (iOS Secure Enclave / Android StrongBox). The credential cannot be presented from a different device — restoring a wallet from a recovery phrase does not transfer the credential to a new device, which is the property regulators look for in “device-bound” identity assertions.

Anonymous participation is preserved. Wallets that do not opt in to this layer continue to participate anonymously (see above). The Natural Person Identity layer adds a compliance path on top — it never gates the baseline anonymous flow.

Session

Every OTL interaction begins with an out-of-band (OOB) exchange. Session Establishment defines how the out-of-band reference is carried to the recipient, and the envelope it resolves to — a session invitation that bootstraps a bilateral DIDComm channel, optionally accompanied by a self-contained direct-authenticated request for recipients who are not network participants.

Payload Modes

Transports carry a URL, not a native payload. The issuer publishes one canonical representation of the resource and signs it once — no separate encoding per transport. The payer fetches and parses, keeping the issuer's surface area minimal and consistent regardless of how the session was initiated.

OOB Response Envelope

Every URL resolved via any entry transport returns the same envelope structure. The type field is application-defined and tells the consumer how to parse payload, making the protocol extensible without breaking existing clients. The envelope always carries a session invitation and may additionally embed a self-contained signed request.

{ "type": "otl:application/<app-defined>", // envelope type — defined by the application "payload": { "goal_code": "<app-defined>", // names the protocol/flow "sessionId": "<uuid>", "from": "did:webvh:<issuer>", "request"?: { // optional direct-authenticated request "data": { ... }, // shape defined by the application "proof": { ... } // issuer's signature over data } } }

Session Invitation

The default and always-present payload. Bootstraps a bilateral DIDComm channel once the recipient resolves the envelope — used when the application's flow needs an identity channel for a full back-and-forth lifecycle. The invitation carries a goal_code identifying the protocol to run, a UUID referencing the session on the initiator's side, and the initiator's DID.

The session establishes bilateral authentication before any data is exchanged or on-chain transaction is executed — both parties prove control of their DIDs via the DIDComm handshake and assert the real-world entities behind those DIDs via Verifiable Credentials (e.g. the LEI credential served from #whois). The invitation is the safer default whenever the recipient is itself a network participant.

Direct-Authenticated Request (optional)

A self-contained signed payload embedded alongside the invitation in payload.request. Lets recipients act on the request immediately — including non-network members with no DID and no credentials — without first establishing a DIDComm session. Signature verification against the issuer's DID document is optional but available to any recipient. Only present when the application's flow can be reduced to a self-contained signed instruction; not all applications support this.

Envelope typeDefined by each application's spec; consumers dispatch on this value to locate the application handler
ExtensibilityUnknown type values must be ignored gracefully
Entry Transports
HTTPS URL

A plain HTTPS link that references a resource on the issuer's domain. Clicking opens a browser-based flow; wallet apps that have registered for the otl:// scheme can use the companion deep link for the same resource.

Formathttps://{vasp-domain}/{app-defined-path}
ResolutionHTTP GET returns the signed OOB envelope
Deep link companionBeneficiaries publish the same reference as otl:// — wallets use the deep link; browsers use the URL
Use casesWeb checkouts, email invoices, clickable payment links, browser-based wallet UIs
QR Code

Encodes an HTTPS URL using the URL-reference model - the QR holds only the short URL; the signed payload is fetched via HTTP GET. This keeps codes compact and allows the beneficiary to update or revoke the reference without invalidating a printed or shared QR.

Encoding standardISO/IEC 18004:2024 - byte mode
PayloadHTTPS URL only (~60–120 bytes; well within the 2 953-byte capacity at error-correction level L)
ModelURL-reference - full signed payload fetched on demand via HTTP GET; code can be revoked by removing the server-side resource
DIDComm OOB noteThe DIDComm OOB spec permits inline ?oob=<base64url> embedding; this protocol uses URL-reference instead for compactness and revocability
Use casesPOS terminals, printed invoices, in-store payments, paper receipts
MPP otl-pay — HTTP 402 Machine-to-Machine

Machine-to-machine session establishment using the MPP otl-pay payment method. An agent requests a resource; the service returns HTTP 402 Payment Required with a WWW-Authenticate: Payment method="otl-pay" challenge. The request parameter carries a base64url-encoded OtlPayRequest containing only a URL. The agent fetches that URL to obtain payment details - using the same URL-reference model as QR code and deep-link transports. Whether the response includes inline payment details (public flow) or requires a DIDComm exchange (bilateral flow) is determined by the isPublic flag in the resolved payment reference, exactly as in every other session entry.

TriggerAgent sends a resource request → service returns HTTP 402 Payment Required
ChallengeWWW-Authenticate: Payment method="otl-pay", id="…", realm="…", request="<base64url(OtlPayRequest)>" where OtlPayRequest = { detailsUrl }
Agent fetchesDecodes OtlPayRequest.detailsUrl → resolves payment reference → processes public or bilateral DIDComm flow
RetryAfter on-chain settlement, agent retries with Authorization: Payment id="…", credential="<base64url(OtlPayCredential)>" where OtlPayCredential = { txHash, paymentId }
ReceiptService responds 200 OK with Payment-Receipt: <base64url(OtlPayReceipt)>
Use casesAI agent API access, automated pipelines, HTTP-native pay-per-use services
Pending: The otl-pay payment method identifier and the OtlPayRequest / OtlPayCredential / OtlPayReceipt object schemas must be registered with the MPP working group / IETF.
EIP-1193 — otl_openSession

A new EIP-1193 provider method that delivers an OTL session URL directly to a connected wallet - bypassing QR, deep link, and manual user navigation entirely. The call carries only a URL; the wallet fetches the OOB payload from that URL using the same URL-reference model used by QR codes and deep links. This keeps the method call compact and the payload format consistent across all four entry transports.

Call shape
await provider.request({ method: 'otl_openSession', params: [{ url: 'https://bene.example/pay/{paymentId}' }] })
Wallet processing steps
  1. Receive URL - the dApp passes the beneficiary's resource URL via the EIP-1193 provider
  2. Fetch OOB payload - wallet GETs the URL; response is the signed OOB message (same application/json payload as the URL/QR/deep-link transports)
  3. Process the OOB envelope — every response is a session invitation:
    • If payload.request is present — the wallet may verify the embedded signature against the issuer's DID document and act on the self-contained instruction without setting up a session
    • Otherwise (or whenever the application's flow requires it) — establish a DIDComm channel using the invitation; complete the application's full compliance lifecycle before presenting
  4. Resolve VCs (optional) - fetch #whois, validate LEI credential; display verified entity name
  5. On approval - wallet signs and broadcasts the on-chain transaction
Parameterurl - HTTPS URL to the beneficiary's payment request or channel reference (same URL as the companion QR / deep link)
ResponsePromise resolves with { txHash } on successful broadcast, or rejects if the user declines or verification fails
Payload formatIdentical to the URL-reference model - the wallet fetches and processes the OOB message exactly as it would from a QR scan or deep link
SecurityIntegrity proof is always verified against the DID document before presenting to the user - a compromised dApp passing a malicious URL cannot bypass the signature check
Use casesdApp checkout flows, browser extension integrations, in-app payments where QR scan is not appropriate
Pending: otl_openSession is not part of the current EIP-1193 standard. An ERC proposal must be submitted to the Ethereum Foundation (ethereum/EIPs) for wallet vendors to implement against.

Transport

The protocol uses two transport modes today, with a third in design. Bilateral VASP↔VASP flows use DIDComm sign-then-encrypt envelopes — inner EdDSA JWS (non-repudiation) + outer ECDH-ES JWE (confidentiality) over HTTPS. Unilateral anonymous flows — non-custodial wallets and AI agents — use plain HTTPS REST with no DIDComm wrapping. WalletConnect is the preferred transport for identified NCWs (Coming Soon).

DIDComm — Bilateral Network Participant Flows

Transport for bilateral flows where both parties are network participants with a did:webvh DID. Every message is wrapped in a sign-then-encrypt envelope: an inner EdDSA General JWS (publicly verifiable sender signature — provides sender authentication and non-repudiation) nested inside an outer ECDH-ES+A256KW / A256GCM compact JWE (anonymous encryption for end-to-end confidentiality). Delivery is HTTPS POST to the recipient's DIDComm service endpoint, discovered by resolving their DID document. Note: a pure DIDComm authcrypt JWE (ECDH-1PU) authenticates the sender to the recipient but is not publicly verifiable and therefore not non-repudiable — the inner JWS is what makes the envelope non-repudiable.

IVMS101 messages carrying PII are routed via the recipient's #pii-didcomm service entry using the standard DIDComm mediator forward protocol. The sender wraps the IVMS101 body in the standard sign-then-encrypt envelope (JWS + JWE) encrypted to the key(s) listed in the entry's recipientKeys field (#pii-key), then wraps that in a forward envelope addressed to each key in routingKeys (#key-agreement), and POSTs to the service uri. The receiving endpoint decrypts the forward wrapper and delivers the inner JWE to the compliance endpoint registered via the DIDComm coordinate-mediation protocol — which is not published in the DID document. The infrastructure handling the outer envelope cannot decrypt the inner IVMS101 payload.

Senders MUST implement support for this pattern to be protocol-compatible. When resolving a recipient's DID document and finding a DIDCommMessaging service entry with a recipientKeys field, senders MUST encrypt the message to those keys and wrap in a DIDComm forward envelope addressed to routingKeys before posting to the service uri. Senders that encrypt directly to #key-agreement instead will expose IVMS101 PII to infrastructure that must not have access to it, violating the P2 data protection principle.

Outer JWE Envelope

Content-Type: application/didcomm-encrypted+json. Compact JWE (5 base64url segments). The outer envelope provides end-to-end confidentiality using the recipient's #key-agreement X25519 key. The kid identifies the recipient key; the plaintext is the serialised inner JWS.

// Compact JWE format <base64url(protected-header)>.<base64url(encrypted-key)>.<base64url(iv)>.<base64url(ciphertext)>.<base64url(tag)> // Protected header (decoded) { "alg": "ECDH-ES+A256KW", "enc": "A256GCM", "kid": "did:webvh:beneficiary.example#key-agreement", "typ": "application/didcomm-encrypted+json", "epk": { "kty": "OKP", "crv": "X25519", "x": "<ephemeral-pub-base64url>" } }

Inner JWS (Sender Authentication)

The JWE plaintext is a General JWS. The kid in the unprotected header identifies the sender's Ed25519 #key-1 key; the recipient verifies the EdDSA signature to authenticate the sender.

{ "payload": "<base64url(DIDComm-body-JSON)>", "signatures": [{ "protected": "<base64url({ alg: 'EdDSA', typ: 'application/didcomm-signed+json' })>", "header": { "kid": "did:webvh:originator.example#key-1" }, "signature": "<base64url-encoded EdDSA signature>" }] }

Message Body

The decoded JWS payload follows the DIDComm v2 message structure. Each message carries an id — a per-message UUID that uniquely identifies this transmitted instance, set by the sender and owned by the transport. The thid links all messages in a thread: it is set by the initiator of the exchange and echoed by every subsequent message. Neither field is part of the message schema itself; both are transport-layer concerns.

{ "id": "<uuid>", "type": "otl:message/payment-details-request", "from": "did:webvh:originator.example", "to": ["did:webvh:beneficiary.example"], "thid": "<thread-id>", "body": { "paymentId": "<payment-id>" } }

DID Resolution

Before sending, the sender resolves the recipient's did:webvh DID to discover the DIDComm service endpoint and their #key-agreement public key.

Resolution URLHTTPS GET https://<host>/.well-known/did.json
Exampledid:webvh:vasp.example.com → GET https://vasp.example.com/.well-known/did.json
Cache policyIn-memory per session; re-resolved on first message to a new peer
Transport keykeyAgreement[#key-agreement] - X25519 key for outer JWE
PII keykeyAgreement[#pii-key] - final-recipient X25519 key for IVMS101; routed via #pii-didcomm mediator service entry

Message Delivery

The resolved service endpoint receives the compact JWE via HTTP POST.

POST https://participant.example/didcomm Content-Type: application/didcomm-encrypted+json <compact-JWE-string>

Out-of-Band (OOB) References

Payment Request and Channel flows begin with a signed OOB reference - a JWS whose body contains only the reference ID. The counterparty fetches full details via a subsequent DIDComm DetailsRequest.

The entry transports for OOB references (HTTPS URL, QR code, deep link) are defined in the Session Establishment layer.

Pending: The otl:* DIDComm protocol namespace is unregistered. A namespace registration and protocol specification must be submitted to the Decentralized Identity Foundation (DIF) for third-party VASP interoperability.
HTTPS REST — Anonymous Unilateral Flows

Used for anonymous unilateral flows where the originator has no registered DID identity — non-custodial wallets and AI agents. Messages are plain JSON over HTTPS with no JWS wrapping. The beneficiary's endpoints are CORS-enabled. Authenticity of signed offers is always verifiable via the proof field included in every public response.

Wallet Flow

A non-custodial wallet (dApp, browser extension, or mobile app) scans a payment link and processes the flow entirely over REST - no DIDComm, no DID identity on the originator side. The wallet is identified solely by its on-chain signing address.

1. Fetch detailsGET /payment-requests/:id/details → JSON with paymentOptions + proof
2. Optional verifyDecode proof JWS, resolve beneficiary DID, verify Ed25519 signature
3. Sign & broadcastWallet signs and broadcasts the on-chain transaction
Content-Typeapplication/json
CORSAccess-Control-Allow-Origin: * on all public detail endpoints
// GET /payment-requests/:id/details response { "type": "otl:application/payment-request", "payload": { "paymentId": "<uuid>", "beneficiaryDid": "did:webvh:beneficiary.example", "paymentOptions": [{ "asset": {...}, "recipientAddress": "0x...", "amount": "..." }], "purpose": "Invoice INV-2024-042", "expiresAt": "2026-05-01T00:00:00Z", "proof": "<packed JWS - decode to verify beneficiary signature>" } }

REST Execution Notification

When the originator resolved payment details via the public REST endpoint (path 2a) and has no established DIDComm session, execution and settlement notifications are exchanged over REST, correlated by paymentId.

Execution endpointPOST /payment-requests/:id/execution - originator notifies beneficiary of broadcast; body: { txHash, asset, amount, callbackUrl? }
SettlementBeneficiary POSTs settlement confirmation to callbackUrl if supplied, or originator polls GET /payment-requests/:id for status
CorrelationpaymentId in the URL path is the thread identifier - no DIDComm channel required
When to usePath 2a originators with no DIDComm endpoint; DIDComm path remains preferred for identified participants who exchanged IVMS101
WalletConnect — Preferred Transport for NCWs Coming Soon

WalletConnect is the preferred transport for identified non-custodial wallets in OTL. Mobile wallets already integrate WalletConnect for dApp pairing, so reusing it for OTL avoids asking wallets to ship a second relay / session protocol just for compliance interactions.

Role. An OTL counterparty (typically a VASP) publishes a pairing URI; the wallet scans / opens it; both peers derive a session over the WalletConnect relay infrastructure with end-to-end-encrypted message exchange. OTL application messages (Natural Person credential presentation, address-ownership proofs, etc.) ride inside the session.

Status. Wire format, session lifecycle, and how the WalletConnect pairing URI is delivered through the Session Establishment entry transports are still in design.

Messaging

All protocol messages are typed DIDComm message bodies identified by a URI in the type field. Each group below corresponds to a sub-protocol. Click a message to expand its field definitions.

Message Identification

Each message definition is identified by a type URI — a protocol-scoped identifier that names the message schema and its field contract. The URI is bound to the message definition, not to any application flow or transport session. A single message type may appear in multiple application flows; it carries no information about which flow it is being used in.

Building Blocks
Payment Request — Signed Out-of-Band Reference

A Payment Request is a signed, shareable payment invite created by the beneficiary and distributed out-of-band — via QR code, invoice link, deeplink, or embedded in a webpage or API response. The same URL supports two resolution modes depending on the originator's capabilities.

Session invite (DIDComm participants). The URL resolves to a packed DIDComm JWS containing only a paymentId UUID — no amounts, assets, or addresses are disclosed on the public surface. The originator verifies the beneficiary's did:webvh signature and establishes an encrypted DIDComm channel. The beneficiary evaluates the originator's DID identity before responding — full payment details are only shared once the counterparty is known, keeping sensitive data off public surfaces and preventing execution of transactions the beneficiary would not accept.

Signed details (wallets and anonymous originators). Appending /details to the same URL returns the full payment fields in plain JSON alongside a proof field — the packed JWS — for optional offline signature verification. No DIDComm stack is required on the originator side. Exposing this endpoint is a discretionary choice by the beneficiary — it is appropriate for public-facing merchant flows but may be omitted where the beneficiary requires counterparty identity before disclosing any payment details.

Session invite endpointGET /payment-requests/:id → packed DIDComm JWS (application/didcomm-signed+json)
Signed details endpointGET /payment-requests/:id/details → plain JSON with payment fields + proof JWS
Payment Details - field reference
Field Type Required Description
paymentId string required UUID of the payment request - stable identifier throughout the flow
paymentOptions PaymentOption[] required One or more accepted asset + address + amount tuples; originator picks one
purpose string required Human-readable description of what the payment is for
invoiceNumber string? optional External invoice or order reference for accounting systems
mcc string? optional ISO 18245 Merchant Category Code - 4-digit code classifying the merchant type (e.g. 5411 = Grocery Stores, 7011 = Hotels & Lodging). Used by originators for transaction classification and compliance screening.
tcc string? optional Transaction Category Code - further classifies the transaction type for routing, AML screening, and reporting purposes (e.g. C = Cash, G = Goods, S = Services, T = Travel)
expiresAt string? optional ISO 8601 timestamp after which the payment request is no longer valid. Originators must reject a resolved request whose expiresAt is in the past.
allowIndirect boolean? optional Default false. When false, the beneficiary requires the on-chain sending address to be controlled by the originator participant - i.e. the settling address must be attributable to the counterparty for Travel Rule purposes. Set true to permit indirect routing where funds arrive via an intermediary (e.g. a DEX, swap service, or payment router) whose address is not directly tied to the originator.
allowContract boolean? optional Default false. When false, the beneficiary requires funds to originate from an externally owned account (EOA). Set true to accept funds from smart contract addresses - such as multisig wallets, DAO treasuries, or DeFi protocol vaults - where the beneficial ownership structure may be complex or impossible to fully validate under standard Travel Rule procedures.
Packed JWS - returned by GET /payment-requests/:id
{ "payload": "<base64url({ id, type, from, body: { paymentId } })>", "signatures": [{ "protected": "<base64url({ alg: 'EdDSA', kid: 'did:webvh:beneficiary.example#key-1' })>", "signature": "<base64url-encoded EdDSA signature>" }] } // body after decode: { "id": "<uuid>", "type": "otl:message/payment-reference", "from": "did:webvh:beneficiary.example", "body": { "paymentId": "8f3a1c2d-..." } }
Payment Details Response - DIDComm body (or /details JSON)
{ "paymentId": "8f3a1c2d-...", "paymentOptions": [{ "asset": { "Header": { "DTI": "CK9PW1MFH", "DTIType": 0 }, "Normative": { "AuxiliaryMechanism": "ERC-20", "AuxiliaryDistributedLedger": "X9J9K872S", "AuxiliaryTechnicalReference": "0xa0b86991..." }, "CAIP19": "eip155:1/erc20:0xa0b86991..." }, "recipientAddress": "0xd8dA6BF...", "amount": "1000000" }], "purpose": "Invoice INV-2024-042 - consulting services Q4", "invoiceNumber": "INV-2024-042", "mcc": "7389", "tcc": "S", "expiresAt": "2026-05-01T00:00:00Z", "allowIndirect": false, "allowContract": false, // /details endpoint only: "beneficiaryDid": "did:webvh:beneficiary.example", "proof": "<full packed JWS for optional offline verification>" }
Asset Identification — ISO 24165 / DTI

Assets are identified using ISO 24165 Digital Token Identifiers (DTI), governed by the DTIF non-profit under ISO mandate. DTI serves as the institutional identifier — a jurisdiction-neutral, registry-backed reference suitable for compliance, reporting, and counterparty verification. When a DTI is available, it is the sole authoritative identifier — Normative metadata fields MUST NOT be set, as receivers are expected to verify asset details against the DTIF registry independently.

An optional CAIP-19 string MAY be included alongside the DTI to provide native wallet support — wallet SDKs and dApp tooling commonly consume CAIP-19 to resolve chain and contract addresses without an additional registry lookup. When both are present, DTI is authoritative and a disagreement between CAIP19 and the DTIF registry MUST cause the receiver to reject the request.

When no DTI is available (asset not yet registered in DTIF), Normative fields MAY be set to describe the unregistered asset. CAIP-19 remains optional in this case.

Field With DTI — Native (DTIType 1) With DTI — Contract (DTIType 0) No DTI — Contract (DTIType 0)
Header.DTI set set omit
Header.DTIType 1 0 0
Normative.AuxiliaryMechanism omit omit optional
Normative.AuxiliaryDistributedLedger omit omit optional
Normative.AuxiliaryTechnicalReference omit omit optional
CAIP19 optional optional optional
Native token — Bitcoin (DTI present)
{ "Header": { "DTI": "4H95J0R2X", "DTIType": 1 }, "CAIP19": "bip122:000000000019d6689c.../slip44:0" }
Contract token — USDC on Ethereum (DTI present)
{ "Header": { "DTI": "CK9PW1MFH", "DTIType": 0 }, "CAIP19": "eip155:1/erc20:0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" }
Unregistered contract token (no DTI)
{ "Header": { "DTIType": 0 }, "Normative": { "AuxiliaryMechanism": "ERC-20", "AuxiliaryDistributedLedger": "X9J9K872S", "AuxiliaryTechnicalReference": "0xAbCd..." }, "CAIP19": "eip155:1/erc20:0xAbCd..." }
Travel Rule Data Exchange — IVMS101

IVMS101 (Inter-VASP Messaging Standard 101) is the global data standard for transferring natural person and legal entity identification data between VASPs as required by the FATF Travel Rule. Developed jointly by the Global Digital Finance working group and major industry bodies, it defines a structured, machine-readable schema covering originator and beneficiary names, addresses, account identifiers, and national identity numbers. It is the format referenced by most Travel Rule regulatory frameworks and implemented by established Travel Rule solution providers worldwide, making it the natural interoperability baseline for this protocol.

Design choice — mediator routing for PII confidentiality. IVMS101 messages are addressed to the service endpoint in the recipient's DID document whose accept array includes otl:application/ivms101. When that endpoint specifies recipientKeys, the sender MUST encrypt to those keys using the DIDComm mediator forward pattern — ensuring PII is decryptable only by the designated compliance key holder and is opaque to any intermediary infrastructure. Senders MUST support this pattern when the recipient's DID document is configured this way; the choice of endpoint is always determined by the accept array, not by any naming convention.

// IVMS101 Originator Data - message body { "paymentId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "payload": { "originator": { "originatorPersons": [{ "naturalPerson": { "name": [{ "nameIdentifier": [{ "primaryIdentifier": "Nakamoto", "secondaryIdentifier": "Satoshi", "nameIdentifierType": "LEGL" }]}], "geographicAddress": [{ "streetName": "Shibuya", "buildingNumber": "1-2-3", "townName": "Tokyo", "country": "JP" }], "dateAndPlaceOfBirth": { "dateOfBirth": "1980-04-05", "placeOfBirth": "Tokyo" } } }] } } }
ISO 20022 Alignment

ISO 20022 is the global financial messaging standard used by SWIFT, central bank payment systems, and major clearing networks for structured payment data exchange. Key OTL payment fields carry intentional semantic alignment with ISO 20022 concepts: purpose maps to Purp/Cd (ExternalPurpose1Code) and tcc maps to PmtTpInf/CtgyPurp/Cd in the pain.001 / pacs.008 message families.

This alignment means that institutions already operating ISO 20022 infrastructure can map incoming OTL payment messages to their existing message schemas with minimal translation effort, and that compliance systems built around ISO 20022 field semantics can interpret OTL data with a thin adapter layer rather than a full schema redesign. The protocol does not mandate ISO 20022 encoding - it uses JSON over DIDComm - but the semantic correspondence is intentional.

Message Types
OAuth Session Establishment Coming Soon

A message group for initiating and confirming cross-provider account linking sessions, using the OTL identity framework and trust perimeter as the authentication foundation. Replaces bilateral key exchange and centralised OAuth intermediaries with a DID-native flow: each party's identity and trust level are established before the session is opened, and the resulting session token is scoped to the verified relationship between the two participants.

Applications

The Application layer defines the logical interactions between participants — the choreography of messages, the compliance lifecycle, and the participant set that together realize a specific use case. Applications need not be payment flows; any protocol-level interaction that builds on Identity, Session, Transport, and Messaging is an application.

Applications are modular and extensible by design: a new application can be specified without touching the lower layers, and clients that do not implement it remain interoperable for the applications they do support. Each application is identified by a stable URI (see below). Participants are not required to support every application; if a counterparty receives a session invitation for an unsupported application, it MUST abort the flow with otl:message/abort and a reason indicating the application is unsupported — the initiator can then fall back to another application or another counterparty.

This section is intentionally high-level. For a step-by-step visual walk-through of each flow's message sequence and state transitions, use the Flow Explorer.

Application Identification

Each application flow is identified by a URI carried in the goal_code field of the session invitation (session layer). This URI is the authoritative identifier for the protocol flow being initiated — it is defined by the protocol and cannot be minted unilaterally by participants. Once a DIDComm channel is established, subsequent messages are correlated by thid; the goal_code is not repeated inside the channel.

otl:application/payment-requestPayment Request (pull) — beneficiary-initiated signed payment invite
otl:application/push-paymentPush Payment — originator-initiated payment to a known beneficiary
otl:application/payment-channelPayment Channel — pre-authorised allowance with repeated sub-payments
otl:application/wallet-attributionWallet Attribution — privacy-preserving on-chain address lookup
otl:application/ncw-identityNCW Identity Pre-seeding — selective-disclosure presentation of a device-bound natural-person credential (Coming Soon)
Payment Request Beneficiary creates a signed payment reference; originator resolves and pays
Use Cases
  • Merchant checkout / invoice. A business generates a QR code or shareable link for a specific invoice amount. The customer's wallet app or provider scans it and pays without any manual address entry.
  • Freelancer billing. An individual sends a payment request link via email or messaging. The recipient's provider resolves the signed reference and executes after compliance.
  • Programmatic billing. An application embeds a signed payment reference in a web page or API response. The originator's system auto-resolves and settles with no human-in-the-loop.
  • Point-of-sale terminal. A retail device displays a payment request QR; any compatible wallet resolves and settles on-chain in seconds.
Participants
RoleDescriptionScenario notes
BeneficiaryCreates the payment request, defines accepted payment options, owns the destination address(es)Always has a did:webvh DID and a DIDComm endpoint; acts as server throughout the flow
OriginatorResolves the payment request URL, runs compliance, executes the on-chain settlementCan be a custodial provider acting on behalf of a user, or a fully automated system
Originator UserEnd customer who initiates or approves the paymentOptional - not present in fully automated/programmatic scenarios
Beneficiary UserEnd customer who receives the fundsMay not interact directly; the beneficiary provider handles the DIDComm layer on their behalf
Message Sequence
#MessageTransportDirection
1Payment ReferenceOOB - link or QR codebeneficiary → originator
2aPayment Reference ResolveREST - public endpointoriginator → beneficiary (anonymous / public details)
2bPayment Details RequestDIDCommoriginator → beneficiary (identified participant)
3Payment Details ResponseREST or DIDCommbeneficiary → originator (matches step 2 path)
4Flow Initialize + IVMS101 exchangeDIDCommif policy requires - identified participants only
5Execution NotificationDIDComm or REST - POST /payment-requests/:id/executionoriginator → beneficiary
6Settlement NotificationDIDComm or REST - callback / pollingbeneficiary → originator

REST path (steps 5–6) applies when the originator took path 2a and has no established DIDComm session. DIDComm is required when IVMS101 was exchanged (path 2b).

State Machine
createdBeneficiary posted payment request; OOB signed reference shared
originator resolves OOB → sends DetailsRequest
details_sentBeneficiary returned payment options and purpose
[policy: IVMS101 required] → compliance exchange
compliantIVMS101 exchanged; originator clears for payment
originator sends ExecutionNotification + tx hash
executingOn-chain transaction broadcast; awaiting confirmation
beneficiary confirms on-chain receipt
settledTerminal - payment complete
Push Payment Originator proposes a payment to a known beneficiary; beneficiary accepts or rejects
Use Cases
  • B2B settlement. One business initiates a payment to a known counterparty DID - e.g. paying a supplier invoice. The originator drives the proposal; the beneficiary applies acceptance policy.
  • Payroll / bulk disbursements. A platform pushes payments to a list of beneficiary DIDs in batch. Each payment proposal is independently negotiated and settled.
  • Manual user-initiated transfer. A user instructs their provider to send funds to a specific beneficiary DID. The provider assembles and dispatches the proposal on their behalf.
Participants
RoleDescriptionScenario notes
OriginatorInitiates the payment by sending a PaymentProposal to the beneficiary's DIDComm endpointMust know the beneficiary's did:webvh in advance; holds the payer's DID
Originator UserEnd customer authorising the pushIn automated scenarios (payroll, programmatic) there may be no interactive user
BeneficiaryReceives the proposal, applies compliance policy, returns acceptance or rejectionControls which payment options are valid; may trigger IVMS101 or counterparty validation requirements
Beneficiary UserEnd customer receiving the fundsNot involved in the DIDComm exchange; beneficiary provider handles on their behalf
Message Sequence
#MessageTransportDirection
1Payment ProposalDIDCommoriginator → beneficiary
2Flow Initialize + IVMS101 exchangeDIDCommif policy requires
3Payment AcceptanceDIDCommbeneficiary → originator
4Execution NotificationDIDCommoriginator → beneficiary
5Settlement NotificationDIDCommbeneficiary → originator
Plain-text option

The PaymentProposal body can include an optional memo field - a human-readable plain-text note attached to the payment (e.g. "Invoice INV-2024-042 - consulting services Q4"). The memo travels inside the DIDComm JWS envelope, so it is authenticated (covered by the sender's signature) but not encrypted. This is intentional: memos are operational notes rather than regulated PII, and keeping them in clear text lets the beneficiary's system parse them without decryption. Any PII required for Travel Rule compliance is transmitted separately as a JWE-encrypted IVMS101 payload.

State Machine
proposedOriginator sent PaymentProposal to beneficiary
[policy: IVMS101 required] → compliance exchange
compliantIVMS101 exchanged; awaiting beneficiary decision
beneficiary sends PaymentAcceptance
acceptedBeneficiary confirmed a payment option
originator sends ExecutionNotification + tx hash
executingOn-chain transaction broadcast; awaiting confirmation
beneficiary confirms on-chain receipt
settledTerminal - payment complete
Payment Channel Compliance negotiated once; fixed originator and beneficiary; pull or push sub-payments up to an aggregate allowance
Definition
  • Single negotiation. Compliance and counterparty validation run once at channel setup. All subsequent sub-payments are auto-approved without re-running the compliance lifecycle.
  • Fixed roles. Originator (fund sender) and beneficiary (fund receiver) are bound at channel creation and cannot change for the lifetime of the channel.
  • Channel direction. Set by channelType in the Channel Details Response and fixed for the channel lifetime. pull — beneficiary requests each sub-payment; originator auto-executes within the allowance. push — originator initiates each sub-payment directly; beneficiary confirms receipt.
  • Allowance (optional). The aggregate cap the originator commits to settle. May be exchanged on-protocol (present in Channel Details Response) or agreed off-protocol between the originator and their provider. When absent, the beneficiary accepts the channel without an explicit on-protocol cap.
Use Cases
  • SaaS subscription / recurring billing. A business pre-approves a recurring billing relationship up to a monthly cap. The service provider pulls each period's fee without re-running compliance.
  • API metered billing. A service charges small per-call or per-unit amounts against a pre-approved allowance. Each micro-payment auto-executes without a new compliance round-trip.
  • Standing order / direct debit. A pre-authorised recurring transfer for a fixed or variable amount - the channel expresses the standing mandate; the beneficiary drives each drawdown.
  • Streaming payments. High-frequency, low-value settlements (e.g. per-second media streaming fees) that would be impractical to clear individually through a full compliance flow.
Participants
RoleDescriptionScenario notes
BeneficiaryCreates the channel offer (allowance, payment options, expiry); in pull channels also drives each sub-payment requestActive during payment phase in pull channels; passive in push channels
OriginatorResolves the channel offer, runs compliance once, sends ChannelAcceptance; in pull auto-approves sub-payment requests within the remaining allowance; in push initiates each sub-payment directlyNo per-payment compliance re-negotiation once the channel is active
Originator UserEnd customer who authorises the channel setup and aggregate allowanceNot involved in individual sub-payment execution
Message Sequence
#MessageTransportDirectionNotes
1Channel ReferenceOOB - link or QR codebeneficiary → originator
2Channel Details RequestDIDCommoriginator → beneficiary
3Channel Details ResponseDIDCommbeneficiary → originator
4Flow Initialize + IVMS101 exchangeDIDCommbilateralif policy requires
5Channel AcceptanceDIDCommoriginator → beneficiary
6Channel OpenedDIDCommbeneficiary → originator
7Sub-Payment RequestDIDCommbeneficiary → originatorpull only; repeats per payment
8Execution NotificationDIDCommoriginator → beneficiaryrepeats per payment; serves as initiation in push
9Settlement NotificationDIDCommbeneficiary → originatorrepeats per payment
10Channel CloseDIDCommoriginator → beneficiaryoptional

In push channels step 7 is absent — the originator broadcasts the on-chain transaction and sends an Execution Notification directly. The originator generates a paymentId UUID per sub-payment for correlation with the Settlement Notification.

Allowance enforcement

The originator tracks cumulative settled amounts across all sub-payments for a channel. Each sub-payment is validated against the remaining allowance (allowance.amount − totalPaid) before execution. A request that would exceed the cap is rejected with a 422 error. Once the allowance is consumed or expiresAt is reached the channel moves to expired; the originator may also close it early by sending ChannelClose. If allowance is absent from the Channel Details Response, the originator's provider enforces the cap off-protocol; the beneficiary accepts this arrangement by completing the ChannelAcceptance handshake.

State Machine
openChannel created by beneficiary; OOB reference shared
originator resolves channel → starts compliance
negotiatingIVMS101 / counterparty validation exchange in progress
originator sends ChannelAcceptance → beneficiary sends ChannelOpened
activeSub-payments auto-approved up to aggregate allowance
↓ (repeats per sub-payment)
pull: beneficiary SubPaymentReq → execution → settlement / push: originator execution → settlement
closedEither party sent ChannelClose - terminal
expiredexpiresAt timestamp reached - terminal
abortedAbort message sent/received during negotiation - terminal
Wallet Attribution Privacy-preserving discovery via federated fingerprint relay; fingerprint matching followed by DID-authenticated direct inquiry
Definition
  • Fingerprint. The originator publishes only a 4-byte fingerprint (last 4 bytes of SHA-256(address)) to a relay. The full address and its hash never reach the relay.
  • Federated relays. A federated set of interoperable relays disseminates fingerprints. Each relay is independent; participants choose which to publish to or subscribe from. No single relay is a chokepoint.
  • Local matching. Candidate participants subscribe to relays and match incoming fingerprints against fingerprints of their custodied addresses locally. False positives are expected at the 32-bit fingerprint width and filtered out in the next phase.
  • DID trust handshake. On a fingerprint match, the candidate authenticates over DIDComm and requests the full inquiry. The originator's policy decides whether to release it (e.g. valid LEI VC, trust list). Only after release does the standard hashed inquiry / claim exchange happen, peer-to-peer.
Use Cases
  • Pre-flight Travel Rule check. Before sending to an on-chain address, the originator needs to know whether a custodial participant controls it. If claimed, the full DIDComm payment flow applies; if not, the originator treats it as an NCW transaction.
  • NCW disambiguation. Determine at runtime whether a target address belongs to a self-custodied wallet or a custodial account — without ever disclosing the full address or hash to non-matching participants.
  • Compliance gate before execution. A compliance policy engine triggers wallet attribution automatically when the payment target is an address rather than a DID, and routes the flow accordingly based on the result.
Participants
RoleDescriptionScenario notes
OriginatorComputes the 4-byte fingerprint of the target address and publishes a signed AddressFingerprintAdvertisement to one or more relaysDecides which candidates receive the full hashed inquiry based on its trust policy
Relay (federated)Receives signed pre-inquiries, maintains a Cuckoo filter snapshot of active fingerprints, and streams fingerprints to subscribersParticipants may publish to or subscribe from multiple relays for redundancy; inter-relay gossip is out of scope
Candidate ParticipantsSubscribe to one or more relays; match incoming fingerprints against their custodied address set locally; on a match, request the full inquiry from the originatorMost fingerprint matches at 32-bit width will resolve to a single candidate; false-positive collisions are dropped silently after hash verification
Beneficiary (post-claim)The candidate whose full SHA-256 hash matches a custodied address becomes the beneficiary for the subsequent payment flowOriginator switches to Payment Request or Push Payment using the claimant's DID
Relay Infrastructure
EndpointMethodPurpose
/pre-inquiriesPOSTSubmit a signed AddressFingerprintAdvertisement; returns 202 Accepted
/fingerprints/snapshotGETReturns the current Cuckoo filter as binary (application/octet-stream) + ETag for delta polling; X-Filter-Params header carries fingerprint-bit-count, bucket count, load factor
/fingerprints/streamGET (SSE)Server-Sent Events stream of {inquiryId, fingerprint, senderVaspDid, expiresAt} per advertisement; subscribers filter client-side

Cuckoo filter parameters (suggested defaults): 32-bit fingerprint, 4-entry bucket, 0.85 target load factor, SHA-256-truncated hash, 60s rebuild cadence under load. The filter supports deletion so fingerprints can be aged out when expiresAt is reached.

Message Sequence
#MessageTransportDirectionNotes
1Address Fingerprint AdvertisementREST POST /pre-inquiriesoriginator → relaySigned JWS; carries only the 4-byte fingerprint
2aCuckoo filter snapshotREST GET /fingerprints/snapshotrelay → candidate subscribersPeriodic poll; baseline state
2bFingerprint streamSSE GET /fingerprints/streamrelay → candidate subscribersLive updates between snapshots
3Inquiry RequestDIDCommcandidate → originatorSent only on local fingerprint match; carries candidate DID + matched fingerprint
4Address Ownership InquiryDIDCommoriginator → candidateReleased after originator's trust policy passes; carries the full SHA-256 hash
5Address Ownership ClaimDIDCommcandidate → originatorSent only if the full hash matches a custodied address; false positives drop silently
State Machine
advertisedOriginator published fingerprint to relay(s)
candidate matches fingerprint locally → sends InquiryRequest → originator releases full inquiry
inquiredFull hash inquiry sent to candidate; awaiting verification (one state per matched candidate)
candidate verifies full hash matches a custodied address
confirmedCandidate sent AddressOwnershipClaim - terminal
rejectedNo candidate confirmed; all matches were 4-byte collisions or no candidate matched - terminal
expiredexpiresAt reached without confirmation - terminal
Privacy Analysis

What the relay learns: originator DID, fingerprint, and timestamp per advertisement — and the originator's query patterns over time.

What the relay cannot learn: the actual address. Recovering it from a 32-bit fingerprint requires brute-forcing 232 hash preimages, feasible only against a known small address candidate set.

False-positive rate: for N custodied addresses across all candidates and a 32-bit fingerprint space, expected matches per query ≈ N / 232. At 100M addresses worldwide that is ≈ 0.023 candidates per query — most queries resolve to zero or one match. Each match independently passes through the inquired state and is verified via the full hash.

Mitigations: the federated relay set distributes correlation risk; originators may rotate which relay(s) they publish to; expiresAt bounds how long any relay retains state.

NCW Identity Pre-seeding Wallet shares a Natural Person credential with the counterparty over the transport layer, using selective disclosure and device binding
Coming Soon
Overview

An opt-in sub-protocol that lets an identified non-custodial wallet share verified natural-person attributes with a counterparty without giving up self-custody. The wallet holds a verifiable credential (issued by a regulated identity provider) and presents only the specific claims required for a given transaction; the rest stay cryptographically committed but undisclosed (selective disclosure). The presentation is bound to a non-exportable key in the wallet's secure element (device binding), so it cannot be lifted onto a different device.

The presentation rides on the Transport-layer channel chosen for NCW interactions (see Transport → WalletConnect). When the NCW participates anonymously, this flow is skipped and the standard anonymous payment path applies.

Wire format, credential schema, presentation protocol, and verifier requirements are still in design.

OAuth Session Establishment Cross-provider authenticated sessions using OTL identity and trust perimeter
Coming Soon

A protocol flow for establishing authenticated cross-provider sessions, leveraging the OTL identity framework and trust perimeter as a global account linking standard. Because both parties already hold verified DIDs and have evaluated each other's credentials, session establishment requires no bilateral key exchange and no centralised identity broker - trust is derived directly from the participants' DID documents and their mutually recognised credential issuers.