Skip to main content

TypeScript Types

@zkp2p/pay-shared exports every type on this page except the SDK-only CreateCheckoutResult, PayApiError, PayoutClientOptions, CreatePayoutOptions and ListPayoutsParams. The SDK re-exports request, response, order, payment, merchant and webhook payload types. Its payout re-exports are:

  • PayoutView, CreatePayoutRequest, CreatePayoutResponse, ListPayoutsResponse
  • PayoutStatusType, PayoutFundingStatusType, PayoutAttemptStatusType, PayoutCancelSourceType
  • PayoutWebhookPayload, PayoutWebhookEventTypeValue, PayoutPartialPayment, PayoutPartialFillView, PayoutSettlementBreakdown
  • The values PayoutStatus, PayoutFundingStatus, PayoutAttemptStatus, PayoutCancelSource, PAYOUT_WEBHOOK_VERSION and isPayoutWebhook

OrderWebhookPayload and WebhookPayload also come from the SDK. Rail, payout-provider, payout-kind, timeline, token-catalog and customer-checkout payout types come directly from @zkp2p/pay-shared, as do tier and chargeback types, PAYOUT_WEBHOOK_EVENT_TYPES and OrderWebhookEventTypeValue.

import { PayApiError, PayoutStatus } from '@zkp2p/pay-sdk';
import type {
CreateOrderRequest,
CreateOrderResponse,
CheckoutOrder,
OpenAmountInput,
CheckoutPayment,
MerchantProfile,
ReferralSplitConfig,
PayoutView,
CreatePayoutRequest,
CreatePayoutResponse,
ListPayoutsResponse,
PayoutWebhookPayload,
PayoutClientOptions,
CreatePayoutOptions,
ListPayoutsParams,
PayoutPartialFillView,
PayoutPartialPayment,
PayoutSettlementBreakdown,
} from '@zkp2p/pay-sdk';
import type {
PayoutRailType,
PayoutProviderType,
PayoutKindType,
PayoutCryptoDestination,
MerchantTierName,
PaymentChargebackFact,
} from '@zkp2p/pay-shared';
import { isPayoutWebhook, type WebhookPayload } from '@zkp2p/pay-sdk';

Create Order Request​

type CreateOrderRequest = (
| {
requestedUsdcAmount: string;
requestedFiatAmount?: never;
requestedFiatCurrency?: never;
}
| {
requestedUsdcAmount?: never;
requestedFiatAmount: string;
requestedFiatCurrency: string;
}
| {
requestedUsdcAmount?: never;
requestedFiatAmount?: never;
requestedFiatCurrency?: never;
// The customer types the amount in checkout. See /sdk/open-amount.
openAmount: OpenAmountInput;
}
) & {
idempotencyKey?: string;
destinationAddress?: string;
destinationToken?: string;
destinationChainId?: number;
feePayer?: FeePayerType;
buyerFeeShareBps?: number;
// Per-order dynamic-orders opt-out: false disables for this order;
// omitted inherits the merchant default; true with the merchant config
// disabled rejects creation (400 DYNAMIC_ORDERS_DISABLED). Snapshotted
// at order creation.
dynamicOrdersEnabled?: boolean;
enabledRails?: string[];
successUrl?: string | null;
cancelUrl?: string | null;
notes?: Record<string, unknown> | null;
};
type OpenAmountInput = {
currency: string; // ISO currency of minAmount, maxAmount and presets
minAmount?: string; // at most 2 decimals, in currency
maxAmount?: string;
presets?: string[]; // 1 to 6 suggested amounts
};

createCheckout forwards idempotencyKey, 8–128 letters, digits, underscores or hyphens. See idempotent order creation.

Create Order Response​

type CreateOrderResponse = {
order: CheckoutOrder;
} & (
| { orderToken: string; idempotentReplay?: never }
| { orderToken: null; idempotentReplay: true }
);

Checkout Result (SDK helper)​

type CreateCheckoutResult = CreateOrderResponse & {
checkoutUrl: string | null;
};

Checkout Order​

CheckoutOrder is a union on amountMode. Narrow it before reading the amounts:

type CheckoutOrder = FixedAmountCheckoutOrder | OpenAmountCheckoutOrder;

type FixedAmountCheckoutOrder = CheckoutOrderBase & {
amountMode: 'FIXED';
requestedUsdcAmount: string;
remainingUsdcAmount: string;
};

type OpenAmountCheckoutOrder = CheckoutOrderBase & {
amountMode: 'OPEN';
// null until the customer starts a payment, which saves the amount
requestedUsdcAmount: string | null;
remainingUsdcAmount: string | null;
openAmount: OpenAmountConfig;
// 0 until the first save; goes up by one per save
amountVersion: number;
};

// Stored normalized: upper-case currency, 2-decimal amounts, presets ascending.
type OpenAmountConfig = {
currency: string;
minAmount: string | null;
maxAmount: string | null;
presets: string[];
};

