Get started
Read it back
Someone sends you a canton: URI. This is how to find out what it asks for, and
what the answer does not cover.
Decode, then validate
// Reading a request someone sent you, and what the verdict does and does not say.
import { decode, validate } from '@payvol/core';
const uri =
'canton:payee%3A%3A1220aabbcc?instrument=DSO%3A%3A1220be58%2FAmulet&amount=12.50&reference=INV-2026-0042';
const request = decode(uri);
console.log('recipient ', request.recipient);
console.log('amount ', request.amount);
const result = validate(request);
console.log('valid ', result.valid);
if (result.valid) {
console.log('signature ', result.signatureVerified);
console.log('warnings ', result.warnings.length);
for (const w of result.warnings) console.log(' -', w.code, w.message);
}
// A request that is structurally wrong is refused by name, not by exception.
const bad = validate({ version: '0.1', type: 'transfer', recipient: 'payee::1220aabbcc' });
console.log('missing ', bad.valid, bad.valid ? '' : bad.code);
// And one whose deadline has passed.
const expired = validate({ ...request, expiry: '2020-01-01T00:00:00Z' });
console.log('expired ', expired.valid, expired.valid ? '' : expired.code); recipient payee::1220aabbcc
amount 12.50
valid true
signature absent
warnings 2
- RECIPIENT_NOT_A_PARTY_ID recipient "payee::1220aabbcc" is not a Canton party id, so no wallet can pay it. A party id is name::fingerprint, where the fingerprint is 68 hex characters beginning 1220 (for example alice::1220aaaaaaaa...). This request is well formed but not settleable as written.
- INSTRUMENT_ADMIN_NOT_A_PARTY_ID instrument "DSO::1220be58/Amulet" does not name a registry admin party. An instrument is admin/id, where admin is the party id of the registry that issues it and id is its symbol (for example DSO::1220.../Amulet). This request is well formed but not settleable as written.
missing false MISSING_INSTRUMENT
expired false EXPIRED
Output from @payvol/core version 0.4.0 and its sibling
packages, installed from npm, on 2026-10-18.
Three separate answers, and they are not the same question
validโ is it well formed against the profile? Structure only.-
warningsโ would a real network recognise the things it names? A request can be perfectly valid and unpayable. -
signatureVerifiedโ was a signature checked? The valuenot-checkedmeans the signature's shape was accepted and no cryptography was verified. It is not a pass.absentmeans there was no signature to check.
Collapsing the three into one boolean is the mistake this API is shaped to prevent. A wallet
that treats valid: true as "safe to pay" has skipped the other two.
Well formed, and not payable
The output above carries two warnings, and they are worth reading rather than skipping: the sample's recipient is not a Canton party id, and its instrument names no registry admin. The request encodes, decodes and validates. No wallet could pay it.
A party id is name::fingerprint, where the fingerprint is 68 hex characters
beginning 1220. An instrument is admin/id, where the admin is the
party id of the registry that issues it. Both come from the network you are on; the packages
cannot look them up and do not pretend to.
How it refuses
A refusal names its reason rather than throwing something generic, and the same reason codes appear in the conformance vectors, so every one is pinned by a case you can run. Reason codes lists them with an input that triggers each.