GitHub
PATH ID

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.

TypeReasonable methods
phoneotp_sms, otp_voice
emaillink_click
bank_accountmicro_deposit, payee_name_check
lei, reg, taxregistry_lookup, document_review

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.

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:

TypeDefault
phonenetwork
emailnetwork
bank_accountprivate
lei, reg, taxpublic

Never move an entry towards public in a migration without an explicit request.


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.

On this page