type CheckoutOrderBase = {
id: string;
merchantId: string;
status: CheckoutOrderStatusType;
// Additive post-settlement facts; never change `status`.
chargebackStatus: OrderChargebackStatusType;
chargebacks: PaymentChargebackFact[];
refundStatus: CheckoutOrderRefundStatusType;
refundAmountUsdc: string | null;
refundDepositId: string | null;
refundTransactionHash: string | null;
refundMetadata: unknown | null;
inPersonCheckout: boolean;
// Hosted aggregates only: fee-grossed Apple Pay charge for the remaining balance.
// Null when Apple Pay is disabled or pricing cannot be resolved.
applePayChargeUsdcAmount?: string | null;
destinationAddress: string;
destinationToken: string;
destinationChainId: string;
feePayer: FeePayerType;
buyerFeeShareBps: number;
enabledRails: string[];
successUrl: string | null;
cancelUrl: string | null;
notes: unknown | null;
metadata: unknown | null;
// Merchant-requested fiat price; non-null only for pristine, un-resized fiat-input orders.
requestedFiat?: { amount: string; currency: string } | null;
bridgeInfo?: OrderBridgeInfo | null;
referralSplitConfig: ReferralSplitConfig | null;
cancelledAt: string | null;
completedAt: string | null;
createdAt: string;
updatedAt: string | null;
};

On the hosted read (GET /api/v1/orders/{orderId}), an open-amount order's openAmount also carries effectiveMin and effectiveMax (the allowed range in its currency; both null while the amount is locked, once the order is FULFILLED or CANCELLED, or while the exchange rate is unavailable) and savedInput ({ amount, currency } the customer saved, in the currency they typed it in, or null), and the order carries amountLock: null while the amount can change, otherwise PAYMENT_IN_PROGRESS, PAYMENT_MAY_SETTLE or PAID.

The wire object carries one field this type does not declare: cryptoReferralSplitConfig: ReferralSplitConfig | null, the referral split that applies when the order is paid over a crypto rail. It is on every order the API returns, from both reads and every webhook, not only on merchant list rows.

Payment​

type CheckoutPayment = {
id: string;
orderId: string;
status: CheckoutPaymentStatusType;
// Additive post-settlement facts; never change `status`.
chargebackStatus: PaymentChargebackStatusType;
chargeback: PaymentChargebackFact | null;
penalties: PaymentPenalty[];
rail: string;
// Buyer's Zelle bank pick ('zelle-chase' | 'zelle-bofa' | 'zelle-citi').
// Display metadata only; the rail is always the base platform (e.g. 'zelle').
// Null for non-Zelle rails.
paymentMethodId: string | null;
proofMode: ProofMode;
attestation: { serviceUrl: string };
currency: string;
payTo: string;
// Payment partner rating in the 1.0 to 5.0 range, rounded to one decimal.
recipientRating: number;
quote: CheckoutPaymentQuote;
quoteExpiresAt: string;
memoPolicy: CheckoutPaymentMemoPolicyType;
suggestedMemo: string | null;
paymentAmount: string;
currencyPerUsdRate: string;
netSettledUsdcAmount: string | null;
totalUsdcFeeAmount: string;
referralFees: SettlementReferralFeeEntry[] | null;
railIdentifier: string | null;
fulfillTransaction: string | null;
errorCode?: string | null;
errorMessage?: string | null;
completedAt: string | null;
createdAt: string;
updatedAt: string | null;
};

Chargeback Fact​

Carried on CheckoutOrder.chargebacks, CheckoutPayment.chargeback, and the data.trigger of chargeback webhooks. A chargeback is additive; see chargeback status.

type PaymentChargebackFact = {
paymentId: string;
intentHash: string;
disputeId: string;
status: 'CHARGEBACKED';
compensatedUsdcAmount: string;
disputedAt: string;
disputeTxHash: string;
refundOverlap:
| 'NONE'
| 'PENDING_HALTED'
| 'SUBMITTING_OR_UNKNOWN'
| 'ALREADY_SUBMITTED'
| 'ALREADY_REFUNDED';
bridgeOrSweepOverlap: null | {
paymentBridgeId: string;
status: 'PENDING' | 'SUBMITTED' | 'COMPLETED' | 'FAILED';
};
};

Payment Penalty​

const PaymentPenaltyKind = {
PURCHASE_PROTECTION: 'PURCHASE_PROTECTION',
CROSS_CURRENCY: 'CROSS_CURRENCY',
} as const;
type PaymentPenaltyKindType = (typeof PaymentPenaltyKind)[keyof typeof PaymentPenaltyKind];

type PaymentPenalty = {
kind: PaymentPenaltyKindType;
penaltyBps: number;
originalAmount: string;
attestedAmount: string;
};

Carried on CheckoutPayment.penalties in REST responses and on data.payment of every webhook that carries a payment.

CheckoutPayment.penalties is always present and is an empty array when no penalties apply. penaltyBps is authoritative for the rate that was applied; the rates are set by the attestation service and can change. Use the returned penaltyBps for purchase protection and cross-currency reductions; do not infer a fixed rate from the payment platform. originalAmount and attestedAmount are the pre- and post-penalty amounts as 2-decimal strings in payment.currency. Entries appear in application order: CROSS_CURRENCY first, then PURCHASE_PROTECTION, so a second entry's originalAmount equals the first entry's attestedAmount. For cross-currency entries, originalAmount is the converted amount in the payment currency, never the source-currency amount.

