API reference
Subjects and identifiers
Creating subjects, registering verified identifiers, and withdrawing from the directory.
Requires a member credential.
POST /subjects
{ "subject_type": "natural_person", "external_ref": "cust_10482" }{
"id": "9f2c41a8-6d1e-4b07-9c3a-2e5f81d0a4b7",
"subject_type": "natural_person",
"created_at": "2026-09-09T10:00:00Z"
}subject_type is natural_person or legal_entity. It is recorded for your own use and does not
drive anything: the discoverability defaults below are keyed on identifier type.
external_ref is your own key. The protocol never interprets it and never returns it to anyone
else. No personal data belongs in a subject — names, dates of birth and documents stay in your
systems.
POST /identifiers
{
"subject_id": "9f2c41a8-6d1e-4b07-9c3a-2e5f81d0a4b7",
"identifier_type": "phone",
"identifier": "+12025550123",
"verification_method": "otp_sms",
"discoverability": "network"
}{
"id": "3b71c9e4-8a52-4d16-bf90-71c4e2a6d835",
"identifier_type": "phone",
"discoverability": "network",
"verified_at": "2026-09-09T10:02:00Z"
}The clear identifier is not stored
It is used to compute the hash and gone in the same request — not in a table, not in application logs, not in the search log. The operator keeps the hash.
Verification
verified_at is set only when verification_method is supplied. Register without one and the entry
still indexes, but a counterparty reading an attestation can tell the difference — which is the only
reason to record it.
| Type | Reasonable methods |
|---|---|
phone | otp_sms, otp_voice |
email | link_click |
bank_account | micro_deposit, payee_name_check |
lei, reg, tax | registry_lookup, document_review |
alias | Set by the server: alias_claim |
Format rules worth repeating
reg names its registry. reg:US:EIN:12-3456789, not reg:US:…. One country holds
several registers — a US company has both an EIN and a DUNS — and omitting the registry
makes them indistinguishable, or collides two different companies where numeric formats overlap.
bank_account carries its scheme. iban:DE89… or bban:…. Never inferred from a country.
An alias names its network. alias:openconnect:awa.diop. The network is the one this operator
belongs to. . and _ are removed before hashing, so awa.diop and awadiop are one alias.
Phone numbers are E.164. Normalisation is normative: two implementations that normalise differently split the directory in half with no error anywhere.
Discoverability defaults
Applied when you omit the field:
| Type | Default |
|---|---|
phone | network |
email | network |
bank_account | private |
lei, reg, tax | public |
alias | network |
Never move an entry towards public in a migration without an explicit request.
Aliases
PIP-0017. An alias is issued, not registered: POST /identifiers with
identifier_type: "alias" asks the network for it.
{
"subject_id": "9f2c41a8-6d1e-4b07-9c3a-2e5f81d0a4b7",
"identifier_type": "alias",
"identifier": "alias:openconnect:awa.diop"
}{
"id": "5c0e7a21-3f94-4b8d-a6e2-d18b9f4c7e30",
"identifier_type": "alias",
"discoverability": "network",
"verified_at": "2026-10-09T09:12:00Z",
"verification_method": "alias_claim"
}| Answer | When |
|---|---|
409 path.id.alias_unavailable | The alias cannot be issued. The answer does not say why |
409 path.id.alias_already_held | The subject already holds an alias in this network |
409 path.id.alias_change_limit | The subject has used the changes its network allows in twelve months |
POST /identifiers/{id}/release
{ "released": true, "quarantine_days": 90 }The alias leaves the directory at once. It is not issued again before the quarantine ends.
POST /identifiers/{id}/transfer
{ "to_member": "member-b" }{ "transferred": true, "holder": "member-b" }The subject moves to another member of the network and keeps its alias. The network records the new
holder. member-b then calls POST /identifiers with the same alias for the subject; a transfer is
not counted as a change.
POST /subjects/{id}/withdraw
{ "subject_id": "9f2c41a8-6d1e-4b07-9c3a-2e5f81d0a4b7", "withdrawn": true, "entries": 3 }Sets every identifier and every address of the subject to private, and removes each entry from the
directory index.
Applied at the subject level. Withdrawal covers every identifier of that subject.
Withdrawal does not delete the subject, and does not erase settlement history. Ending discoverability and destroying the record of what happened are different operations with different legal consequences.
POST /attestations
Requires the local member credential. expires_at is mandatory.
{
"subject_id": "9f2c41a8-6d1e-4b07-9c3a-2e5f81d0a4b7",
"claim": "kyc_level",
"value": "standard",
"expires_at": "2027-09-09T10:00:00Z"
}Closed claims: kyc_level, kyb_level, aml, sanctions, pep, risk_tier, identity_level.
GET /subjects/{id}/attestations lists this subject's claims for the local member.
GET /attestations/{reference}
Requires a member credential — any member. This is the execution-time check. Revoked →
path.id.attestation_revoked (410). Expired → path.id.attestation_expired (410).
POST /attestations/{reference}/revoke
Local member. Leaves the row; the next read answers 410.
POST /ownership-bindings
Requires the local member credential. target is hashed and forgotten.
{
"subject_id": "9f2c41a8-6d1e-4b07-9c3a-2e5f81d0a4b7",
"binding_type": "identifier",
"target_kind": "phone",
"target": "+12025550123",
"proof_method": "otp_sms"
}GET /ownership-bindings/{reference} is the execution-time check, same 410 rules.