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.
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.
| Context URI | Purpose |
|---|---|
| https://www.w3.org/ns/did/v1 | Base DID Core vocabulary |
| https://w3id.org/security/suites/ed25519-2020/v1 | Ed25519VerificationKey2020 key type for signing |
| https://w3id.org/security/suites/x25519-2020/v1 | X25519KeyAgreementKey2020 key type for encryption |
| https://didcomm.org/messaging/contexts/v2 | DIDComm v2 service and messaging vocabulary |
| Field | Requirement | Notes |
|---|---|---|
| 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.
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 type | Status | Description |
|---|---|---|
| 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.
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.
| Capability | Anonymous participant | Network 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:webvhidentity, 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.
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.
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.
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.
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.
typeDefined by each application's spec; consumers dispatch on this value to locate the application handlertype values must be ignored gracefullyA 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.
https://{vasp-domain}/{app-defined-path}otl:// — wallets use the deep link; browsers use the URLEncodes 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.
?oob=<base64url> embedding; this protocol uses URL-reference instead for compactness and revocabilityotl://
►
A custom URI scheme that opens any conformant wallet or payment app directly, bypassing
the browser. No prior agreement between beneficiary and payer is needed - any wallet that
has registered the otl:// scheme can handle any compliant deep link,
analogous to how bitcoin: and ethereum: URIs work today.
HTTPS-based Universal Links / App Links require the beneficiary's server to explicitly
allowlist specific wallet app bundle IDs (via apple-app-site-association /
Digital Asset Links). This does not scale to an open protocol with an unknown wallet on
the payer side - so OTL uses a custom URI scheme instead.
URI Shape
otl://CFBundleURLSchemes in Info.plist · Android: intent-filter with scheme=otlotl:// linkotl URI scheme must be registered with IANA under RFC 7595 before any public or regulated deployment.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.
HTTP 402 Payment RequiredWWW-Authenticate: Payment method="otl-pay", id="…", realm="…", request="<base64url(OtlPayRequest)>" where OtlPayRequest = { detailsUrl }OtlPayRequest.detailsUrl → resolves payment reference → processes public or bilateral DIDComm flowAuthorization: Payment id="…", credential="<base64url(OtlPayCredential)>" where OtlPayCredential = { txHash, paymentId }200 OK with Payment-Receipt: <base64url(OtlPayReceipt)>otl-pay payment method identifier and the OtlPayRequest / OtlPayCredential / OtlPayReceipt object schemas must be registered with the MPP working group / IETF.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.
- Receive URL - the dApp passes the beneficiary's resource URL via the EIP-1193 provider
- Fetch OOB payload - wallet GETs the URL; response is the signed OOB message (same
application/jsonpayload as the URL/QR/deep-link transports) - Process the OOB envelope — every response is a session invitation:
- If
payload.requestis 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
- If
- Resolve VCs (optional) - fetch
#whois, validate LEI credential; display verified entity name - On approval - wallet signs and broadcasts the on-chain transaction
url - HTTPS URL to the beneficiary's payment request or channel reference (same URL as the companion QR / deep link){ txHash } on successful broadcast, or rejects if the user declines or verification failsotl_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).
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.
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.
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.
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.
keyAgreement[#key-agreement] - X25519 key for outer JWEkeyAgreement[#pii-key] - final-recipient X25519 key for IVMS101; routed via #pii-didcomm mediator service entryMessage Delivery
The resolved service endpoint receives the compact JWE via HTTP POST.
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.
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.
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.
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.
POST /payment-requests/:id/execution - originator notifies beneficiary of broadcast; body: { txHash, asset, amount, callbackUrl? }callbackUrl if supplied, or originator polls GET /payment-requests/:id for statuspaymentId in the URL path is the thread identifier - no DIDComm channel requiredWalletConnect 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.
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.
GET /payment-requests/:id → packed DIDComm JWS (application/didcomm-signed+json)GET /payment-requests/:id/details → plain JSON with payment fields + proof JWS| 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. |
GET /payment-requests/:id/details JSON)
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 |
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.
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.
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 inviteotl:application/push-paymentPush Payment — originator-initiated payment to a known beneficiaryotl:application/payment-channelPayment Channel — pre-authorised allowance with repeated sub-paymentsotl:application/wallet-attributionWallet Attribution — privacy-preserving on-chain address lookupotl:application/ncw-identityNCW Identity Pre-seeding — selective-disclosure presentation of a device-bound natural-person credential (Coming Soon)- 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.
| Role | Description | Scenario notes |
|---|---|---|
| Beneficiary | Creates 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 |
| Originator | Resolves the payment request URL, runs compliance, executes the on-chain settlement | Can be a custodial provider acting on behalf of a user, or a fully automated system |
| Originator User | End customer who initiates or approves the payment | Optional - not present in fully automated/programmatic scenarios |
| Beneficiary User | End customer who receives the funds | May not interact directly; the beneficiary provider handles the DIDComm layer on their behalf |
| # | Message | Transport | Direction |
|---|---|---|---|
| 1 | Payment Reference | OOB - link or QR code | beneficiary → originator |
| 2a | Payment Reference Resolve | REST - public endpoint | originator → beneficiary (anonymous / public details) |
| 2b | Payment Details Request | DIDComm | originator → beneficiary (identified participant) |
| 3 | Payment Details Response | REST or DIDComm | beneficiary → originator (matches step 2 path) |
| 4 | Flow Initialize + IVMS101 exchange | DIDComm | if policy requires - identified participants only |
| 5 | Execution Notification | DIDComm or REST - POST /payment-requests/:id/execution | originator → beneficiary |
| 6 | Settlement Notification | DIDComm or REST - callback / polling | beneficiary → 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).
- 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.
| Role | Description | Scenario notes |
|---|---|---|
| Originator | Initiates the payment by sending a PaymentProposal to the beneficiary's DIDComm endpoint | Must know the beneficiary's did:webvh in advance; holds the payer's DID |
| Originator User | End customer authorising the push | In automated scenarios (payroll, programmatic) there may be no interactive user |
| Beneficiary | Receives the proposal, applies compliance policy, returns acceptance or rejection | Controls which payment options are valid; may trigger IVMS101 or counterparty validation requirements |
| Beneficiary User | End customer receiving the funds | Not involved in the DIDComm exchange; beneficiary provider handles on their behalf |
| # | Message | Transport | Direction |
|---|---|---|---|
| 1 | Payment Proposal | DIDComm | originator → beneficiary |
| 2 | Flow Initialize + IVMS101 exchange | DIDComm | if policy requires |
| 3 | Payment Acceptance | DIDComm | beneficiary → originator |
| 4 | Execution Notification | DIDComm | originator → beneficiary |
| 5 | Settlement Notification | DIDComm | beneficiary → originator |
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.
- 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
channelTypein 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.
- 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.
| Role | Description | Scenario notes |
|---|---|---|
| Beneficiary | Creates the channel offer (allowance, payment options, expiry); in pull channels also drives each sub-payment request | Active during payment phase in pull channels; passive in push channels |
| Originator | Resolves 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 directly | No per-payment compliance re-negotiation once the channel is active |
| Originator User | End customer who authorises the channel setup and aggregate allowance | Not involved in individual sub-payment execution |
| # | Message | Transport | Direction | Notes |
|---|---|---|---|---|
| 1 | Channel Reference | OOB - link or QR code | beneficiary → originator | |
| 2 | Channel Details Request | DIDComm | originator → beneficiary | |
| 3 | Channel Details Response | DIDComm | beneficiary → originator | |
| 4 | Flow Initialize + IVMS101 exchange | DIDComm | bilateral | if policy requires |
| 5 | Channel Acceptance | DIDComm | originator → beneficiary | |
| 6 | Channel Opened | DIDComm | beneficiary → originator | |
| 7 | Sub-Payment Request | DIDComm | beneficiary → originator | pull only; repeats per payment |
| 8 | Execution Notification | DIDComm | originator → beneficiary | repeats per payment; serves as initiation in push |
| 9 | Settlement Notification | DIDComm | beneficiary → originator | repeats per payment |
| 10 | Channel Close | DIDComm | originator → beneficiary | optional |
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.
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.
- 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.
- 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.
| Role | Description | Scenario notes |
|---|---|---|
| Originator | Computes the 4-byte fingerprint of the target address and publishes a signed AddressFingerprintAdvertisement to one or more relays | Decides 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 subscribers | Participants may publish to or subscribe from multiple relays for redundancy; inter-relay gossip is out of scope |
| Candidate Participants | Subscribe to one or more relays; match incoming fingerprints against their custodied address set locally; on a match, request the full inquiry from the originator | Most 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 flow | Originator switches to Payment Request or Push Payment using the claimant's DID |
| Endpoint | Method | Purpose |
|---|---|---|
/pre-inquiries | POST | Submit a signed AddressFingerprintAdvertisement; returns 202 Accepted |
/fingerprints/snapshot | GET | Returns 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/stream | GET (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 | Transport | Direction | Notes |
|---|---|---|---|---|
| 1 | Address Fingerprint Advertisement | REST POST /pre-inquiries | originator → relay | Signed JWS; carries only the 4-byte fingerprint |
| 2a | Cuckoo filter snapshot | REST GET /fingerprints/snapshot | relay → candidate subscribers | Periodic poll; baseline state |
| 2b | Fingerprint stream | SSE GET /fingerprints/stream | relay → candidate subscribers | Live updates between snapshots |
| 3 | Inquiry Request | DIDComm | candidate → originator | Sent only on local fingerprint match; carries candidate DID + matched fingerprint |
| 4 | Address Ownership Inquiry | DIDComm | originator → candidate | Released after originator's trust policy passes; carries the full SHA-256 hash |
| 5 | Address Ownership Claim | DIDComm | candidate → originator | Sent only if the full hash matches a custodied address; false positives drop silently |
expiresAt reached without confirmation - terminalWhat 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.
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.
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.