paymentAmount depends on who pays fees. On merchant-pays-fees orders it is the pre-penalty amount the customer sent (the first entry's originalAmount), so the order still fulfils in full and the merchant absorbs the penalty. On customer-pays-fees orders it is the penalised amount actually credited (the last entry's attestedAmount), so the order shows the shortfall.

Merchant Types​

type MerchantTierName = 'BASE' | 'PRO' | 'CONCIERGE';

type MerchantProfile = {
id: string;
name: string;
logoUrl: string | null;
industryType: string | null;
industryOtherText: string | null;
integrationPath: MerchantIntegrationPathValue | null;
onboardingCompletedAt: string | null;
onboardingSkippedAt: string | null;
apiKey: string;
// Canonical CIDR entries allowed to use the API key; empty means the
// allowlist is off. See /api#ip-allowlist.
apiKeyIpAllowlist: string[];
environment: MerchantEnvironmentType;
sandboxMerchantId: string | null;
inPersonCheckoutEnabled: boolean;
evmWalletAddress: string | null;
solanaWalletAddress: string | null;
v1EvmWalletAddress: string | null;
v1SolanaWalletAddress: string | null;
// null until a plan is selected; live order creation needs one.
tier: MerchantTierName | null;
// The Master Merchant Account merchant that created this sub merchant; null unless the merchant is a Master Merchant Account sub-merchant.
masterMerchantId: string | null;
createdAt: string;
updatedAt: string | null;
merchantConfig: MerchantConfig | null;
merchantUsers: MerchantUser[];
};

Webhook Types​

export type WebhookEventTypeValue = typeof WebhookEventType[keyof typeof WebhookEventType];

/** Payout events carry a PayoutView, not the order envelope. */
export const PAYOUT_WEBHOOK_EVENT_TYPES = [
WebhookEventType.PAYOUT_ORDER_CREATED,
WebhookEventType.PAYOUT_ORDER_CANCELLED,
WebhookEventType.PAYOUT_ORDER_PARTIALLY_FUNDED,
WebhookEventType.PAYOUT_ORDER_FUNDED,
WebhookEventType.PAYOUT_ORDER_MATCHED,
WebhookEventType.PAYOUT_ORDER_PARTIALLY_PAID,
WebhookEventType.PAYOUT_ORDER_SETTLED,
WebhookEventType.PAYOUT_ORDER_EXPIRED,
] as const;
export type PayoutWebhookEventTypeValue = typeof PAYOUT_WEBHOOK_EVENT_TYPES[number];
export const DISPUTE_WEBHOOK_EVENT_TYPES = [
WebhookEventType.DISPUTE_OPENED,
WebhookEventType.DISPUTE_PAID,
WebhookEventType.DISPUTE_ESCALATED,
] as const;
export type DisputeWebhookEventTypeValue = typeof DISPUTE_WEBHOOK_EVENT_TYPES[number];
export type OrderWebhookEventTypeValue = Exclude<WebhookEventTypeValue, PayoutWebhookEventTypeValue | DisputeWebhookEventTypeValue>;

type OrderWebhookPayload = {
id: string;
type: OrderWebhookEventTypeValue; // every event type except PAYOUT_ORDER_* and DISPUTE_*
timestamp: string;
data: {
order: CheckoutOrder | null;
payment: CheckoutPayment | null;
refund: Record<string, unknown> | null;
paymentBridge: Record<string, unknown> | null;
// Chargeback events only: why the order aggregate changed.
trigger?:
| { type: 'CHARGEBACK_APPLIED'; chargeback: PaymentChargebackFact }
| { type: 'PAYMENT_SETTLED'; paymentId: string };
// ORDER_RESIZED only.
resize?: {
previousAmountUsdc: string;
newAmountUsdc: string;
};
// ORDER_AMOUNT_SET only.
amountChange?: {
previousAmountUsdc: string | null;
newAmountUsdc: string;
fiat: { amount: string; currency: string }; // as typed, in the customer's selected currency
amountVersion: number;
};
};
};

const PAYOUT_WEBHOOK_VERSION = 2; // bumped only on a breaking payout payload change

type PayoutPartialFillView = {
fiat: PayoutFiatValue | null; // exact bound-rate fiat, null when unknown
amount: string; // decimal USDC this buyer paid
txHash: string;
paidAt: string;
};

// Where a SETTLED payout's USDC ended up; decimal USDC, "0.00" for a part that did not happen.
type PayoutSettlementBreakdown = {
buyerPaidAmount: string; // paid by buyers through the escrow, across every listing
buyerPaidFiat: PayoutFiatValue[] | null; // first-paid order; null if any unknown, [] if no buyer paid
sentAmount: string; // USDC sent for a crypto payout or a remainder
sentTo: string | null; // customer recipient: EVM lower-case, otherwise canonical; never a deposit address
returnedAmount: string; // left in, or returned to, the customer's Peer wallet
dustAmount: string; // swept by the escrow as dust when it closed the deposit
refundFeeAmount: string; // USDC retained on bridge refunds; "0.00" when none
};

