Skip to content
Payvol

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);
Output, captured from a real run
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 value not-checked means the signature's shape was accepted and no cryptography was verified. It is not a pass. absent means 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.