GitHub
Transverse

SDK reference

Crossway

CrosswayClient — resolve destinations on other networks through Path Global, and verify every answer against its connector attestation.

LIVE — @pathprotocol/sdk 0.3.0. PIP-0014.

Private access. The Crossway API is operated by Path Global. A credential is issued per client and per registry, on request at pathglobal.finance. It is not a PATH member credential, and a member credential does not open Crossway.

new CrosswayClient(options)

import { CrosswayClient } from '@pathprotocol/sdk';

const crossway = new CrosswayClient({
  credential: { kid: 'client_2026_01', privateKeyHex: process.env.PATH_GLOBAL_KEY! },
});
OptionNotes
baseUrlDefaults to https://api.pathglobal.finance/crossway/v1 (CROSSWAY_BASE_URL)
credential{ kid, privateKeyHex }, issued by Path Global. Requests are signed as in Authentication
fetch, timeoutMsAs for PathClient

A separate client, on purpose. A Crossway answer is a registry's entry relayed by a connector, not a PATH member's signed terms, and the types keep the two apart.

From your server, never from an app. Same reason as for a member credential.

Discovery

const { candidates } = await crossway.discovery({ keyType: 'phone', key: '+5511987654321' });
// [{ registry: "pix.dict", rail: "pix", zone: "BR", operations: [...], granted: true }]

Which registry can know the key. No registry is queried: a candidate never means the key exists.

keyType: phone, email, tax, random, alias, iban, vpa.

finder(params)

SONAR then RESOLVER, composed on your side.

const reach = await crossway.finder({
  registry: 'pix.dict',
  keyType: 'phone',
  key: '+5511987654321',
});

reach.found;                     // true
reach.participant;               // { id: "12345678", name: "Example Bank S.A." }
reach.resolution?.rail;          // "pix"
reach.resolution?.asset_code;    // "BRL"
reach.resolution?.destination;   // { key_type: "phone", key: "+55…", account_type: "CACC" }
reach.proofs;                    // { sonar, resolver }

Stops after SONAR when the registry does not hold the key. sonar() and resolver() are available on their own. Each sends purpose: "payment_preparation" and a fresh nonce.

Budgeted and logged. Look a key up when a payment is being prepared. There is no batch call.

Hand resolution.destination to your provider on the external rail. Crossway does not move money.

payeeCheck(params)

const check = await crossway.payeeCheck({
  registry: 'pix.dict',
  keyType: 'phone',
  key: '+5511987654321',
  mode: 'verify',
  name: 'Alice Martin',
});
check.result; // "match" | "close_match" | "no_match" | "unavailable"

The scheme's own name check, in the two modes of the PATH payee check. Never another identifier.

Verifying

const { valid, reason } = await crossway.verify(reach.resolution!);
if (!valid) throw new Error(`Refusing an unattested destination: ${reason}`);

verify fetches Path Global's keys and the attestation the answer names, then checks:

  1. the answer's signature, under the connector key;
  2. the attestation's signature, under the root key;
  3. that the attestation is active, unexpired, issued to that connector, and covers that registry and that operation.

Without network calls, when you hold the keys and the attestation:

import { verifyCrosswayAnswer } from '@pathprotocol/sdk';

const result = verifyCrosswayAnswer(answer, attestation, keys);

Public reads

await crossway.registries();              // the connector register
await crossway.keys();                    // root and connector keys
await crossway.attestation('att_9kq3…');  // read at use: revocation is checked here

Errors

PathApiError, with crossway.* codes. crossway.budget.exhausted is not a rate limit — do not retry slower. See the API reference.

On this page