// One buyer payment that left part of the payout unpaid; decimal USDC.
type PayoutPartialPayment = {
expectedAmount: string; // the payout's depositAmount
paidAmount: string; // paid so far, this payment included
remainingAmount: string; // expectedAmount − paidAmount
fill: { amount: string; txHash: string; fiat: PayoutFiatValue | null };
};

type PayoutWebhookPayload =
| {
id: string;
type: Exclude<PayoutWebhookEventTypeValue, 'PAYOUT_ORDER_PARTIALLY_PAID'>; // every other PAYOUT_ORDER_*
timestamp: string;
version: typeof PAYOUT_WEBHOOK_VERSION;
data: PayoutView;
}
| {
id: string;
type: 'PAYOUT_ORDER_PARTIALLY_PAID';
timestamp: string;
version: typeof PAYOUT_WEBHOOK_VERSION;
data: PayoutView;
partialPayment: PayoutPartialPayment;
};

type DisputeWebhookPayload = {
id: string; // dispute_<disputeId>_<type>
type: DisputeWebhookEventTypeValue;
timestamp: string;
version: 1;
data: DisputeWebhookData;
};

type WebhookPayload = OrderWebhookPayload | PayoutWebhookPayload | DisputeWebhookPayload;

function isPayoutWebhook(payload: WebhookPayload): payload is PayoutWebhookPayload;
function isDisputeWebhook(payload: WebhookPayload): payload is DisputeWebhookPayload;

Narrow before reading data: isPayoutWebhook(payload) (or a check on payload.type) gives a PayoutWebhookPayload, whose data is a PayoutView; isDisputeWebhook(payload) gives a DisputeWebhookPayload. After excluding both families, it is an OrderWebhookPayload. See payout events and dispute events. partialPayment is present only on PAYOUT_ORDER_PARTIALLY_PAID. PayoutView.paidAmount is the decimal USDC buyers have paid the customer across every listing, partial payments included. PayoutView.partialFills is a PayoutPartialFillView[]. PayoutView.settlement is a PayoutSettlementBreakdown, set only when status is SETTLED; otherwise it is null. PayoutView.payoutTransfer is set only when settled with a sent amount, including a send-to-address remainder. amount is delivered destination tokens, while settlement.sentAmount remains decimal USDC:

payoutTransfer: {
chainId: number;
tokenAddress: string;
decimals: number;
address: string; // canonical customer recipient (EVM checksummed; settlement.sentTo is lower-case), never a deposit address
amount: string; // delivered decimal destination token
txHash: string; // delivering transaction on chainId
usdcTxHash: string; // the payout's USDC transfer on Base; settlement.sentAmount is its amount
provider: PayoutProviderType | null;
} | null;

Direct Base USDC uses chain 8453, six decimals and provider: null, with equal txHash and usdcTxHash. A bridge uses provider: 'RELAY' or provider: 'NEAR_INTENTS' and the destination transaction. NEAR Intents delivers ZEC to transparent Zcash addresses; its txHash is a 64-hex Zcash txid without 0x, while usdcTxHash is the Base transfer.

Seller dispute types​

The SDK re-exports DisputeWebhookPayload, DisputeWebhookEventTypeValue, DisputeWebhookData, DisputeView, DisputeStatusType, DisputeStatus, DISPUTE_WEBHOOK_VERSION, and isDisputeWebhook.

const DisputeStatus = { OPEN: 'OPEN', PAID: 'PAID', ESCALATED: 'ESCALATED' } as const;
type DisputeStatusType = typeof DisputeStatus[keyof typeof DisputeStatus];
const DISPUTE_WEBHOOK_VERSION = 1;

type DisputeWebhookData = {
disputeId: string;
orderId: string;
paymentId: string;
intentHash: string;
amountUsdc: string; // raw 6-decimal units, "12500000" = 12.5 USDC
caseAmount: string; // 2-decimal fiat amount, "12.40"
caseCurrency: string;
sellerAddress: string;
status: DisputeStatusType;
dueAt: string; // ISO 8601
paidTxHash: string | null;
};

DisputeView adds merchant read fields such as id, merchantId, rail, stakeStatusAtOpen, fulfillTxHash, lifecycle timestamps, orderRefunded, and activeAttempt. Disputes are merchant-paid; chargebacks remain stake compensation recorded on-chain.

Payout types​

These merchant-facing types come from @zkp2p/pay-shared and are re-exported by @zkp2p/pay-sdk. See Payouts for the client calls.

/** Merchant-facing payout. Never includes the Peer fee, the customer's email, wallet or payee handle. */
export interface PayoutView {
payoutCurrency: PayoutCurrencyType | null; // null for crypto or no method
payoutId: string;
merchantReference: string | null;
status: PayoutStatusType;
fundingStatus: PayoutFundingStatusType;
payout: { amount: string; chainId: number; tokenAddress: string; decimals: number };
merchantFee: { bps: number; amount: string };
funding: {
chainId: number;
tokenAddress: string;
decimals: number;
/** Quoted amount to send, decimal funding token. */
amount: string;
/** Sent before the deadline, decimal funding token. */
sentAmount: string;
/** amount − sentAmount, never below zero; decimal funding token. */
missingAmount: string;
/** Payout plus buffer: the USDC the quote delivers, decimal USDC. */
expectedAmount: string;
/** Reached the customer's wallet on Base, decimal USDC. */
receivedAmount: string;
/** Null once the payout can no longer be funded. */
depositAddress: string | null;
/** Funding deadline; each on-time arrival moves it to at least the funding TTL (24 hours by default) after that check. */
quoteExpiresAt: string;
};
depositAmount: string | null;
/** What buyers have paid across every listing, partial fills included. */
paidAmount: string;
partialFills: PayoutPartialFillView[];
/** Only when status is SETTLED. */
settlement: PayoutSettlementBreakdown | null;
attempt: { kind: PayoutKindType; rail: string | null; status: PayoutAttemptStatusType } | null;
/** Only when settled with a sent amount, including a send-to-address remainder. */
payoutTransfer: {
/** Where the customer received it. */
chainId: number; tokenAddress: string; decimals: number; address: string;
/** Delivered, decimal destination token. */
amount: string;
/** The delivering transaction on chainId. */
txHash: string;
/** The USDC transfer on Base; settlement.sentAmount is its amount. */
usdcTxHash: string;
/** The bridge provider (RELAY or NEAR_INTENTS) when bridged; null for USDC on Base, where txHash = usdcTxHash. */
provider: PayoutProviderType | null;
} | null;
cancelSource: PayoutCancelSourceType | null;
returnUrl: string | null;
expiresAt: string;
fundedAt: string | null;
settledAt: string | null;
cancelledAt: string | null;
createdAt: string;
}

/** A replay with the same key and body returns the current checkout link, marked `idempotentReplay: true`. */
export type CreatePayoutResponse = PayoutView & { checkoutUrl: string; idempotentReplay: boolean };

export interface ListPayoutsResponse {
items: PayoutView[];
page: number;
limit: number;
total: number;
}

PayoutStatus, PayoutFundingStatus, PayoutCancelSource and PayoutAttemptStatus are values exported by both packages, along with their Type aliases. PayoutKind and PayoutKindType come only from @zkp2p/pay-shared.

export const PayoutStatus = {
AWAITING_FUNDING: 'AWAITING_FUNDING',
FUNDED: 'FUNDED',
READY: 'READY',
LISTING: 'LISTING',
PAYING: 'PAYING',
SETTLED: 'SETTLED',
CANCELLED: 'CANCELLED',
EXPIRED: 'EXPIRED',
} as const;
export type PayoutStatusType = typeof PayoutStatus[keyof typeof PayoutStatus];

export const PayoutFundingStatus = {
AWAITING_FUNDING: 'AWAITING_FUNDING',
PARTIALLY_FUNDED: 'PARTIALLY_FUNDED',
FUNDED: 'FUNDED',
FUNDING_EXPIRED: 'FUNDING_EXPIRED',
} as const;
export type PayoutFundingStatusType = typeof PayoutFundingStatus[keyof typeof PayoutFundingStatus];

export const PayoutCancelSource = {
MERCHANT: 'MERCHANT',
PEER_APP: 'PEER_APP',
} as const;
export type PayoutCancelSourceType = typeof PayoutCancelSource[keyof typeof PayoutCancelSource];

export const PayoutKind = { ESCROW: 'ESCROW', CRYPTO: 'CRYPTO' } as const;
export type PayoutKindType = typeof PayoutKind[keyof typeof PayoutKind];

export const PayoutAttemptStatus = {
ACTIVE: 'ACTIVE',
CLOSED: 'CLOSED',
SETTLED: 'SETTLED',
FAILED: 'FAILED',
} as const;
export type PayoutAttemptStatusType = typeof PayoutAttemptStatus[keyof typeof PayoutAttemptStatus];

The following client types are SDK-only: import them from @zkp2p/pay-sdk.

/** Server-side only: every call sends your secret API key. */
export type PayoutClientOptions = {
apiBaseUrl: string;
apiKey: string;
fetcher?: typeof fetch;
signal?: AbortSignal;
};

export type CreatePayoutOptions = {
/** Stable per payout (e.g. your withdrawal id); a retry with the same key and body returns the same payout. */
idempotencyKey: string;
};

export type ListPayoutsParams = {
customerEmail?: string;
status?: PayoutStatusType;
merchantReference?: string;
page?: number;
limit?: number;
};

Payout currency types​

Players can receive catalog currencies on PayPal and Revolut; you still fund and see USD. All canonical payout, fee, limit, refund and settlement accounting remains USDC. Fiat amounts are information only. PayoutCurrency, PayoutCurrencyType and PayoutFiatValue are exported by both the SDK and shared package. The remaining selector and pricing types below are from @zkp2p/pay-shared.

export const PayoutCurrency = {
USD: 'USD', EUR: 'EUR', GBP: 'GBP', AUD: 'AUD', CAD: 'CAD', CHF: 'CHF',
CNY: 'CNY', MXN: 'MXN', NZD: 'NZD', SGD: 'SGD', TRY: 'TRY', ZAR: 'ZAR',
} as const; // Derived from PAYOUT_CURRENCY_INFO in the implementation.
export type PayoutCurrencyType = typeof PayoutCurrency[keyof typeof PayoutCurrency];
export type PayoutOracleCurrencyType = Exclude<PayoutCurrencyType, 'USD'>;
export interface PayoutFiatValue {
currency: PayoutCurrencyType;
amount: string; // two-decimal fiat amount
}
export interface CashoutFxRate {
currency: PayoutOracleCurrencyType;
rate: string; // six-decimal fiat per USD; display only
}
export interface CashoutPayoutCurrencyOption {
currency: PayoutCurrencyType;
rails: PayoutFiatRailType[];
rate: CashoutFxRate | null; // null for USD or an unavailable read
estimate: PayoutFiatValue | null;
}
export interface CashoutFiatPricing {
currency: PayoutOracleCurrencyType;
rate: CashoutFxRate | null;
unpaid: PayoutFiatValue | null;
listed: PayoutFiatValue | null; // null without a live listing
}

CashoutCheckoutState adds payoutCurrencies: CashoutPayoutCurrencyOption[], fiatPricing: CashoutFiatPricing | null (null for USD, crypto or no method), and buyerPayment: PayoutFiatValue | null (exact while PAYING with a known bound rate). Its partialPayment.paidFiat and PayoutSettlementBreakdown.buyerPaidFiat are PayoutFiatValue[] | null: one total per currency in first-paid order, null if any payment's currency or bound rate is unknown, and [] if no buyer paid. PayoutPartialFillView.fiat and webhook PayoutPartialPayment.fill.fiat are null when that payment's fiat is unknown. A failed estimate never hides a currency or blocks saving or confirming a payout.

Fiat CashoutSarMethod, CashoutBuyerProofMethod and PayoutMethodView carry currency: PayoutCurrencyType. The strict SetCashoutPayoutMethodRequest Revolut variant is { rail: 'revolut'; payeeHandle: string; currency: PayoutCurrencyType }. PayPal requires { rail: 'paypal'; payeeHandle: string; paypalEmail: string; currency: PayoutCurrencyType }. The API validates currency against each rail's catalog. Other request variants are unchanged and do not accept a currency. Old PayPal/Revolut requests without it receive 400.

Every rail catalog currency is available by default. Shared catalog helpers include PAYOUT_RAIL_CURRENCIES, PAYOUT_ORACLE_CURRENCIES, PAYOUT_CURRENCY_NAMES, isPayoutCurrency, payoutRailPaysCurrency and isPayoutMultiCurrencyRail. payoutFiatCents computes ceil(USDC base units × 18-decimal bound rate / 10^22); formatPayoutFiatCents formats two decimals, and formatPayoutRate formats six decimals, rounded half up.

Payout rail and destination types​

PayoutRail holds the six fiat rails followed by thirteen crypto network rails, in this order. The customer sees the crypto rails as one Crypto option. crypto is not accepted in requests. PAY_CRYPTO_TOKEN_SYMBOLS lists catalog symbols; each symbol exists only on some networks. See the per-network coin list.

export const PayoutFiatRail = {
VENMO: 'venmo',
CASHAPP: 'cashapp',
PAYPAL: 'paypal',
ZELLE: 'zelle',
REVOLUT: 'revolut',
CHIME: 'chime',
} as const;
export type PayoutFiatRailType = typeof PayoutFiatRail[keyof typeof PayoutFiatRail];

/** One rail per crypto payout network, with checkout's rail ids; the customer sees them as one "Crypto" option. */
export const PayoutCryptoRail = {
RELAY_1: 'relay_1',
RELAY_10: 'relay_10',
RELAY_56: 'relay_56',
RELAY_137: 'relay_137',
RELAY_480: 'relay_480',
RELAY_999: 'relay_999',
RELAY_5042: 'relay_5042',
RELAY_8453: 'relay_8453',
RELAY_42161: 'relay_42161',
RELAY_8253038: 'relay_8253038',
RELAY_728126428: 'relay_728126428',
RELAY_792703809: 'relay_792703809',
NEAR_INTENTS_133701: 'near_intents_133701',
} as const;
export type PayoutCryptoRailType = typeof PayoutCryptoRail[keyof typeof PayoutCryptoRail];

/** Every payout rail, in rail order: fiat, then the crypto networks. */
export const PayoutRail = { ...PayoutFiatRail, ...PayoutCryptoRail } as const;
export type PayoutRailType = PayoutFiatRailType | PayoutCryptoRailType;
export const PayoutProvider = { RELAY: 'RELAY', NEAR_INTENTS: 'NEAR_INTENTS' } as const;
export type PayoutProviderType = typeof PayoutProvider[keyof typeof PayoutProvider];
export const CashoutPayoutBridgeStatus = {
QUOTED: 'QUOTED', NOT_SENT: 'NOT_SENT', BRIDGING: 'BRIDGING', DELIVERED: 'DELIVERED', REFUNDED: 'REFUNDED',
} as const;
export type CashoutPayoutBridgeStatusType = typeof CashoutPayoutBridgeStatus[keyof typeof CashoutPayoutBridgeStatus];

export const PAY_CRYPTO_TOKEN_SYMBOLS = [
'BTC',
'USDC',
'USDT',
'ETH',
'PYUSD',
'SOL',
'BNB',
'HYPE',
'WBTC',
'USDH',
'ZEC',
] as const;
export type PayCryptoTokenSymbol = (typeof PAY_CRYPTO_TOKEN_SYMBOLS)[number];

/** A crypto payout's coin, network and the customer's address there. */
export interface PayoutCryptoDestination {
chainId: number;
/** The shared catalog's address for the token on chainId. */
tokenAddress: string;
symbol: PayCryptoTokenSymbol;
decimals: number;
/** Canonical: EVM checksummed, bech32 lower-case, base58 as entered. */
address: string;
}

/** Body of the crypto payout-method, payout-quote and send-to-address requests. The rail names the network. */
export interface CashoutPayoutDestinationRequest { rail: PayoutCryptoRailType; tokenAddress: string; address: string }

/** A crypto payout method: the network's rail and where on it the customer takes the payout. */
export interface PayoutCryptoMethod { rail: PayoutCryptoRailType; destination: PayoutCryptoDestination }

/** POST /api/v1/cashout-checkout/:id/payout-quote. Not a promise. */
export interface CashoutPayoutQuote {
destination: PayoutCryptoDestination;
/** Decimal USDC that would leave the customer's wallet. */
usdcAmount: string;
/** Decimal destination token; equals usdcAmount for USDC on Base. */
estimatedAmount: string;
quotedAt: string;
}

/** The customer's bridged payout, newest attempt first; never NOT_SENT. */
export interface CashoutPayoutBridgeView {
provider: PayoutProviderType;
status: Exclude<CashoutPayoutBridgeStatusType, typeof CashoutPayoutBridgeStatus.NOT_SENT>;
destination: PayoutCryptoDestination;
usdcAmount: string;
/** The binding quote's output, decimal destination token. */
estimatedAmount: string;
deliveredAmount: string | null;
destinationTxHash: string | null;
/** Decimal USDC. */
refundedAmount: string | null;
refundTxHash: string | null;
}

CashoutPayoutBridgeView.status excludes NOT_SENT: the checkout reports payoutBridge: null for that state. Bridge estimates and deliveries are decimal destination tokens; refunds and usdcAmount are decimal USDC. quotedAt is an ISO timestamp. Request destination addresses are canonicalized and checked against the payout’s current rails.

export type PayoutMethodView =
| { rail: PayoutFiatRailType; handle: string; currency: PayoutCurrencyType }
| PayoutCryptoMethod;

export interface CreatePayoutRequest {
merchantReference?: string;
customerEmail: string;
payout: { amount: string; chainId: number; tokenAddress: string };
funding: { chainId: number; tokenAddress: string; refundAddress?: string };
returnUrl?: string;
rails?: PayoutRailType[]; // non-empty, unique; limits effective rails, never widens
}

export const PayoutTimelineEventType = {
CREATED: 'CREATED',
PARTIALLY_FUNDED: 'PARTIALLY_FUNDED',
FUNDED: 'FUNDED',
FUNDING_EXPIRED: 'FUNDING_EXPIRED',
SIGNED_IN: 'SIGNED_IN',
METHOD_READY: 'METHOD_READY',
LISTED: 'LISTED',
TRANSFER_SENT: 'TRANSFER_SENT',
MATCHED: 'MATCHED',
/** A buyer paid part of the listing; the rest is listed again or left for the customer. */
PARTIALLY_PAID: 'PARTIALLY_PAID',
/** Pay set the deposit's intent range to what is left, so another buyer can take it. */
RELISTED: 'RELISTED',
BUYER_DROPPED: 'BUYER_DROPPED',
RECONNECT_NEEDED: 'RECONNECT_NEEDED',
/** The listing came back to the customer's wallet so they can pick another payout method. */
LISTING_WITHDRAWN: 'LISTING_WITHDRAWN',
SETTLED: 'SETTLED',
CANCELLED: 'CANCELLED',
FUNDING_ISSUE: 'FUNDING_ISSUE',
PAYOUT_REFUNDED: 'PAYOUT_REFUNDED',
} as const;
export type PayoutTimelineEventTypeValue = typeof PayoutTimelineEventType[keyof typeof PayoutTimelineEventType];

export interface PayoutTimelineEntry {
type: PayoutTimelineEventTypeValue; // includes PAYOUT_REFUNDED
text: string;
txHash: string | null;
txChainId: number | null; // the chain txHash is on; null when it is not linked
occurredAt: string;
}

CashoutCheckoutState.method uses PayoutCryptoMethod for crypto; lastPayoutTarget is PayoutMethodView | null. settleTx is { chainId: number; txHash: string } | null, and payoutBridge is CashoutPayoutBridgeView | null. See the customer API.

SDK Errors​

Thrown by every SDK call when the API answers with a non-2xx status or a success: false envelope. See error handling.

class PayApiError extends Error {
readonly statusCode: number;
readonly errorCode?: string;
readonly responseObject?: unknown;
readonly fieldErrors?: Readonly<Record<string, readonly string[]>>;
readonly formErrors?: readonly string[];
}

Constants​

const FeePayer = {
MERCHANT: 'MERCHANT',
PAYEE: 'PAYEE',
SPLIT: 'SPLIT',
} as const;

const CheckoutOrderStatus = {
CREATED: 'CREATED',
PARTIALLY_FULFILLED: 'PARTIALLY_FULFILLED',
FULFILLED: 'FULFILLED',
CANCELLED: 'CANCELLED',
} as const;

const CheckoutPaymentStatus = {
CREATED: 'CREATED',
SETTLED: 'SETTLED',
CANCELLED: 'CANCELLED',
EXPIRED: 'EXPIRED',
FAILED: 'FAILED',
} as const;

const OrderChargebackStatus = {
NONE: 'NONE',
PARTIALLY_CHARGEBACKED: 'PARTIALLY_CHARGEBACKED',
CHARGEBACKED: 'CHARGEBACKED',
} as const;

const PaymentChargebackStatus = {
NONE: 'NONE',
CHARGEBACKED: 'CHARGEBACKED',
} as const;

const CheckoutOrderRefundStatus = {
NONE: 'NONE',
PENDING: 'PENDING',
COMPLETED: 'COMPLETED',
} as const;

const WebhookEventType = {
DISPUTE_OPENED: 'DISPUTE_OPENED',
DISPUTE_PAID: 'DISPUTE_PAID',
DISPUTE_ESCALATED: 'DISPUTE_ESCALATED',
ORDER_CREATED: 'ORDER_CREATED',
ORDER_FULFILLED: 'ORDER_FULFILLED',
ORDER_CANCELLED: 'ORDER_CANCELLED',
ORDER_RESIZED: 'ORDER_RESIZED',
ORDER_AMOUNT_SET: 'ORDER_AMOUNT_SET',
PAYMENT_CREATED: 'PAYMENT_CREATED',
PAYMENT_SETTLED: 'PAYMENT_SETTLED',
PAYMENT_CANCELLED: 'PAYMENT_CANCELLED',
PAYMENT_EXPIRED: 'PAYMENT_EXPIRED',
PAYMENT_FAILED: 'PAYMENT_FAILED',
REFUND_PENDING: 'REFUND_PENDING',
REFUND_COMPLETED: 'REFUND_COMPLETED',
PAYMENT_BRIDGE_PENDING: 'PAYMENT_BRIDGE_PENDING',
PAYMENT_BRIDGE_SUBMITTED: 'PAYMENT_BRIDGE_SUBMITTED',
PAYMENT_BRIDGE_COMPLETED: 'PAYMENT_BRIDGE_COMPLETED',
PAYMENT_BRIDGE_FAILED: 'PAYMENT_BRIDGE_FAILED',
PAYMENT_CHARGEBACKED: 'PAYMENT_CHARGEBACKED',
ORDER_PARTIALLY_CHARGEBACKED: 'ORDER_PARTIALLY_CHARGEBACKED',
ORDER_CHARGEBACKED: 'ORDER_CHARGEBACKED',
PAYOUT_ORDER_CREATED: 'PAYOUT_ORDER_CREATED',
PAYOUT_ORDER_CANCELLED: 'PAYOUT_ORDER_CANCELLED',
PAYOUT_ORDER_PARTIALLY_FUNDED: 'PAYOUT_ORDER_PARTIALLY_FUNDED',
PAYOUT_ORDER_FUNDED: 'PAYOUT_ORDER_FUNDED',
PAYOUT_ORDER_MATCHED: 'PAYOUT_ORDER_MATCHED',
PAYOUT_ORDER_PARTIALLY_PAID: 'PAYOUT_ORDER_PARTIALLY_PAID',
PAYOUT_ORDER_SETTLED: 'PAYOUT_ORDER_SETTLED',
PAYOUT_ORDER_EXPIRED: 'PAYOUT_ORDER_EXPIRED',
} as const;

const OrderAmountMode = {
FIXED: 'FIXED',
OPEN: 'OPEN',
} as const;

const AmountLockReason = {
PAYMENT_IN_PROGRESS: 'PAYMENT_IN_PROGRESS',
PAYMENT_MAY_SETTLE: 'PAYMENT_MAY_SETTLE',
PAID: 'PAID',
} as const;

const OpenAmountErrorCode = {
OPEN_AMOUNT_INVALID: 'OPEN_AMOUNT_INVALID',
OPEN_AMOUNT_DISABLED: 'OPEN_AMOUNT_DISABLED',
AMOUNT_REQUIRED: 'AMOUNT_REQUIRED',
AMOUNT_NOT_ALLOWED: 'AMOUNT_NOT_ALLOWED',
AMOUNT_OUT_OF_RANGE: 'AMOUNT_OUT_OF_RANGE',
AMOUNT_LOCKED: 'AMOUNT_LOCKED',
AMOUNT_CONFLICT: 'AMOUNT_CONFLICT',
RESIZE_NOT_SUPPORTED: 'RESIZE_NOT_SUPPORTED',
} as const;