# Create API Key Source: https://docs.coinvoyage.io/api-reference/api-keys/create-api-key https://api.coinvoyage.io/v3/openapi.json post /api-keys Create another credential for the authenticated entity. The full key and secret are returned only in this response and exact idempotent replays. # List API Keys Source: https://docs.coinvoyage.io/api-reference/api-keys/list-api-keys https://api.coinvoyage.io/v3/openapi.json get /api-keys # Revoke API Key Source: https://docs.coinvoyage.io/api-reference/api-keys/revoke-api-key https://api.coinvoyage.io/v3/openapi.json delete /api-keys/{api_key_id} # Update API Key Source: https://docs.coinvoyage.io/api-reference/api-keys/update-api-key https://api.coinvoyage.io/v3/openapi.json patch /api-keys/{api_key_id} # Claim Fees Source: https://docs.coinvoyage.io/api-reference/fees/claim-fees https://api.coinvoyage.io/v3/openapi.json post /fees/claim # Get Fee Balance Source: https://docs.coinvoyage.io/api-reference/fees/get-fee-balance https://api.coinvoyage.io/v3/openapi.json get /fees/balance # Archive Invoice Source: https://docs.coinvoyage.io/api-reference/invoices/archive-invoice https://api.coinvoyage.io/v3/openapi.json post /invoices/{invoice_id}/archive # Create Invoice Source: https://docs.coinvoyage.io/api-reference/invoices/create-invoice https://api.coinvoyage.io/v3/openapi.json post /invoices Creates and publishes an invoice backed by a SALE pay order. The recipient is emailed a PDF invoice with a hosted payment link. Pass id to publish an existing draft. # Delete Invoice Draft Source: https://docs.coinvoyage.io/api-reference/invoices/delete-invoice-draft https://api.coinvoyage.io/v3/openapi.json delete /invoices/{invoice_id} Deletes an unpublished draft. Published invoices cannot be deleted. # Get Invoice Source: https://docs.coinvoyage.io/api-reference/invoices/get-invoice https://api.coinvoyage.io/v3/openapi.json get /invoices/{invoice_id} # List Invoices Source: https://docs.coinvoyage.io/api-reference/invoices/list-invoices https://api.coinvoyage.io/v3/openapi.json get /invoices # Save Invoice Draft Source: https://docs.coinvoyage.io/api-reference/invoices/save-invoice-draft https://api.coinvoyage.io/v3/openapi.json post /invoices/drafts Creates a draft invoice, or updates an existing unpublished one when id is set. Draft blocks are stored verbatim and only validated at publish. # Add Bank Account Source: https://docs.coinvoyage.io/api-reference/off-ramp/add-bank-account https://api.coinvoyage.io/v3/openapi.json post /off-ramp/bank-accounts # Create Off-ramp Intent Source: https://docs.coinvoyage.io/api-reference/off-ramp/create-off-ramp-intent https://api.coinvoyage.io/v3/openapi.json post /off-ramp/intents # Create Off-ramp Verification Source: https://docs.coinvoyage.io/api-reference/off-ramp/create-off-ramp-verification https://api.coinvoyage.io/v3/openapi.json post /off-ramp/verification # Get an exact off-ramp verification Source: https://docs.coinvoyage.io/api-reference/off-ramp/get-an-exact-off-ramp-verification https://api.coinvoyage.io/v3/openapi.json get /off-ramp/verifications/{verification_id} # Get Bank Account Source: https://docs.coinvoyage.io/api-reference/off-ramp/get-bank-account https://api.coinvoyage.io/v3/openapi.json get /off-ramp/bank-accounts/{bank_account_id} # List Bank Accounts Source: https://docs.coinvoyage.io/api-reference/off-ramp/list-bank-accounts https://api.coinvoyage.io/v3/openapi.json get /off-ramp/bank-accounts # List Off-ramp Intents Source: https://docs.coinvoyage.io/api-reference/off-ramp/list-off-ramp-intents https://api.coinvoyage.io/v3/openapi.json get /off-ramp/intents # Off-ramp Verification Status Source: https://docs.coinvoyage.io/api-reference/off-ramp/off-ramp-verification-status https://api.coinvoyage.io/v3/openapi.json get /off-ramp/verification/status # Checkout Onramp Session Source: https://docs.coinvoyage.io/api-reference/onramp/checkout-onramp-session https://api.coinvoyage.io/v3/openapi.json post /onramp/sessions/{session_id}/checkout Checkout a headless Stripe embedded onramp session and return the checkout client secret required by the Stripe embedded component. The session is resolved to the stored Link OAuth token server-side. # Create Onramp Auth Intent Source: https://docs.coinvoyage.io/api-reference/onramp/create-onramp-auth-intent https://api.coinvoyage.io/v3/openapi.json post /onramp/auth/intent Create a Stripe Link OAuth auth intent for the embedded onramp flow. # Create Onramp Auth Token Source: https://docs.coinvoyage.io/api-reference/onramp/create-onramp-auth-token https://api.coinvoyage.io/v3/openapi.json post /onramp/auth/token Create a short-lived Stripe Link auth token client secret for React Native seamless sign-in. The server uses the previously stored Link OAuth token for the provided auth intent. # Create Onramp Intent Source: https://docs.coinvoyage.io/api-reference/onramp/create-onramp-intent https://api.coinvoyage.io/v3/openapi.json post /onramp/intents Create a reusable onramp intent. For Orders, amount, destination currency, and wallet fields are derived server-side from the Order and cannot be overridden by the client. # Create Onramp Session Source: https://docs.coinvoyage.io/api-reference/onramp/create-onramp-session https://api.coinvoyage.io/v3/openapi.json post /onramp/intents/{intent_id}/session Create a headless Stripe embedded onramp session from a server-held onramp intent using the server-held Link OAuth access token. If Stripe identity or KYC action is required, this endpoint returns 422 with action fields in error.details, including status, reason, kyc_next_step, kyc_status, kyc_required, identity_verification_required, and identity_verification_status. # Get Onramp Verification Status Source: https://docs.coinvoyage.io/api-reference/onramp/get-onramp-verification-status https://api.coinvoyage.io/v3/openapi.json post /onramp/verification/status Retrieve normalized Stripe CryptoCustomer KYC and identity verification status after Link authentication or after KYC steps. The server exchanges or reuses the Link OAuth token and never returns it to the client. # Refresh Onramp Quote Source: https://docs.coinvoyage.io/api-reference/onramp/refresh-onramp-quote https://api.coinvoyage.io/v3/openapi.json post /onramp/sessions/{session_id}/quote Refresh the executable quote for a headless Stripe embedded onramp session. The session is resolved to the stored Link OAuth token server-side. # Create Bank Onramp Account Source: https://docs.coinvoyage.io/api-reference/onramps/create-bank-onramp-account https://api.coinvoyage.io/v3/openapi.json post /onramp/bank/accounts Create reusable bank deposit instructions. Each incoming transfer is recorded as a separate deposit, and the required deposit message must be included with every transfer. # Get Bank Onramp Account Source: https://docs.coinvoyage.io/api-reference/onramps/get-bank-onramp-account https://api.coinvoyage.io/v3/openapi.json get /onramp/bank/accounts/{account_id} # List Bank Onramp Accounts Source: https://docs.coinvoyage.io/api-reference/onramps/list-bank-onramp-accounts https://api.coinvoyage.io/v3/openapi.json get /onramp/bank/accounts Paginated list of reusable bank deposit accounts for the authenticated organization. # List Bank Onramp Deposits Source: https://docs.coinvoyage.io/api-reference/onramps/list-bank-onramp-deposits https://api.coinvoyage.io/v3/openapi.json get /onramp/bank/accounts/{account_id}/deposits Paginated deposit history for a reusable bank deposit account. # Create Order Source: https://docs.coinvoyage.io/api-reference/orders/create-order https://api.coinvoyage.io/v3/openapi.json post /orders Create an order (SALE / DEPOSIT). SALE requires an Authorization signature; DEPOSIT accepts X-API-KEY or a signature. # Create Payment Details Source: https://docs.coinvoyage.io/api-reference/orders/create-payment-details https://api.coinvoyage.io/v3/openapi.json post /orders/{order_id}/payment Create the payment details for an order — returns the resolved payment block (receiving address, amount, expiry, and steps to fund). The server always requotes (no quote_id). # Get Order Source: https://docs.coinvoyage.io/api-reference/orders/get-order https://api.coinvoyage.io/v3/openapi.json get /orders/{order_id} # Get Payment Methods Source: https://docs.coinvoyage.io/api-reference/orders/get-payment-methods https://api.coinvoyage.io/v3/openapi.json get /orders/{order_id}/payment-methods # List Orders Source: https://docs.coinvoyage.io/api-reference/orders/list-orders https://api.coinvoyage.io/v3/openapi.json get /orders Paginated list of orders for the authenticated organization. # Order Quotes Source: https://docs.coinvoyage.io/api-reference/orders/order-quotes https://api.coinvoyage.io/v3/openapi.json post /orders/{order_id}/quotes Ranked options for paying an order: source tokens (wallet scan and/or explicit list) ranked against the order fulfillment destination, falling back to a settlement currency. EXACT_OUTPUT only. # Refund Order Source: https://docs.coinvoyage.io/api-reference/orders/refund-order https://api.coinvoyage.io/v3/openapi.json post /orders/{order_id}/refund # Currency Search (Typeahead) Source: https://docs.coinvoyage.io/api-reference/search/currency-search-typeahead https://api.coinvoyage.io/v3/openapi.json get /search/currencies Token search for source/destination pickers. Queries shorter than 2 characters return an empty list. # Execute Swap Source: https://docs.coinvoyage.io/api-reference/swap/execute-swap https://api.coinvoyage.io/v3/openapi.json post /swap/execute Create the payment details for a SWAP-mode order — returns the deposit instruction (payment block with steps to fund). # Swap Quote Source: https://docs.coinvoyage.io/api-reference/swap/swap-quote https://api.coinvoyage.io/v3/openapi.json post /swap/quote Spammable single-pair swap quote for live price discovery. Flat request/response. result_type carries soft errors on a 200. Optional recipient; fees match the swap execute path. # Wallet Portfolio Source: https://docs.coinvoyage.io/api-reference/wallet/wallet-portfolio https://api.coinvoyage.io/v3/openapi.json post /wallet/portfolio Scan wallets with per-wallet success or error results. # Create Webhook Source: https://docs.coinvoyage.io/api-reference/webhooks/create-webhook https://api.coinvoyage.io/v3/openapi.json post /webhooks # Delete Webhook Source: https://docs.coinvoyage.io/api-reference/webhooks/delete-webhook https://api.coinvoyage.io/v3/openapi.json delete /webhooks/{webhook_id} # List Webhooks Source: https://docs.coinvoyage.io/api-reference/webhooks/list-webhooks https://api.coinvoyage.io/v3/openapi.json get /webhooks # Update Webhook (partial) Source: https://docs.coinvoyage.io/api-reference/webhooks/update-webhook-partial https://api.coinvoyage.io/v3/openapi.json patch /webhooks/{webhook_id} # Order Status WebSocket Source: https://docs.coinvoyage.io/api-reference/websocket/order-status-websocket https://api.coinvoyage.io/v3/openapi.json get /ws Upgrade to a WebSocket for real-time order events. Authenticate with a `connect` message, then `subscribe` by `order_id`. Event pushes use `{"type":"event","data":{"event":"ORDER_*","delivered_at":...,"order":{...}}}`. # x402 Complete Source: https://docs.coinvoyage.io/api-reference/x402/x402-complete https://api.coinvoyage.io/v3/openapi.json post /orders/{order_id}/x402/complete x402 payment completion. Public: the PAYMENT-SIGNATURE header is the auth boundary. Without it, returns 402 with a PAYMENT-REQUIRED header. # CoinVoyage API overview Source: https://docs.coinvoyage.io/api/overview Learn how to call the CoinVoyage v3 API directly, including authentication, orders, invoices, swaps, off-ramp flows, errors, pagination, and webhook-driven processing. Use the CoinVoyage API when you need direct control over order creation, invoices, quote selection, payment instructions, standalone swaps, webhook management, refunds, fee claims, off-ramp verification, bank accounts, or backend reconciliation. React payment integrations should start with [PayKit](/sdk/overview), while React swap integrations should start with [SwapKit](/sdk/swapkit). Use the API directly for custom or server-side workflows that should never run in the browser. Endpoint-level schemas, request bodies, and response examples are available in the generated [API Reference](/api-reference). This page explains the conventions that apply across the API. ## Base URL Production API requests use: ```text theme={null} https://api.coinvoyage.io/v3 ``` SDK integrations select the base URL through the `environment` option: ```typescript theme={null} import { ApiClient, ChainId } from "@coin-voyage/paykit/server"; const apiClient = ApiClient({ apiKey: process.env.COIN_VOYAGE_API_KEY!, environment: "production", }); ``` | Environment | Base URL | Use for | | ------------- | ---------------------------------- | ------------------------------- | | `production` | `https://api.coinvoyage.io/v3` | Live payments and settlement. | | `development` | `https://acc-api.coinvoyage.io/v3` | Acceptance/integration testing. | | `local` | `http://localhost:8000/v3` | Local backend development. | ## Authentication CoinVoyage uses two credential types: | Credential | Where to use it | Purpose | | ---------- | ----------------- | --------------------------------------------------------------------------------------- | | API key | Browser or server | Identifies your organization. Safe to expose in client-side code. Sent as `X-API-KEY`. | | API secret | Server only | Generates HMAC-SHA256 signatures for privileged operations. Never expose it to clients. | Signed requests use the `Authorization` header value: ```text theme={null} APIKey=,signature=,timestamp= ``` When signing v3 requests, compute the signature over `METHOD + path + timestamp`, where `path` excludes the `/v3` prefix. For example, sign `POST /orders`, not `POST /v3/orders`. ```typescript theme={null} const authorization = apiClient.generateAuthorizationSignature( process.env.COIN_VOYAGE_API_SECRET!, "POST", "/orders" ); ``` Generate authorization signatures on your server only. A leaked API secret can create sales, refunds, invoices, off-ramp intents, webhooks, and fee claims for your organization. ## Public API key flows `DEPOSIT` orders can be created with the public API key because the recipient address is supplied by the integration and no merchant settlement configuration is modified. ```typescript theme={null} const { data, error } = await apiClient.createDepositOrder({ amount: "10", currency: { chain_id: ChainId.SUI, address: null, }, recipient: "0xYourReceivingAddress", }); ``` ## Signed server-side flows `SALE` orders, refunds, order listing, invoices, off-ramp verification, bank account management, off-ramp intents, webhook management, and fee operations require your API secret. ```typescript theme={null} const { data, error } = await apiClient.createSaleOrder( { amount: "49.99", fiat_unit: "USD", metadata: { order_id: "order_123", }, }, process.env.COIN_VOYAGE_API_SECRET! ); ``` See [Invoices API](/invoices/api) for signed invoice creation, draft, list, retrieval, archive, and delete operations. ## Where off-ramp APIs fit Off-ramp verification, bank accounts, and off-ramp intents are optional. They do not replace orders, and they are not required when you only want on-chain settlement to a wallet. | API area | When to use it | Credential | | --------------------- | ---------------------------------------------------------------------------------------------------------------- | ----------------------------------------------- | | Off-ramp verification | Create a hosted KYC/KYB verification link or check whether an organization is approved for fiat payout activity. | API secret | | Bank accounts | Add and retrieve linked payout destinations for a verified individual or business. | API secret | | Off-ramp intents | Move a supported on-chain settlement balance to a linked bank account. | API secret, plus wallet execution when required | Bank-account payouts are optional. You can bring your own externally owned account (EOA), receive funds there, and move funds anywhere you want from that wallet. Use off-ramp intents only when your product needs a CoinVoyage-managed fiat payout to a linked bank account. See [Integration flows](/flows/overview) for the end-to-end verification -> bank account -> off-ramp intent sequence. ## Where swap APIs fit Swap APIs are for standalone on-chain asset exchanges. They are separate from checkout and deposit orders: an order may use swaps internally while routing a payment, but `swapQuote()` and `swapExecute()` are useful when your product wants to quote and execute a swap as its own user action. | API area | When to use it | Credential | | ------------ | ----------------------------------------------------------------------- | ----------------------------------- | | Swap quote | Show the expected route, output amount, and fees before the user signs. | API key | | Swap execute | Create payment instructions for funding the swap. | API key, plus source-wallet signing | ## Request conventions * Send JSON request bodies with `Content-Type: application/json`. * Use ISO 8601 timestamps in UTC for stored times and event payloads. * Treat IDs as opaque strings. Do not parse structure out of order IDs, webhook IDs, quote IDs, transaction hashes, or off-ramp intent IDs. * Store token amounts exactly as returned by the API. Prefer raw string amounts for accounting systems that require exact precision. * Attach your own order, user, or invoice identifiers in `metadata` so webhook handlers can reconcile events without a separate lookup. ## Response shape The SDK wraps every result in `APIResponse`: ```typescript theme={null} type APIResponse = { data?: T error?: { path: string statusCode: number status: string message: string details?: unknown } } ``` Check `error` before using `data`: ```typescript theme={null} const { data, error } = await apiClient.getOrder("order_123"); if (error) { console.error(error.statusCode, error.message); return; } if (!data) { return; } console.log(data.status); ``` ## Error handling Handle API errors by status code category: | Status | Meaning | Recommended action | | ------ | --------------------------------------------------------- | -------------------------------------------------------------------------- | | `400` | Invalid request parameters or path values | Show a validation error and fix the request before retrying. | | `401` | Missing, invalid, or expired authentication | Check API key, signature, timestamp, and server-side secret configuration. | | `403` | Credential is valid but not allowed to perform the action | Confirm organization permissions, environment, and feature access. | | `404` | Resource not found | Confirm the ID belongs to the same organization and environment. | | `422` | Semantically valid JSON that fails business validation | Surface the message to the operator or correct the integration mapping. | | `429` | Too many requests | Back off and retry after the limit resets. | | `5xx` | Temporary platform or provider error | Retry with backoff for safe operations and alert if failures persist. | For payment fulfillment, prefer webhook-driven state changes over repeated polling. Polling is useful for dashboard-style views and recovery jobs, but webhooks are the source of real-time completion signals. ## Rate limits When a request exceeds a limit, CoinVoyage returns `429 Too Many Requests`. Back off instead of retrying immediately, and use the `Retry-After` response header when it is present. See [Rate limits](/resources/rate-limits) for retry guidance and response headers. ## Pagination and listing List endpoints return paginated data when the result set can grow over time. Use pagination for reconciliation jobs, dashboards, exports, and backfills instead of assuming a single response contains every record. When consuming paginated endpoints: * Keep the original filter set stable while walking pages. * Store the last successful cursor, page, or timestamp checkpoint for long-running jobs. * Expect new records to appear while you paginate. * Reconcile by order ID rather than by page position. See the generated [API Reference](/api-reference) for the exact pagination parameters supported by each endpoint. ## Idempotency and retries Network requests can time out after CoinVoyage receives them. Design your integration so retrying does not create duplicate business effects: * Store your internal order ID in order `metadata`. * Before creating a replacement order, check whether your internal order already has an active CoinVoyage order. * Treat webhook deliveries as at-least-once events and order IDs as payment lifecycle IDs. * Make fulfillment idempotent by recording the CoinVoyage order ID and terminal status before shipping goods, crediting balances, or updating inventory. For server retries, use exponential backoff with jitter. Retry read operations freely. Retry create or mutation operations only when your application can detect duplicates by metadata, order ID, or its own internal state. ## Webhook-first processing Production integrations should use webhooks for payment state changes: 1. Create an order from your server. 2. Store the CoinVoyage order ID against your internal order or account. 3. Show the payment modal to the user. 4. Verify webhook signatures before parsing the event. 5. Update internal state from outcome and recovery events such as `ORDER_COMPLETED`, `ORDER_REFUNDED`, `ORDER_EXPIRED`, `ORDER_ERROR`, or `ORDER_PARTIAL_PAYMENT`. 6. Run a scheduled reconciliation job that compares your internal state with the CoinVoyage API. See [Webhooks overview](/webhooks/overview) and [Webhook events](/webhooks/events) for delivery setup and payload examples. ## Go-live requirements Before using production credentials, confirm that: * Your API secret is stored server-side only. * Webhook signature verification is enabled. * Fulfillment is idempotent. * Failed, expired, refunded, and partial-payment states are handled. * Your settlement currency and wallet address are configured in the dashboard if you create `SALE` orders without explicit `currency` and `recipient` values. * You have tested a full payment, refund, and webhook flow in the intended environment. Use the [Production checklist](/guides/production-checklist) before launch. # How CoinVoyage routes and settles cross-chain payments Source: https://docs.coinvoyage.io/concepts/how-it-works Learn how CoinVoyage routes payments from any chain to your destination, from order creation through provider chaining to final settlement. CoinVoyage handles the full complexity of cross-chain payments so you don't have to. When a user pays, CoinVoyage scans their wallet, fetches quotes from multiple liquidity providers, selects the optimal route, and executes each step in sequence — all the way through to settlement at your destination address. The sections below walk through every stage of that process. ## End-to-end payment flow The sequence diagram below shows every actor and message involved in a complete payment: ```mermaid theme={null} sequenceDiagram participant Merchant as Merchant App participant CV as CoinVoyage participant User as User participant Wallet as User Wallet participant Providers as Quote Providers participant Dest as Destination Wallet Note over Merchant,CV: 1. Order Creation Merchant->>CV: Create Order (DEPOSIT or SALE) CV-->>Merchant: Order ID and details CV->>Merchant: Webhook: ORDER_CREATED Merchant->>User: Display Payment Modal Note over User,User: 2. Select Payment Method User->>User: Select Chain (SUI/BTC/SOL/EVM) User->>CV: Send Wallet Address and Chain Type Note over CV,Providers: 3. Quote Generation CV->>CV: Scan for Token Balances CV->>Providers: Request Quotes Providers-->>CV: Return All Possible Routes CV-->>User: Display Top Quotes Note over User,CV: 4. Quote Selection User->>CV: Select Preferred Quote CV->>CV: Generate Payment Details CV-->>User: Deposit Address, Amount, Expiration Note over User,CV: 5. Payment Execution CV->>Merchant: Webhook: ORDER_AWAITING_PAYMENT User->>Wallet: Send Funds to Deposit Address Wallet->>CV: Transaction Detected CV->>Merchant: Webhook: ORDER_CONFIRMING CV->>CV: Verify Transaction Note over CV,Dest: 6. Provider Chain Execution CV->>Providers: Execute Route (Provider 1 -> Provider 2 -> ...) CV->>Merchant: Webhook: ORDER_EXECUTING Providers->>Dest: Deliver Funds Dest-->>CV: Confirm Receipt Note over CV,Merchant: 7. Settlement Complete CV->>CV: Update order status: COMPLETED CV->>Merchant: Webhook: ORDER_COMPLETED CV-->>User: Payment Success Notification ``` ## Detailed flow steps Your server (or the SDK's `ApiClient`) creates an order specifying the destination chain, asset, and amount. CoinVoyage returns an order ID and immediately fires an `ORDER_CREATED` webhook. You then pass that ID to the payment modal so the user can begin. The main create modes are **Deposit** and **Sale**. Refunds are created through the dedicated refund endpoint. See [Orders](/concepts/pay-orders) for details. ```typescript theme={null} const order = await apiClient.createDepositOrder({ amount: "10", currency: { chain_id: ChainId.SUI, address: null, }, recipient: "0xYourWalletAddress", }); ``` The user opens the payment modal and picks their preferred chain and token. CoinVoyage supports payment from: * **SUI** — Sui blockchain * **BTC** — Bitcoin * **SOL** — Solana * **EVM** — Ethereum, Arbitrum, Base, Optimism, Polygon, BSC, and more Once the user selects a chain, they provide their wallet address so CoinVoyage can scan for available balances. CoinVoyage builds quotes by: 1. **Scanning wallet balances** to identify tokens the user can pay with 2. **Querying providers** for live rates — AMMs (Uniswap V2, V3, and V4; Jupiter; Cetus), Across and CCTP for cross-chain routes, and direct same-chain transfers 3. **Optimizing routes** by chaining providers together when that yields a better rate 4. **Ranking quotes** by best effective output (highest received amount, lowest fees) The top quotes are surfaced to the user for selection. After the user picks a quote, CoinVoyage generates the exact payment instructions for that route: * **Deposit address** — a unique address generated for this payment * **Exact amount** — the precise token amount to send * **Expiration time** — the payment window, typically 30 minutes * **Refund address** — where funds return if anything goes wrong A quote looks like this before the user confirms: ``` Pay: 0.05 ETH (Ethereum) Receive: 10 SUI (Sui Network) Route: Ethereum → CCTP → Sui Fee: 1.5% Estimated time: ~30 seconds ``` The user sends the exact amount to the deposit address from their wallet. CoinVoyage monitors the blockchain for: * **Transaction submission** — the payment has been broadcast * **On-chain confirmation** — the transaction is included in a block * **Amount verification** — the correct amount was received Your server receives an `ORDER_CONFIRMING` webhook as soon as the transaction is detected. Once payment is confirmed, CoinVoyage executes the selected route. Routes can involve a single provider or a chain of providers: **Single-provider flow** ``` User Payment → Provider → Destination Example: SOL → Direct Transfer → SOL (same chain) ``` **Multi-provider chain** ``` User Payment → Provider 1 → Provider 2 → Destination Example: ETH → Uniswap (ETH → USDC) → CCTP → SUI ``` CoinVoyage handles route execution, error recovery, and real-time status webhooks throughout this stage. If any step fails, an automatic refund is initiated. Webhook subscriptions and delivered payloads use uppercase `ORDER_*` identifiers, such as `ORDER_EXECUTING`. When funds arrive at the destination address, CoinVoyage marks the order as `COMPLETED` and fires the `ORDER_COMPLETED` webhook. All relevant transaction hashes are recorded on-chain. If execution fails at any point: 1. An automatic refund is initiated 2. Funds are returned to the user's refund address 3. The order status changes to `REFUNDED` 4. An `ORDER_REFUNDED` webhook is fired ## Provider chaining example CoinVoyage can chain multiple providers together to find the best possible route for a given payment. Here is what that looks like when a user pays with ETH on Ethereum and you want to receive SUI: ```text theme={null} Step 1: User pays ETH on Ethereum | Step 2: Uniswap swaps ETH → USDC (on Ethereum) | Step 3: CCTP transfers USDC from Ethereum → Sui | Step 4: Sui DEX swaps USDC → SUI | Step 5: SUI delivered to your wallet ``` The entire process is: * **Automated** — no manual intervention required at any step * **Optimized** — the best route is calculated in real time before the user confirms * **Transparent** — all fees, routes, and timing are shown to the user upfront * **Fast** — typically completes in under 60 seconds # Orders: DEPOSIT, SALE, REFUND modes and statuses Source: https://docs.coinvoyage.io/concepts/pay-orders Understand the CoinVoyage order object at the center of every payment, including modes, statuses, and metadata. An order is the core unit of work in CoinVoyage. Every payment, whether a user deposits funds to a wallet, a merchant collects a sale, or a merchant issues a refund, is represented as an order. You create one before showing the payment UI, and CoinVoyage updates it as payment moves through detection, confirmation, execution, and settlement. ## Order modes CoinVoyage exposes `SALE` and `DEPOSIT` as direct create modes. `REFUND`, `SWAP`, and `FEE_CLAIM` are created through dedicated endpoints or platform workflows. A `DEPOSIT` order moves funds directly to a wallet address you specify on a target chain. Use this when a user is topping up a wallet, funding an account, or making a transfer where you control the destination address. ```typescript theme={null} import { ApiClient, ChainId } from "@coin-voyage/paykit/server"; const apiClient = ApiClient({ apiKey: process.env.COIN_VOYAGE_API_KEY!, }); const { data, error } = await apiClient.createDepositOrder({ amount: "10", currency: { chain_id: ChainId.SUI, address: null, }, recipient: "0xYourReceivingAddressHere", metadata: { items: [{ name: "Wallet top-up" }], }, }); ``` `DEPOSIT` orders do not require an API secret for authorization. You can create them with only your API key. A `SALE` order represents a merchant collecting payment for goods or services. Settlement goes to your organization's configured settlement currency, or to a specific asset and recipient if you provide `currency` and `recipient`. `SALE` orders require an API secret and must be created server-side. ```typescript theme={null} const { data, error } = await apiClient.createSaleOrder( { amount: "200", fiat_unit: "USD", metadata: { items: [ { name: "t-shirt", description: "A nice t-shirt", image: "https://example.com/tshirt.jpg", quantity: 1, unit_price: 200, currency: "USD", }, ], }, }, process.env.COIN_VOYAGE_API_SECRET! ); ``` If this specific order should settle to a particular on-chain asset instead of your dashboard default, include `currency` and `recipient`: ```typescript theme={null} const { data, error } = await apiClient.createSaleOrder( { amount: "570.52", fiat_unit: "USD", currency: { chain_id: ChainId.SOL, address: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", }, recipient: "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM", }, process.env.COIN_VOYAGE_API_SECRET! ); ``` Never call `createSaleOrder` from the browser. The method signs the request with your API secret, which must remain confidential and server-side only. A `REFUND` order sends funds back to a user for a previous payment. You reference the original order ID and specify the refund amount, currency, and recipient. ```typescript theme={null} const { data: refundOrder, error } = await apiClient.createRefundOrder( "original-order-id", { amount: "100", fiat_unit: "USD", currency: { chain_id: ChainId.ETH, address: null, }, recipient: "0xRefundAddress", metadata: { refund: { reason: "Item out of stock", refund_amount: 100, currency: "USD", }, }, }, process.env.COIN_VOYAGE_API_SECRET! ); ``` ## Order statuses Your webhook handler and polling logic should account for each lifecycle state. | Status | Description | | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `PENDING` | The order has been created but is not yet ready for payment. | | `AWAITING_PAYMENT` | The order is ready. CoinVoyage is waiting for the user to send funds. | | `AWAITING_CONFIRMATION` | A payment transaction has been detected on-chain and is pending confirmation. | | `OPTIMISTIC_CONFIRMED` | The transaction is optimistically confirmed; execution can begin before full finality. | | `EXECUTING_ORDER` | Payment is confirmed and CoinVoyage is routing funds through the provider chain. | | `COMPLETED` | Funds have arrived at the destination. The order is complete. | | `EXPIRED` | The payment window elapsed before the user sent funds. | | `REFUNDED` | Execution failed or a refund was issued and funds were returned to the refund address. | | `FAILED` | The order encountered an unrecoverable error during processing. | | `PARTIAL_PAYMENT` | The user sent less than the required amount. Pay to Address can accept additional deposits until the order is fully funded; other payment methods require resolution. | Webhook subscription event types and delivered payload values use uppercase `ORDER_*` identifiers, such as `ORDER_COMPLETED`. The typical happy-path progression is: ```text theme={null} PENDING -> AWAITING_PAYMENT -> AWAITING_CONFIRMATION -> OPTIMISTIC_CONFIRMED -> EXECUTING_ORDER -> COMPLETED ``` Failure paths branch to `EXPIRED`, `REFUNDED`, `FAILED`, or `PARTIAL_PAYMENT` depending on where the problem occurs. ### Continue a Pay to Address partial payment For Pay to Address, `PARTIAL_PAYMENT` is recoverable. Keep fulfillment paused and use `order.payment.funding.remaining_amount` to show the amount still due. Additional deposits are accumulated in `order.payment.funding.transactions`; fulfill only after the order reaches `COMPLETED`. This behavior applies only to Pay to Address. Wallet, card, and other payment methods do not support additional deposits after `PARTIAL_PAYMENT`. ## Order metadata You can attach structured metadata when you create an order. Metadata is visible in the dashboard and included in webhook payloads, making it useful for reconciliation, analytics, and displaying order context to the user. ```typescript theme={null} type OrderMetadata = { items?: Array<{ name: string description?: string image?: string quantity?: number unit_price?: number currency?: string }> refund?: { name?: string reason?: string additional_info?: string refund_amount?: number currency?: string } [key: string]: unknown } ``` ### Items Use the `items` array to describe what the user is paying for. Each item can include a name, description, image URL, quantity, and unit price. ```typescript theme={null} metadata: { items: [ { name: "Annual subscription", description: "Pro plan - 12 months", quantity: 1, unit_price: 199, currency: "USD", }, ], } ``` ### Refund details For refunds, populate the `refund` object to record the reason and amount being refunded: ```typescript theme={null} metadata: { refund: { reason: "Item damaged in shipping", refund_amount: 49.99, currency: "USD", }, } ``` ### Custom fields You can add custom top-level fields to carry your own data, such as customer IDs, order references, or campaign tags. ```typescript theme={null} metadata: { items: [{ name: "Premium plan" }], customer_id: "cust_12345", order_reference: "ORD-2026-001", campaign: "summer_sale", } ``` Use stable internal IDs in metadata so webhook handlers can reconcile CoinVoyage orders back to your own records without an extra lookup. # Blockchain networks and chain IDs supported by CoinVoyage Source: https://docs.coinvoyage.io/concepts/supported-networks Complete list of every blockchain CoinVoyage supports for payments and settlement, including chain IDs, testnet availability, and upcoming networks. CoinVoyage accepts payments from and settles to a growing set of blockchain networks. Each network is identified by a chain ID that you use when creating orders or requesting quotes. The table below lists every currently supported chain and its ID. If you need support for a network not listed here, contact the team at [help@coinvoyage.io](mailto:help@coinvoyage.io). ## Supported networks | Chain | Chain ID | | ------------------------- | ---------------- | | Bitcoin | `20000000000001` | | Solana | `30000000000001` | | Ethereum Mainnet | `1` | | Optimism | `10` | | Arbitrum | `42161` | | Base | `8453` | | Binance Smart Chain (BSC) | `56` | | Polygon | `137` | | Robinhood Chain | `4663` | | Sui | `30000000000002` | ## Using chain IDs in your integration When you create an order or request payment details, you reference a network by its chain ID. The SDK exports a `ChainId` enum so you don't have to remember raw numbers: ```typescript theme={null} import { ChainId } from "@coin-voyage/paykit/server"; // ChainId.SUI === 30000000000002 // ChainId.ETH === 1 // ChainId.SOL === 30000000000001 const { data } = await apiClient.createDepositOrder({ amount: "10", currency: { chain_id: ChainId.SUI, address: null, // null = native token }, recipient: "0xYourAddress", }); ``` ## Coming soon CoinVoyage is actively expanding network coverage. Networks currently in integration include: * **AVAX** — Avalanche * **Tron** * Additional Ethereum L2s Network availability may differ between the `production` and `development` environments. The development environment may include early-access chains not yet available in production. # Fiat off-ramp regions, currencies, and payment rails Source: https://docs.coinvoyage.io/concepts/supported-regions Learn which fiat currencies CoinVoyage supports for settlement, the payment rails used in each region, settlement timing, and KYC requirements. CoinVoyage settles crypto into the fiat currencies your customers actually use. You can currently accept payments that settle in four fiat currencies spanning North America, Europe, and Latin America. New rails are added on a rolling basis, and additional currencies are in active integration. Each currency has its own payment rail, settlement timing, and recipient information requirements — the details for each are below. ## Supported fiat currencies | Currency | Symbol | Payment rail | Settlement | | -------------- | ------ | ------------------ | ------------------------------------------ | | US Dollar | `USD` | ACH, Wire | ACH 1–3 business days · Wire same-day | | Euro | `EUR` | SEPA, SEPA Instant | 0–1 business day · Instant where available | | Brazilian Real | `BRL` | Pix | Instant, 24/7 | | Mexican Peso | `MXN` | SPEI | Minutes, 24/7 | ## Per-currency details **Rails:** ACH, Wire * **Settlement.** ACH credits typically settle in 1–3 US business days. Wires settle the same day during banking hours. * **Coverage.** All US states **except New York** for new customers. Businesses incorporated in New York but operating primarily out of another state may still qualify on a case-by-case basis — contact support. * **Recipient info required.** Routing number and account number (ACH), or full wire instructions. Individuals must provide an SSN; businesses must provide an EIN. **Rails:** SEPA, SEPA Instant * **Settlement.** 0–1 business day across the SEPA zone. SEPA Instant is used automatically where the recipient bank supports it. * **Coverage.** All 36 SEPA jurisdictions, including the EU, EEA (Iceland, Liechtenstein, Norway), Switzerland, Monaco, San Marino, Andorra, the United Kingdom, and Vatican City. * **Recipient info required.** IBAN and BIC (where required by the recipient bank). **MiCA notice for EEA residents.** Per the EU's Markets in Crypto-Assets regulation, USDT is not available to users resident in the EEA. Use **USDC** or **EURC** as the stablecoin leg for EEA off-ramps. **Rail:** Pix * **Settlement.** Instant, 24/7 via Pix. * **Coverage.** Brazil. * **Recipient info required.** A valid Pix key — CPF, CNPJ, email address, phone number, or random key — plus the matching CPF (individuals) or CNPJ (businesses) for KYC. **Rail:** SPEI * **Settlement.** Typically a few minutes. SPEI runs 24/7 at most participating banks. * **Coverage.** Mexico. * **Recipient info required.** 18-digit CLABE. Individuals must provide an RFC and a Mexican government-issued ID. Businesses must provide a CSF (Constancia de Situación Fiscal). ## KYC and compliance All off-ramp activity is subject to KYC and AML screening. The level of verification required scales with transaction volume: * **Small flows** can complete with a single hosted KYC link (government-issued ID and a selfie). * **Larger flows and business accounts** require additional documentation: proof of address, beneficial ownership disclosures, and source-of-funds attestation where applicable. For questions about KYC requirements for your organization, contact [help@coinvoyage.io](mailto:help@coinvoyage.io). ## Country availability CoinVoyage onboards individuals and businesses globally, with the exceptions listed below. CoinVoyage is unable to onboard customers who are resident or registered in the following jurisdictions: Afghanistan, Belarus, Cuba, Democratic Republic of the Congo, Gaza Strip, Iran, Iraq, Lebanon, Libya, Myanmar, North Korea, Russian Federation, Somalia, South Sudan, Sudan, Syria, Ukrainian Territories (Crimea, Donetsk, Luhansk), Venezuela, West Bank, Yemen. ## On the roadmap CoinVoyage is actively expanding rail coverage. Currencies currently in active integration: * **GBP** — Faster Payments (United Kingdom) * **COP** — Bre-B / domestic bank transfer (Colombia) * Additional APAC currencies under evaluation # Custom fees: charge an extra percentage on every payment Source: https://docs.coinvoyage.io/dashboard/custom-fees Configure a custom fee percentage on top of the CoinVoyage platform fee, then claim accrued fees in USDC on EVM, Sui, or Solana. The **Custom Fee** setting lets your organization charge an additional fee percentage on top of the CoinVoyage platform fee. You can use it to collect an extra amount from each completed payment without changing your integration code. Custom fees accrue over time and can be claimed in **USDC** on **EVM**, **Sui**, and **Solana**. ## Setting a custom fee Navigate to **Organization Settings** and select the **Custom Fee** tab. Enter the additional fee percentage you want to charge on top of the CoinVoyage platform fee. Save your configuration. The custom fee is now applied to all new payments through CoinVoyage. ## Claiming accrued fees When fees have accrued, connect the wallet where you want to receive the funds and click **Claim** to receive them in USDC. Custom fees can help offset operational costs or add a merchant-controlled revenue line to each payment. # Manage API keys and webhooks in the Developers tab Source: https://docs.coinvoyage.io/dashboard/developers Generate and rotate your CoinVoyage API key pair, configure webhook endpoints, and follow security best practices for your integration. The [Developers tab](https://dashboard.coinvoyage.io/developers) is where you manage the credentials and event integrations that connect your backend to CoinVoyage. From here you can generate your API key pair, rotate credentials when needed, and configure webhook endpoints to receive real-time payment events. ## API keys CoinVoyage issues two credentials for your integration: * **Public API key** — Used client-side to identify your organization when initializing the SDK. * **Secret** — Used server-side to authenticate requests to the CoinVoyage API. ### Creating an API key Go to [dashboard.coinvoyage.io/developers](https://dashboard.coinvoyage.io/developers). Click **Generate API Key**. CoinVoyage creates a public key and a secret for your organization. The secret is shown only once at the time of creation. Copy it and store it securely — you will not be able to retrieve it again from the dashboard. Store your secret in a secure secrets manager or environment variable. Never commit it to source control or expose it in client-side code. ### Rotating API keys If your secret is compromised, or as part of routine credential hygiene, you can rotate your API key pair from the Developers tab. In the Developers tab, find your existing key pair and click **Rotate**. Confirm the action. CoinVoyage invalidates the existing credentials and generates a new public key and secret. Replace the old credentials in your application with the new key pair before the old ones expire or are invalidated. Rotating your API key immediately invalidates the previous secret. Make sure your updated credentials are deployed before rotating to avoid downtime. ## Webhook endpoints Webhooks allow CoinVoyage to notify your backend in real time when payment events occur, such as a payment confirmation or a dispute. You can register one or more endpoint URLs and subscribe to the specific events you care about. ### Adding a webhook endpoint In the Developers tab, scroll to the **Webhooks** section and click **Add Endpoint**. Provide the HTTPS URL on your server that will receive webhook payloads. Choose the event types you want to receive — for example, payment confirmations or dispute notifications. Save the configuration. CoinVoyage will begin sending events to your endpoint for the subscribed event types. For implementation details including payload structure and signature verification, see the [Webhooks overview](/webhooks/overview). ## Security best practices * **Keep your secret server-side.** Never include your API secret in frontend code, browser bundles, or mobile apps. * **Use environment variables.** Store credentials in your deployment environment rather than hardcoding them in your codebase. * **Rotate immediately if compromised.** If you suspect your secret has been exposed, rotate your API key pair right away and update your integration with the new credentials. * **Validate webhook signatures.** Always verify the signature on incoming webhook payloads before acting on the event data. # Invoices: send crypto payment requests to customers Source: https://docs.coinvoyage.io/dashboard/invoices Create and send crypto invoices directly from the CoinVoyage Dashboard. Customers pay via a link; you receive settlement in your preferred currency. CoinVoyage invoices let you generate crypto payment requests directly from the dashboard and deliver them to customers by email. Your customer receives a link to pay in their preferred cryptocurrency, and you receive settlement in the asset and chain you have configured — no custom integration code required. To automate invoice creation, drafts, listing, and lifecycle management, see the complete [Invoices guide](/invoices/overview) and [Invoices API](/invoices/api). ## Before you start You need to configure your **organization profile** in the dashboard before you can create invoices. The organization profile supplies the sender details shown on the invoice and payment email your customer receives. Set up your organization profile under **Organization Settings** before creating your first invoice. ## Creating an invoice Navigate to the Invoices section in your [CoinVoyage Dashboard](https://dashboard.coinvoyage.io/) and click **Create Invoice**. Fill in the required fields: * **Amount** — The payment amount you are requesting. * **Currency** — The currency denomination for the invoice amount. * **Customer details** — The recipient's name and email address. * **Description** — A description of the goods or services being invoiced. * **Due date** — Optionally set a date by which payment is expected. Review the invoice and click **Send**. CoinVoyage emails the invoice to your customer with a payment link included. ## How the customer pays Your customer receives an email containing the invoice details and a payment link. When they click the link, they are taken to a CoinVoyage-hosted payment page where they can complete the transaction using any supported cryptocurrency. The payment experience is the same widget your customers may already know from your checkout — they connect their wallet, select the token they want to pay with, and confirm the transaction on-chain. ## How you receive settlement Once your customer completes the payment, CoinVoyage settles the funds to you in your preferred currency and chain, as configured in your [Settlement settings](/dashboard/settlement). You do not need to handle conversion or routing manually. Make sure you have at least one settlement currency configured before sending invoices so payments route correctly to your wallet. # CoinVoyage Dashboard: manage payments and settings Source: https://docs.coinvoyage.io/dashboard/overview Manage payments, configure settlement currencies, issue refunds, withdraw to fiat, and set up developer integrations from one place. The [CoinVoyage Dashboard](https://dashboard.coinvoyage.io/) is the central place for managing every aspect of your crypto payment operations. From here you can monitor analytics in real time, inspect transactions, configure how and where your payments settle, withdraw funds to your bank account, and set up developer integrations — all without leaving a single interface. ## What you can do * **Analyze payments** — Monitor volume, conversion rates, payment method breakdowns, and trends in real time and over historical periods. * **Search and review transactions** — Inspect every crypto payment with full on-chain metadata, activity summaries, and status details. * **Initiate refunds** — Issue full or partial on-chain refunds directly from a transaction's detail view. * **Configure settlement currencies** — Define which asset and chain you want to receive payouts in, regardless of what your customers pay with. * **Withdraw to fiat** — Off-ramp settlement balances to a linked bank account after completing KYC or KYB. * **Set custom fees** — Charge an additional fee percentage on top of the CoinVoyage platform fee and claim accrued earnings in USDC. * **Manage developer integrations** — Generate API keys and configure webhook endpoints for payment events. * **Create and send invoices** — Generate crypto payment requests and email them directly to customers. ## Dashboard sections Search, filter, and inspect payments. Initiate full or partial refunds from any completed transaction. Configure the assets and chains you want to receive payment settlement in. Off-ramp settlement balances to a linked bank account after completing KYC or KYB. Set an additional fee percentage and claim accrued earnings in USDC. Create and send crypto payment invoices to customers directly from the dashboard. Generate API keys, rotate secrets, and set up webhook endpoints for your integration. # Settlement currencies: choose your payout asset and chain Source: https://docs.coinvoyage.io/dashboard/settlement Define which asset and chain you receive payment settlement in, regardless of what your customers pay with at checkout. The [Settlement tab](https://dashboard.coinvoyage.io/settlement) controls how and where CoinVoyage pays out completed payments. You can define which asset and chain you receive funds in, regardless of what your customers pay with. ## How settlement currencies work When a customer pays in any supported cryptocurrency, such as BTC, ETH, or SOL, CoinVoyage can automatically convert and settle the funds to the asset and chain you specify. You can configure multiple settlement currencies across different chains, and CoinVoyage optimizes routing by selecting the path with the best available quote. All settlements are non-custodial and executed on-chain. Settlement currency configuration applies only to orders with `mode = "SALE"`. A `SALE` order without explicit `currency` and `recipient` values requires at least one settlement currency configured in the dashboard. If a `SALE` order includes `currency` and `recipient`, that order settles to the specified asset and address instead of the dashboard default. For `mode = "DEPOSIT"`, the crypto asset settles directly to the recipient address specified in the order request. ## Configuring a settlement currency Go to [dashboard.coinvoyage.io/settlement](https://dashboard.coinvoyage.io/settlement) and click **Add Settlement Currency**. Choose the blockchain network you want to receive settlement on. Choose the token or coin you want your payments converted and settled into on that chain. Confirm the selection. The new settlement currency is now active and will be used for eligible incoming payments. You can add multiple settlement currencies to give CoinVoyage more routing options and improve settlement efficiency. # Transactions: search, inspect, and refund payments Source: https://docs.coinvoyage.io/dashboard/transactions Use the Transactions tab to search and filter payments, view full on-chain details, and initiate full or partial refunds directly from the dashboard. The [Transactions tab](https://dashboard.coinvoyage.io/transactions) logs every crypto payment and settlement event processed through CoinVoyage. You can use it to monitor payment flow, debug failed or pending payments, confirm on-chain settlements, and issue refunds — all from a single interface. ## Searching and filtering transactions The Transactions tab provides search and filter controls to help you isolate specific orders or payment events. * Use the **search bar** to look up transactions by order ID, wallet address, or other identifiers. * Apply **filters** to narrow results by status, date range, chain, or token. * The **analytics summary** at the top of the page gives you a snapshot of volume and activity trends across your filtered view. ## Viewing transaction details Click any row in the transactions table to open the full detail page for that payment. The detail view includes: * **On-chain metadata** — The transaction hash, originating chain, token, and amount. * **Status** — Whether the payment is pending, complete, or failed. * **Associated hashes** — Links to on-chain records for the payment and any related settlement or swap transactions. Use this view to confirm that a specific payment settled correctly or to investigate unexpected statuses. ## Initiating refunds You can issue a refund from any completed transaction's detail view. Refunds are executed as new on-chain transactions and are linked back to the original payment. Refunds are recorded as child transactions under the original payment. They appear in the **Refund Transactions** section of the transaction detail page once submitted. ### How to create a refund From the Transactions tab, click the row for the completed payment you want to refund. In the transaction detail view, click the **Refund** button to open the refund modal. Complete the required fields in the modal: * **`refund_amount`** — The amount to refund. Supports both full and partial refunds in the settlement currency. * **`refund_reason`** — A required string explaining why the refund is being issued. * **`note`** — An optional field for any additional context you want to record. * **`recipient_address`** — The wallet address that will receive the refunded funds. Click **Proceed**. The CoinVoyage widget opens so you can select the currency for the refund and confirm the on-chain transaction. Once sent, refund status updates in real time within the dashboard. All refund transactions appear under the **Refund Transactions** section of the original payment's detail page. Refunds are executed as a new on-chain transfer. They are not reversed charges — the refunded amount is sent directly to the recipient address you specify. ### Full vs. partial refunds * **Full refund** — Set `refund_amount` to the total settled amount of the original payment. * **Partial refund** — Set `refund_amount` to any amount less than the original settlement amount. You can issue multiple partial refunds against the same payment, up to the original total. Once a refund transaction is confirmed on-chain, it cannot be reversed. Double-check the `recipient_address` and `refund_amount` before confirming. # Withdrawals: off-ramp crypto settlement balances to fiat Source: https://docs.coinvoyage.io/dashboard/withdrawals Move supported settlement balances off-chain to a linked bank account after completing identity verification and configuring a payout destination. The [Withdrawals section](https://dashboard.coinvoyage.io/settlement) lets you move supported settlement balances off-chain to a linked bank account. You can use it to off-ramp funds after completing identity verification and linking a payout destination. Only wallets you've configured under Settlement are available for withdrawal. ## Viewing withdrawal history The withdrawals view includes a history table for every payout. Use it to track withdrawal status, payment rail, withdrawn amount, and completion date. ## How to withdraw Before you can withdraw, your organization must complete identity verification. Individual accounts require **KYC** (Know Your Customer); business accounts require **KYB** (Know Your Business). Link the bank account you want to receive fiat payouts. Follow the verification steps to confirm ownership. In the Withdrawals section, choose the settlement coin you want to off-ramp and the destination bank account. Connect the wallet holding your settlement balance and confirm the off-ramp transaction. Bank account details and recipient information must match the verified customer or business profile for the organization. # CoinVoyage integration flows Source: https://docs.coinvoyage.io/flows/overview Choose the right CoinVoyage flow for wallet deposits, merchant checkout, swaps, optional fiat off-ramp payouts, dashboard invoices, and x402 payments. Most CoinVoyage payment integrations use the same order lifecycle. Adjacent operational flows cover swaps, off-ramp verification, bank accounts, off-ramp intents, and x402 agent payments. Use this page to choose the flow that matches your product before you jump into endpoint-level reference. Bank-account payouts are optional. You can receive funds in an externally owned account (EOA) or any configured destination wallet and move those funds wherever you want from that wallet. Use the off-ramp verification, bank-account, and off-ramp intent APIs only when you want CoinVoyage to coordinate a fiat payout to a linked bank account. ## Flow map | Goal | Flow | Credential boundary | | --------------------------------------------------- | ------------------------ | ------------------------------------------------------- | | Let a user fund a wallet or app balance | `DEPOSIT` order | Public API key | | Accept checkout payments for your business | `SALE` order | Authorization signature created on the server. | | Exchange one supported on-chain asset for another | Swap | Public API key | | Move settlement funds to a bank account | Optional off-ramp intent | Authorization signature created on the server. | | Send payment links without code | Dashboard invoices | Dashboard operator workflow. | | Pay for protected resources from an agent or server | x402 payments (Preview) | Agent/server signs the payment with its own wallet key. | ## Example 1: Let users deposit to their own wallet Use this pattern when a user wants to fund a wallet or account on a destination chain while paying from a different chain or token. Wallet funding, app balances, gaming accounts, trading accounts, and user-controlled deposits. `DEPOSIT` ### Flow Ask the user which supported chain and destination address they want to receive funds on. Create a `DEPOSIT` order with the destination chain, destination token, amount, and recipient address. Render `PayButton` or pass the generated `orderId` to your own checkout UI. Listen for `ORDER_COMPLETED`, `ORDER_EXPIRED`, `ORDER_REFUNDED`, and `ORDER_ERROR` events. ```tsx theme={null} ``` ### Implementation notes * `DEPOSIT` can use the public API key because the user-provided destination defines where funds go. * Store your internal account or session ID in order metadata if the deposit credits an app balance. * Use webhooks for final crediting. Browser callbacks are useful for UI updates, but they should not be your only fulfillment signal. * This flow does not require off-ramp verification, a linked bank account, or a fiat payout unless you later add a fiat off-ramp experience. Read next: [Quickstart](/quickstart), [PayButton](/sdk/paybutton), and [Orders](/concepts/pay-orders). ## Example 2: Accept merchant checkout payments Use this pattern when your application sells products, bookings, subscriptions, credits, or services and you want settlement to your configured merchant wallet or a specific settlement asset. Ecommerce checkout, SaaS billing, travel bookings, digital goods, and service payments. `SALE` ### Flow Create the order in your backend first and store the amount, currency, customer, and line items. Use `ApiClient.createSaleOrder()` with your API secret. Include your internal order ID in metadata. The client renders the payment modal with the server-generated `orderId`. Do not expose the API secret. Fulfill the order after your backend receives and verifies `ORDER_COMPLETED`. ```typescript theme={null} const { data, error } = await apiClient.createSaleOrder( { amount: "149", fiat_unit: "USD", metadata: { order_id: "order_123", customer_id: "customer_456", }, }, process.env.COIN_VOYAGE_API_SECRET! ); ``` ### Implementation notes * `SALE` must be created server-side because it authorizes settlement to your merchant configuration. * Make fulfillment idempotent by internal order ID and CoinVoyage order ID. * Handle `EXPIRED`, `FAILED`, `REFUNDED`, and `PARTIAL_PAYMENT` states so unpaid orders do not remain ambiguous. * Fiat off-ramp payout is a separate, optional follow-up flow. If you settle to your EOA or another configured wallet, you can move funds directly on-chain without using bank-account payouts. Read next: [ApiClient](/sdk/apiclient), [API overview](/api/overview), [Webhooks](/webhooks/overview), and [Production checklist](/guides/production-checklist). ## Example 3: Swap between supported assets Use this pattern when you want a wallet to exchange one supported on-chain asset for another. For checkout and deposit flows, CoinVoyage can route swaps as part of payment execution; use the standalone swap APIs when the swap itself is the user action. Wallet rebalancing, pre-funding a payment asset, treasury operations, and custom swap UI. `swapQuote()`, `swapExecute()` ### Flow Ask the wallet owner for the source asset, destination asset, amount, sender address, and acceptable slippage. Call `ApiClient.swapQuote()` with the source and destination details so the user can review the expected output and route. Call `ApiClient.swapExecute()` to create the payment instructions required to fund and execute the swap. Have the source wallet sign and submit the returned transaction data, then track the resulting on-chain transaction in your own UI or ledger. ### Implementation notes * Standalone swaps create an internal `SWAP` order and return payment instructions for funding the swap. * Treat quotes as time-sensitive. Refresh the quote if the user waits, changes wallets, or changes the source amount. * Show slippage, fees, source asset, destination asset, and destination chain before the user signs. * Keep swap execution separate from fiat off-ramp payouts. A swap changes on-chain assets; an off-ramp intent moves eligible settlement funds to a linked bank account. Read next: [SwapKit widget](/sdk/swapkit), [Swap API methods](/sdk/apiclient/swaps), and [Supported networks](/concepts/supported-networks). ## Example 4: Off-ramp settlement funds to a bank account Use this pattern only when you want CoinVoyage to help move an on-chain settlement balance to fiat through a linked bank account. The off-ramp verification, bank-account, and off-ramp intent APIs belong here because they prepare and execute the optional off-ramp path. Treasury operations, creator payouts, merchant fiat settlement, and finance-team withdrawals. Verification, bank accounts, off-ramp intents ### Flow Create a hosted verification link with `ApiClient.createOffRampVerification()` and check the organization's verification status with `ApiClient.getOffRampVerificationStatus()`. Add the payout destination with `ApiClient.addBankAccount()`, then use `listBankAccounts()` or `getBankAccount()` when an operator selects where funds should go. Call `ApiClient.createOffRampIntent()` from your server with the source currency, amount, linked bank account ID, withdrawal currency, payment rail, and sender wallet address. If the response includes wallet execution data, have the source wallet sign the required on-chain transaction. Track off-ramp status with `listOffRampIntents()` and your internal ledger. ### Implementation notes * Off-ramp verification, bank account, and off-ramp intent methods are signed server-side operations. Never expose the API secret to the browser. * Bank account details must match the verified individual or business profile for the organization. * Keep payment settlement and fiat payout as separate ledger events. A completed order means funds reached the destination wallet; a completed off-ramp intent means a later fiat payout finished. * Use [Supported regions](/concepts/supported-regions) to choose the correct fiat rail and recipient fields before adding bank-account forms. Read next: [ApiClient](/sdk/apiclient), [API overview](/api/overview), [Supported regions](/concepts/supported-regions), and [Withdrawals](/dashboard/withdrawals). ## Example 5: Send no-code payment invoices Use this pattern when an operator wants to request payment without building a custom checkout flow. Manual invoices, sales-assisted deals, professional services, B2B payments, and one-off payment links. CoinVoyage Dashboard ### Flow Open the dashboard, enter the customer, amount, due date, and line items, then generate the payment link. Email the invoice from the dashboard or copy the payment link into your own customer communication. The customer opens the link, chooses a supported chain and token, and completes the payment. Track status in the dashboard and use webhooks if your backend needs to update CRM, ERP, or accounting records. ### Implementation notes * Invoices are useful when you do not need an embedded checkout or SDK integration. * Use clear line items and due dates so finance and support can reconcile payments later. * If invoices need to update an internal system, subscribe to webhook events and store invoice identifiers in metadata. Read next: [Invoice lifecycle](/invoices/overview), [Create invoices in the dashboard](/dashboard/invoices), and [Automate invoices through the API](/invoices/api). ## Example 6: Pay protected resources with x402 (Preview) Use this pattern when an agent, server, or automation flow needs to fetch a protected resource, satisfy an x402 `PAYMENT-REQUIRED` challenge, and retry the request with a `PAYMENT-SIGNATURE` header. x402 support is in Preview. Expect supported chains, payload details, and helper APIs to evolve before general availability. ### Flow Fetch the resource normally. If payment is required, the server returns a `PAYMENT-REQUIRED` challenge with one or more acceptable payment requirements. Use `@coin-voyage/paykit-headless` to decode the challenge and choose a supported chain, network, asset, and amount. Sign the payment payload with the agent or server wallet key and retry the original request with the generated `PAYMENT-SIGNATURE` header. Read next: [PayKit Headless](/sdk/paykit-headless). # Production checklist Source: https://docs.coinvoyage.io/guides/production-checklist Validate credentials, settlement settings, webhooks, payment states, refunds, monitoring, and reconciliation before launching CoinVoyage payments in production. Use this checklist before switching a CoinVoyage integration to production. It is written for teams that already have a working payment flow in development and are preparing to accept live payments. ## Credentials * [ ] `COIN_VOYAGE_API_SECRET` is available only to backend services, server actions, or API routes. * [ ] Client-side code uses only the public API key, such as `NEXT_PUBLIC_COIN_VOYAGE_API_KEY`. ## Settlement configuration * [ ] The production organization has at least one settlement currency configured if you plan to use `SALE` orders. * [ ] If not using a settlement currency, make sure order generation specifies `recipient` and `currency` at all times. * [ ] Settlement wallet addresses have been checked on the correct chain. * [ ] It has been confirmed who owns each receiving wallet, and they have access to them. * [ ] A small live payment has been completed and reconciled before opening the flow to all users. * [ ] Off-ramp settings, bank details, and KYC or KYB status are complete if you plan to withdraw to fiat. ## Order creation * [ ] `SALE` and `REFUND` orders are created from server-side code only. * [ ] Each order stores your internal order, invoice, account, or customer ID in `metadata`. * [ ] Your backend stores the CoinVoyage order ID before showing the payment modal. * [ ] Your integration validates amount, currency, destination chain, and destination address before creating an order. ## Payment state handling Your application should handle every outcome and recovery state, not just successful payments. | State | Required handling | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `COMPLETED` | Fulfill the order, credit the account, mark the invoice paid, or release access. | | `EXPIRED` | Mark the checkout attempt expired and let the user start a new payment. | | `REFUNDED` | Record the refund transaction and notify the user or operator. | | `FAILED` | Keep the order unpaid and send the case to support or automated recovery. | | `PARTIAL_PAYMENT` | Pause fulfillment. For Pay to Address, show `payment.funding.remaining_amount` and allow additional deposits; for other methods, route the case to a resolution flow. | * [ ] Fulfillment code is idempotent by CoinVoyage order ID. * [ ] Duplicate webhook deliveries do not create duplicate shipments, credits, or inventory updates. * [ ] Partial-payment and underpayment cases are visible to support. * [ ] Pay to Address partial payments remain unfulfilled until additional deposits move the order to `COMPLETED`. * [ ] Users can recover from expired payments by creating a new order. ## Webhooks * [ ] A production webhook endpoint is configured in the [Developers dashboard](https://dashboard.coinvoyage.io/developers). * [ ] The endpoint uses HTTPS. * [ ] The raw request body is verified with `CoinVoyage-Webhook-Signature` before parsing or processing. * [ ] The webhook secret is stored in a server-side secret store. * [ ] The endpoint returns a `2xx` response quickly and moves heavy work to a queue or background job. * [ ] Event processing is idempotent by event type, delivery timestamp, and CoinVoyage order ID. * [ ] Unknown event types are logged and acknowledged instead of breaking delivery. * [ ] Webhook failures trigger alerts. Treat webhooks as the real-time signal and API reconciliation as the safety net. Do not depend on browser callbacks alone for fulfillment. ## Monitoring and reconciliation * [ ] Payment creation, quote generation, webhook receipt, fulfillment, and refund actions are logged with correlation IDs. * [ ] Dashboards track created, started, confirming, executing, completed, expired, refunded, failed, and partial-payment counts. * [ ] Alerts exist for webhook verification failures, elevated API errors, delayed settlement, and repeated provider failures. * [ ] A scheduled reconciliation job compares internal orders against CoinVoyage order status. * [ ] Reconciliation can safely repair missed webhook updates. * [ ] Finance can export or inspect transaction records needed for accounting. ## Final launch review Use a small amount and verify that the order reaches `COMPLETED`, the destination wallet receives funds, and your application fulfills exactly once. Refund the test payment, confirm the refund transaction, and verify that your internal records link the refund to the original order. Temporarily disable non-critical webhook processing in a controlled test, then confirm your reconciliation job can repair missed state updates. Rotate a non-production key first. Confirm your deployment and rollback process can update credentials without exposing secrets. After these checks pass, update production traffic gradually and monitor completion rate, error rate, webhook latency, and support tickets during the first live sessions. # CoinVoyage: non-custodial cross-chain crypto payments Source: https://docs.coinvoyage.io/introduction Non-custodial crypto payments platform — users pay with any supported token while you settle in the asset and chain you choose. CoinVoyage is a cross-chain crypto payment platform that lets your customers pay with the tokens and chains they already own — while you receive settlement in the asset and network that fits your product. It's non-custodial, meaning CoinVoyage never holds your funds during the payment flow. Find the right path for payments, swaps, invoices, or agent payments. Understand the end-to-end order flow and route optimization. Add SALE and DEPOSIT payment flows to your application. Add standalone token swap capabilities to your application. Build custom interfaces and backend workflows with the REST API or ApiClient. Manage payments, configure settlement, and issue invoices. ## Why CoinVoyage * **Flexible** — Let customers pay with any supported token or chain. You settle to whatever you prefer. * **Fast** — Routes are optimized across multiple liquidity providers for speed and best pricing. * **Simple** — Embed payments with `@coin-voyage/paykit` or token swaps with `@coin-voyage/swapkit`. * **Non-custodial** — Funds go directly from buyer to your wallet. CoinVoyage never holds them. For hosted payment requests, start with [Invoices](/invoices/overview). For agent or server payments, see [PayKit Headless (Preview)](/sdk/paykit-headless). ## Start a PayKit integration Sign up at [dashboard.coinvoyage.io](https://dashboard.coinvoyage.io/) and create your organization. In the dashboard, go to **Developers** and generate an API key and secret. Install `@coin-voyage/paykit` and add the required providers to your React app. Render `` in your UI to launch the CoinVoyage payment modal for your users. Ready to dive in? Start with the [PayKit quickstart](/quickstart) guide. # Invoices API Source: https://docs.coinvoyage.io/invoices/api Create, publish, list, retrieve, archive, and delete CoinVoyage invoices through the signed v3 REST API. The v3 Invoices API supports complete invoice automation. All invoice endpoints require an HMAC-SHA256 authorization signature and must be called from your server. TypeScript integrations can use the signed methods in the [ApiClient invoice reference](/sdk/apiclient/invoices). Endpoint schemas are also available in the generated [API reference](/api-reference). ## Endpoints | Method | Path | Purpose | | -------- | -------------------------------- | ------------------------------------------------------------ | | `POST` | `/invoices` | Create and publish an invoice, or publish an existing draft. | | `POST` | `/invoices/drafts` | Create a draft or update an unpublished draft. | | `GET` | `/invoices` | List invoices and drafts with pagination and search. | | `GET` | `/invoices/{invoice_id}` | Retrieve one invoice. | | `POST` | `/invoices/{invoice_id}/archive` | Archive an invoice. | | `DELETE` | `/invoices/{invoice_id}` | Delete an unpublished draft. | ## Authentication Generate the `Authorization` value from the API secret, HTTP method, exact path without the `/v3` prefix, and current timestamp. The `ApiClient` generates this automatically when you pass `apiSecret`. ```typescript theme={null} const authorization = apiClient.generateAuthorizationSignature( process.env.COIN_VOYAGE_API_SECRET!, "POST", "/invoices" ); ``` ## Create and publish `POST /invoices` validates the complete invoice, creates a linked `SALE` order, and emails the recipient a PDF and hosted payment link. ```json theme={null} { "invoice": { "no": "INV-001", "date": "2026-08-03", "due_date": "2026-08-17" }, "from": { "name": "Acme Inc.", "email": "billing@acme.com" }, "recipient": { "name": "Jane Doe", "email": "jane@example.com" }, "items": [ { "name": "Consulting", "unitPrice": "100.00", "quantity": "2", "tax": "10" } ] } ``` To publish an existing draft, include its `id` and provide the complete required blocks shown above. ## Save or update a draft `POST /invoices/drafts` accepts partial content. Omit `id` to create a draft or include an unpublished draft ID to update it. ```json theme={null} { "id": "invoice_draft_123", "invoice": { "no": "INV-002" }, "recipient": { "email": "customer@example.com" } } ``` Draft blocks are stored as supplied and validated only when published. ## List invoices `GET /invoices` accepts: | Parameter | Default | Constraints | Description | | --------- | ------: | -------------------- | -------------------------------------------------------------------------------------------- | | `limit` | `20` | 1–100 | Maximum results returned. | | `offset` | `0` | 0 or greater | Number of results skipped. | | `search` | — | Up to 200 characters | Case-insensitive match on invoice ID or number, recipient name or email, or linked order ID. | The response contains `data: InvoiceResponse[]` and pagination metadata. ## Retrieve, archive, or delete * `GET /invoices/{invoice_id}` returns the invoice details and linked orders. * `POST /invoices/{invoice_id}/archive` retains the invoice and sets `archived_at`. * `DELETE /invoices/{invoice_id}` returns `204` for a deleted unpublished draft. Published invoices cannot be deleted. ## Response fields ```typescript theme={null} type InvoiceResponse = { id: string organization_id: string data: { invoice?: Partial from?: Partial recipient?: Partial items?: InvoiceItem[] } order_id?: string orders: Order[] payment_url: string published_at: string | null archived_at?: string | null created_at: string updated_at: string } ``` Use `order_id` and `orders` for payment reconciliation. A published invoice is not necessarily paid; fulfillment depends on the linked order reaching `COMPLETED`. ## Common errors | Status | Meaning | | ------ | ---------------------------------------------------------------------- | | `400` | Invalid list pagination or search parameters. | | `401` | Missing or invalid authorization signature. | | `404` | Invoice or draft not found, or invoice already archived. | | `422` | Invalid request body or invoice validation failure. | | `500` | Internal processing failure on supported read or lifecycle operations. | # CoinVoyage invoices Source: https://docs.coinvoyage.io/invoices/overview Create crypto invoices from the dashboard or API, send hosted payment links, track linked orders, and settle customer payments. CoinVoyage invoices turn line items and customer details into a hosted crypto payment request. Publishing an invoice creates a linked `SALE` order, emails the recipient a PDF invoice, and includes a hosted payment link. Settlement follows your organization's configured settlement currencies and wallets. Create and send invoices without writing integration code. Automate drafts, publishing, retrieval, archiving, and listing. ## Before you start * Configure your organization profile. Its sender details appear on the invoice and customer email. * Configure at least one [settlement currency and wallet](/dashboard/settlement). * For API integrations, keep your API secret on the server. ## Invoice lifecycle ```text theme={null} Draft -> Published -> Customer payment -> Linked order completed \ -> Archived ``` | Stage | What happens | | ---------- | ---------------------------------------------------------------------------------------------------------------------------- | | Draft | Partial invoice content can be saved and updated. No email is sent. | | Published | Required fields are validated, a `SALE` order is linked, and the recipient receives the PDF and hosted payment link. | | Payment | The customer opens `payment_url` and pays using an available CoinVoyage payment method. | | Settlement | The linked order routes funds to your configured settlement destination. | | Archived | A published invoice can be archived as a separate lifecycle action. It remains retrievable and is marked with `archived_at`. | Published invoices cannot be deleted. Delete is available only for unpublished drafts. ## Required publishing fields | Block | Required fields | | --------- | ----------------------------------------------------------------------------------------- | | Invoice | Invoice number and invoice date; due date is optional. | | Sender | Name and email; phone, logo, and address fields are optional. | | Recipient | Name and email; phone and address fields are optional. | | Items | Name, decimal-string unit price, and decimal-string quantity; tax and image are optional. | ## Payment and settlement An invoice response includes: * `payment_url`, the customer-facing hosted invoice link. * `order_id`, the latest linked order ID. * `orders`, the linked order snapshots. * `published_at` and `archived_at`, the invoice lifecycle timestamps. Use the linked order status—not invoice timestamps—to decide whether to fulfill goods or services. Fulfill only after the order reaches `COMPLETED`. ## Partial payments If a customer underpays through **Pay to Address**, the linked order enters `PARTIAL_PAYMENT`. The same flow can accept additional deposits until the required amount is received. Funding progress is available under `order.payment.funding`: * `required_amount` is the total needed. * `received_amount` is the cumulative amount received. * `remaining_amount` is the amount still due. * `transactions` lists every funding deposit. Keep the invoice unpaid and fulfillment paused until the linked order reaches `COMPLETED`. Additional deposits after `PARTIAL_PAYMENT` are supported only by Pay to Address. Other payment methods require a separate resolution path. ## Reconciliation Store the CoinVoyage invoice ID and linked order ID with your internal invoice record. Process order webhook deliveries idempotently, and use `ORDER_COMPLETED` as the fulfillment signal. # Quickstart: install CoinVoyage and accept payments Source: https://docs.coinvoyage.io/quickstart Install @coin-voyage/paykit, configure WalletProvider and PayKitProvider, and render a working PayButton in your React app in minutes. This guide walks you through installing `@coin-voyage/paykit`, wiring up the required providers, and rendering a `PayButton` that launches the CoinVoyage payment modal — all in under five minutes. ## Prerequisites * A React application (Next.js, Vite, Create React App, etc.) * Node.js 18 or later * A [CoinVoyage account](https://dashboard.coinvoyage.io/) ## Steps Install `@coin-voyage/paykit` and its required peer dependency `@tanstack/react-query`. ```bash npm theme={null} npm i @coin-voyage/paykit @tanstack/react-query@^5.90.6 ``` ```bash pnpm theme={null} pnpm add @coin-voyage/paykit @tanstack/react-query@^5.90.6 ``` ```bash yarn theme={null} yarn add @coin-voyage/paykit @tanstack/react-query@^5.90.6 ``` ```bash bun theme={null} bun add @coin-voyage/paykit @tanstack/react-query@^5.90.6 ``` Open the [CoinVoyage Dashboard](https://dashboard.coinvoyage.io/) and create an organization if you haven't already. Then navigate to **Developers** and create an API key. You will receive two values: * **API Key** (public) — safe to use in client-side code. Expose as `NEXT_PUBLIC_COIN_VOYAGE_API_KEY`. * **API Secret** — server-side only. Never expose this in client-side code. Keep your API secret on the server at all times. Only the public API key belongs in client-side code. Wrap your app with `QueryClientProvider`, `WalletProvider`, and `PayKitProvider`. In a Next.js app, create a `providers.tsx` file: ```tsx providers.tsx theme={null} "use client"; import { PayKitProvider, WalletProvider } from "@coin-voyage/paykit"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; const queryClient = new QueryClient(); export function Providers({ children }: { children: React.ReactNode }) { if (!process.env.NEXT_PUBLIC_COIN_VOYAGE_API_KEY) { throw new Error("NEXT_PUBLIC_COIN_VOYAGE_API_KEY is required"); } return ( {children} ); } ``` Then import and use `` in your root layout: ```tsx app/layout.tsx theme={null} import { Providers } from "./providers"; export default function RootLayout({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` Add `` anywhere in your app to launch the CoinVoyage payment modal. The example below creates a deposit button that settles 10 SUI to your specified wallet: ```tsx components/deposit-button.tsx theme={null} "use client"; import { PayButton } from "@coin-voyage/paykit"; import { ChainId } from "@coin-voyage/paykit/server"; export function DepositButton() { return ( console.log("Awaiting payment")} onConfirmingPayment={() => console.log("Payment confirming")} onExecutingPayment={() => console.log("Payment executing")} onPaymentCompleted={() => console.log("Payment completed")} /> ); } ``` When a user clicks the button, CoinVoyage opens a modal where they can select their preferred chain and token to pay with. ## Next steps Explore provider options, PayButton props, and payment status hooks. Understand the full payment lifecycle from order creation to settlement. Receive real-time notifications when payments complete or fail. Monitor transactions, configure settlement, and manage your API keys. # CoinVoyage SDK and API changelog — release notes Source: https://docs.coinvoyage.io/resources/changelog Integration-facing CoinVoyage SDK and API changes, ordered newest-first. Includes breaking changes, new features, and user-visible fixes. Integration-facing changes to the CoinVoyage SDK, API, and platform behavior are documented here. Internal implementation details are omitted unless they change what you configure, call, receive, or need to handle in production. Releases follow [Semantic Versioning](https://semver.org/): major versions for breaking changes, minor for new backward-compatible functionality, and patch for bug fixes. Current docs describe the v3 API and PayKit `^3` surface. Older entries below are retained for historical context. *** ## 3.4.0 - 2026-08-03 **Added** * **Invoice generation via API**: Create and publish invoices, save drafts, list and retrieve invoices, archive invoices, and delete unpublished drafts through the v3 API and `ApiClient`. Published invoices create a linked `SALE` order and email the recipient a PDF with a hosted payment link. See the [Invoices guide](/invoices/overview). **Changed** * **Recoverable Pay to Address partial payments**: Orders in `PARTIAL_PAYMENT` can now accept additional deposits through Pay to Address until the required amount is received and the order can be fulfilled. This recovery behavior is available only for Pay to Address; other payment methods still require a separate resolution flow. *** ## 3.3.0 - 2026-07-29 This release changes the `portfolio()` request and response shapes. Replace the singular `wallet` field with a `wallets` array and handle per-wallet results and errors. See the [ApiClient migration example](/sdk/apiclient/wallets-and-search#portfolio). **Added** * **Robinhood Chain**: Added payment and settlement support for Robinhood Chain (`4663`). * **Across provider**: Added Across as a cross-chain routing provider. * **Uniswap V2 and V4**: Expanded Uniswap routing beyond V3 to include V2 and V4 liquidity. * **EUR card payments**: Card Payments can now fund eligible orders in EUR as well as USD. **Changed** * **Multi-wallet portfolio requests**: `ApiClient.portfolio()` and `POST /wallet/portfolio` now accept `wallets`, an array of 1–10 wallets. Responses contain a `portfolio` array of successful scans and an `errors` array for wallets that could not be scanned. **Removed** * **ChangeNow provider**: ChangeNow is no longer used for routing. *** ## 3.2.0 - 2026-07-07 **Added** * **PayButton direct payment routing**: `PayButton` and `PayButton.Custom` now accept `paymentMethod`, so integrations can open the modal directly to `PaymentMethod.WALLET`, `PaymentMethod.DEPOSIT_ADDRESS`, or `PaymentMethod.CARD` instead of first showing method selection. **Changed** * **On-ramp API errors**: On-ramp API responses now return user-friendly `error.message` values for common Link, identity, payment-method, session, and checkout failures. *** ## 3.1.0 - 2026-06-27 **Changed** * **PayKit Modal**: Improvements to Card Payment flow *** *** ## 3.0.0 - 2026-06-25 **Changed** * **API base URL**: Current API requests use `https://api.coinvoyage.io/v3`; the generated OpenAPI reference is served from `/v3/openapi.json`. * **Order API naming**: SDK helpers now use `Order` naming: `createDepositOrder()`, `createSaleOrder()`, `createRefundOrder()`, `getOrder()`, `listOrders()`, `orderQuotes()`, `createPayment()`, and `getOrderPaymentMethods()`. * **PayButton lifecycle callbacks**: Payment lifecycle callbacks are now split into `onAwaitingPayment`, `onConfirmingPayment`, `onExecutingPayment`, `onPaymentCompleted`, and `onPaymentBounced`. * **Off-ramp API naming**: Verification and fiat payout APIs use `createOffRampVerification()`, `getOffRampVerificationStatus()`, `listOffRampIntents()`, and `createOffRampIntent()`. * **Webhook payloads**: Webhook deliveries now use the v3 envelope `{ event, delivered_at, order }` with uppercase `ORDER_*` event identifiers. **Removed** * **Legacy PayOrder client helper names**: `createDepositPayOrder()`, `createSalePayOrder()`, `createRefundPayOrder()`, `getPayOrder()`, `listPayOrders()`, `payOrderQuote()`, `payOrderPaymentDetails()`, `getPayOrderPaymentMethods()`, and `swapData()` are no longer the current PayKit API surface. *** ## 2.5.0 - 2026-05-27 **Added** * **KYC helpers**: `ApiClient.createKYCLink()` creates an identity-verification link for an organization, and `ApiClient.getKYCStatus()` retrieves the current KYC and terms-of-service status. **Changed** * **Signed organization methods**: `ApiClient.listPayOrders()`, `ApiClient.listWithdrawals()`, and `ApiClient.createWithdrawal()` now require `apiSecret` as an argument. Server integrations that call these methods must pass the API secret from server-side code. *** ## 2.4.6 - 2026-05-27 **Added** * **PayKit Headless (Preview)**: Added `@coin-voyage/paykit-headless` for server and agent runtimes that need to satisfy CoinVoyage x402 `PAYMENT-REQUIRED` challenges. Sui x402 signing is supported through this package. * **Order status WebSocket hook**: Added `useOrderStatusWS()` for React integrations that need live backend PayOrder events outside the default PayButton UI. * **Backend API access**: Added `useBackendApi()` for advanced PayKit integrations that need direct access to the configured API client. * **ApiClient helpers**: Added `currencySearch()`, `portfolio()`, `listPayOrders()`, `listBankAccounts()`, `getBankAccount()`, `addBankAccount()`, `listWithdrawals()`, and `createWithdrawal()`. * **Organization-wide WebSocket subscriptions**: `ApiClient.subscribeOrderStatus()` can now subscribe to all organization PayOrder events when called with a server-generated authorization signature for `GET /ws`. **Changed** * **Quote filters**: `PayOrderQuoteParams` now accepts `chain_ids` instead of a single `chain_id`. * **Swap requests**: Swap requests now support `payment_rail` and nested `crypto` input options, including `source_currency`, `sender_address`, and `slippage_bps`. * **Route quote field**: Route quote responses use `quote_id`. * **Payment details**: Payment details now use the shared `PaymentDataBase` shape for rail-specific payment instruction data. **Fixed** * **PayButton order creation errors**: Backend order-creation failures are now surfaced to consumers. * **PayButton lifecycle callbacks**: Payment lifecycle callbacks continue working across multiple orders for the same button. * **`resetOnSuccess` behavior**: Successful payments reset payment state when configured. * **EVM transaction errors**: User-facing EVM transaction errors are clearer. * **Desktop WalletConnect**: Desktop WalletConnect routes through the QR flow correctly. *** ## 2.4.1 — 2026-04-08 **Added** * **`PARTIAL_PAYMENT` status**: A new terminal state for PayOrders that receive an insufficient amount. Use this status to detect and handle partial-payment outcomes in your order fulfillment logic. * **`PayOrderPartialPaymentEvent`**: A new event type so you can explicitly handle partial-payment outcomes in your event flows. *** ## 2.4.0 — 2026-04-04 **Added** * **Stripe on-ramp integration**: Full Stripe fiat-to-crypto on-ramp support, including geo-blocking for restricted regions. * **`PaymentSteps` model**: Tracks granular payment progress across stages such as on-ramp, swap, and delivery. * **Payment methods endpoint**: `GET /pay-orders/{payorder_id}/payment-methods` returns available payment methods for a given PayOrder. * **Organization settings in PayOrder responses**: `Organization.settings` is now a JSON field and is returned in `GET /pay-orders` and `GET /pay-orders/{payorder_id}` responses. * **`hosted_url` field**: Added to `GET /pay-orders` and `GET /pay-orders/{payorder_id}` responses so you can link customers directly to the hosted pay page. **Changed** * **Webhook payload coverage**: `payorder_error` and `payorder_refunded` events now include the full `payment_data` object. * **PayKit payment handling**: The widget now consumes `payment_data.steps`, supports organization settings, and exposes location-aware payment method options. * **SDK request and response models**: Added `metadata` support to `ClaimFeesRequest` and `SwapQuoteRequest`, updated order event types and `ClaimFeeResponse`, and expanded `ApiClient` to accept `Opts`. * **EVM prefunding flow**: Improved prefunding across CCTP, Direct, Relay, and Uniswap providers with better gas estimation, prefund calculation, and error handling. **Fixed** * **Webhook subscription filtering**: Webhook deliveries now correctly respect each subscriber's registered event types. * **Payment state persistence**: Improved handling of `payment_data` so the correct exchange state is always persisted throughout the payment flow. * **Refund PayOrder client flow**: Fixed `createRefundPayOrder` handling in `ApiClient`. * **WalletConnect desktop routing**: Desktop WalletConnect flows now route through the QR flow correctly. **Removed** * **Deprecated `RequestData` / `request` field**: Removed from `PayOrderResponse`. * **Legacy transaction hash aliases**: Removed `SourceTransactionHash` and `DestinationTransactionHash` alias fields. *** ## 0.1.25 — 2025-11-04 **Added** * **Phantom wallet (EVM)**: Users can now connect an EVM wallet through the Phantom browser extension. *** ## 0.1.24 — 2025-10-29 **Added** * **German localization**: Display the PayKit modal in German by specifying the `de-DE` language in your widget configuration. **Changed** * **`ApiClient` responses**: All `ApiClient` methods now return `APIResponse` for a consistent response shape. *** ## 0.1.23 — 2025-10-21 **Changed** * **`WalletProvider` props**: Updated property names and nesting structure. Review your `WalletProvider` configuration if you pass custom props. *** ## 0.1.22 — 2025-10-17 **Added** * **Stable Testnet support**: Added support for the Stable Testnet network so you can test integrations without using mainnet funds. # CoinVoyage integration FAQ: auth, testing, and flows Source: https://docs.coinvoyage.io/resources/faqs Common questions about testing, authentication, Deposit vs Sale flows, authorization signatures, and viewing transaction history in CoinVoyage. These are the most common questions developers ask when integrating CoinVoyage. If you don't find your answer here, email [help@coinvoyage.io](mailto:help@coinvoyage.io). You can test both the **Deposit** and **Sale** flows using the [CoinVoyage example site](https://example.coinvoyage.io/). Create an account in the [CoinVoyage Dashboard](https://dashboard.coinvoyage.io/), copy your **API Key** and **API Secret**, and add them to the example site. This connects the demo flow directly to your organization so you can verify the end-to-end payment experience before integrating into your own application. Your API Key and Secret are available in the [CoinVoyage Dashboard](https://dashboard.coinvoyage.io/): 1. Navigate to **Developers** → **API Keys** 2. Create a new key 3. Store both the **API Key** and **Secret** securely Your API Secret is shown only once at creation time. Store it in a secure secrets manager immediately. If you lose it, you will need to rotate the key. These two payment flows serve different use cases: **Deposit** — The user sends funds to a wallet address they control. For example, a user can fund their Sui wallet with native SUI while paying with USDC on mainnet. No server-side authorization is required. **Sale** — A merchant or organization creates an order for the user to fulfill. The user's funds are routed to the configured [settlement wallet](https://dashboard.coinvoyage.io/settlement). This mode requires a server-side **Authorization** signature generated from your API Secret, and the signature must be created on your backend — never in client-side code. The Authorization signature is an HMAC-SHA256 hash computed over `method + path + timestamp`. The `method` and `path` must match the request exactly. **Using the CoinVoyage PayKit SDK:** ```typescript theme={null} const signature = apiClient.generateAuthorizationSignature( process.env.COIN_VOYAGE_API_SECRET!, "POST", "/orders" ); ``` **Manual implementation:** ```typescript theme={null} import { createHmac } from "crypto"; function generateAuthorizationSignature( apiKey: string, apiSecret: string, method: string, path: string ): string { const timestamp = Math.floor(Date.now() / 1000).toString(); const data = `${method}${path}${timestamp}`; const signature = createHmac("sha256", apiSecret).update(data).digest("hex"); return `APIKey=${apiKey},signature=${signature},timestamp=${timestamp}`; } ``` Run this function only on the server. Never expose your API Secret in client-side code or commit it to source control. A transaction overview and detailed transaction pages are available in the [CoinVoyage Dashboard](https://dashboard.coinvoyage.io/) under **Transactions**. You can filter by status, date range, and payment method from that view. # CoinVoyage pricing: transaction fees and volume plans Source: https://docs.coinvoyage.io/resources/pricing CoinVoyage charges 1.5% per successful transaction with 0.5% off-ramp fees. Volume discounts are available on request. CoinVoyage is free to get started. Pricing is usage-based with transparent transaction fees and no hidden platform costs. You can contact [help@coinvoyage.io](mailto:help@coinvoyage.io) to discuss reduced rates once your volume grows. ## Fee schedule | Fee type | Standard rate | Description | | --------------- | ------------- | ----------------------------------------------- | | Transaction fee | 1.5% | Applied to each successful payment transaction. | | Swap fee | 0.25% | Applied to each successful swap. | | Off-ramp fee | 0.5% | Applied when withdrawing to fiat. | Volume discounts are available. Contact [help@coinvoyage.io](mailto:help@coinvoyage.io) to discuss custom rates based on your monthly transaction volume. ## Pricing options You can launch a payment integration in under five minutes from the [CoinVoyage Dashboard](https://dashboard.coinvoyage.io/) or through the API. Price in digital assets or fiat currency such as USD. Your customers see the amount in their preferred denomination at checkout. Accept payments across Bitcoin, Ethereum, Solana, Sui, and supported EVM networks including Arbitrum, Base, Optimism, Polygon, BSC, and Robinhood Chain. Let customers pay with the assets they hold while you receive the assets and chains you prefer. CoinVoyage handles quote selection and price conversion. ## Non-custodial payouts CoinVoyage never holds custody of your funds. Payments flow directly from the buyer to your designated wallet address or settlement route. Funds arrive in real time as soon as the on-chain transaction is confirmed. You maintain ownership of your assets at all times — no escrow, no lock-up periods. Configure multiple wallet addresses across EVM, Sui, Bitcoin, and Solana chains. ## Settlement currencies You configure which token and chain you want to receive in the [Dashboard](https://dashboard.coinvoyage.io/settlement). CoinVoyage automatically converts incoming payments to your chosen settlement currency when the payment flow requires it, and optimizes routing by selecting the path with the best quote when you have multiple settlement currencies configured. In the [CoinVoyage Dashboard](https://dashboard.coinvoyage.io/settlement), navigate to **Settlement**. Choose the cryptocurrency and blockchain network you want to receive payments on. Enter the wallet address where funds should be delivered. Save your settlement settings. CoinVoyage uses this configuration for all future payments. ## Supported payment methods Your customers can pay using any of the following networks. CoinVoyage handles the cross-chain conversion so you always receive your configured settlement currency. * **Bitcoin** (BTC) * **Ethereum** and EVM-compatible chains — Arbitrum, Base, Optimism, Polygon, BSC, Robinhood Chain * **Solana** (SOL) * **Sui** (SUI) ## Invoicing You can create and send crypto payment invoices directly from the dashboard without writing any code. * Generate payment links with QR codes * Set due dates and payment terms * Track invoice status in real time * Send automatic email notifications * Support itemized billing ## Volume pricing For businesses processing high transaction volume, CoinVoyage offers custom pricing plans with reduced fees and dedicated support. Transaction fees scale down based on your monthly volume. Contact us to get a custom rate for your business. Access priority support, a dedicated account manager, and custom integration assistance. To discuss volume pricing, email [help@coinvoyage.io](mailto:help@coinvoyage.io) or visit the [Support](/resources/support) page. # CoinVoyage API rate limits, 429 responses, and retry behavior Source: https://docs.coinvoyage.io/resources/rate-limits Understand CoinVoyage API rate limits, how to handle 429 Too Many Requests responses, and recommended retry and backoff behavior for production integrations. CoinVoyage applies rate limits to keep payment flows reliable during traffic spikes, retries, and abusive request patterns. Limits vary by endpoint and credential type. ## 429 responses When a request exceeds a limit, CoinVoyage returns `429 Too Many Requests`: ```http theme={null} HTTP/1.1 429 Too Many Requests Retry-After: ``` ```json theme={null} { "error": "Too Many Requests", "message": "Rate limit exceeded. Please try again later.", "code": 429 } ``` If `Retry-After` is present, wait at least that many seconds before retrying. ## Retry guidance * Respect `Retry-After` instead of retrying immediately. * Add jitter when multiple workers or browser tabs may retry the same operation. * Use bounded exponential backoff for network failures and `5xx` responses. * Retry reads more freely than create or mutation requests. * Keep fulfillment and reconciliation idempotent by CoinVoyage order ID. Use webhooks for payment state changes. Polling is useful for dashboard views and reconciliation jobs, but webhooks should be the primary completion signal. # CoinVoyage support: contact, examples, and resources Source: https://docs.coinvoyage.io/resources/support Contact CoinVoyage support by email, test with the example site, browse GitHub examples, or inquire about enterprise and volume pricing. Use the channel below that best matches what you need. For most integration and technical questions, emailing [help@coinvoyage.io](mailto:help@coinvoyage.io) is the fastest path to a resolution. Contact the CoinVoyage team at [help@coinvoyage.io](mailto:help@coinvoyage.io) for implementation questions, migration assistance, billing inquiries, or production issues. Manage your API keys, configure settlement currencies, view transaction history, and create invoices from the [Dashboard](https://dashboard.coinvoyage.io/). Test the end-to-end Deposit and Sale payment flows at [example.coinvoyage.io](https://example.coinvoyage.io/) before building your own integration. Connect it to your account using your API Key and Secret. Browse working integration examples in the [coin-voyage/examples](https://github.com/coin-voyage/examples) repository. You can open issues there if you find a bug in the example code or documentation flow. ## Enterprise and volume pricing If you are processing high transaction volume and want to discuss reduced fees, dedicated account management, or custom integration support, email [help@coinvoyage.io](mailto:help@coinvoyage.io) with details about your use case and monthly volume. For a full overview of standard rates and what is included at each tier, see the [Pricing](/resources/pricing) page. ## Reporting issues If you encounter a bug in the public examples or documentation, open an issue directly in the [examples repository](https://github.com/coin-voyage/examples/issues). For issues with your live integration or account, contact [help@coinvoyage.io](mailto:help@coinvoyage.io) directly. # ApiClient - server-side CoinVoyage REST API client Source: https://docs.coinvoyage.io/sdk/apiclient Initialize ApiClient from @coin-voyage/paykit/server and navigate its order, wallet, on-ramp, off-ramp, invoice, fee, webhook, and type references. `ApiClient` is the server-side interface to the CoinVoyage v3 API. Import it from `@coin-voyage/paykit/server` and use it in API routes, server actions, or backend services. Every method returns an `APIResponse` wrapper with either `data` or `error`. Keep your API secret on the server. The SDK can create `DEPOSIT` orders with the public API key, but `SALE`, refunds, list operations, off-ramp operations, invoices, webhooks, and fees require a server-generated authorization signature. ## Initialization ```typescript theme={null} import { ApiClient, ChainId, ChainType } from "@coin-voyage/paykit/server"; const apiClient = ApiClient({ apiKey: process.env.COIN_VOYAGE_API_KEY!, environment: "production", }); ``` Your organization's API key from the [CoinVoyage dashboard](https://dashboard.coinvoyage.io/developers). API environment. Accepted values are `"production"`, `"development"`, and `"local"`. | Environment | Base URL | | ------------- | ---------------------------------- | | `production` | `https://api.coinvoyage.io/v3` | | `development` | `https://acc-api.coinvoyage.io/v3` | | `local` | `http://localhost:8000/v3` | ## `APIResponse` Every `ApiClient` method returns a `Promise>`: ```typescript theme={null} type APIResponse = { data?: T error?: { path: string statusCode: number status: string message: string details?: unknown } } ``` Check `error` before using `data`: ```typescript theme={null} const { data, error } = await apiClient.getOrder("order_123"); if (error) { console.error(error.statusCode, error.message); return; } console.log(data?.status); ``` ## Reference Generate server-side signatures for privileged operations. Create orders, retrieve quotes, and generate payment instructions. Request standalone swap quotes and execution instructions. Search currencies and retrieve multi-wallet portfolios. Integrate Stripe Link and embedded card-funded on-ramp flows. Manage verification, bank accounts, and fiat payout intents. Create, publish, list, retrieve, archive, and delete invoice drafts. Retrieve and claim organization fee balances. Manage webhook subscriptions and consume real-time order events. Reference shared order, status, payment, and funding types. # ApiClient authentication and authorization signatures Source: https://docs.coinvoyage.io/sdk/apiclient/authentication Authenticate CoinVoyage ApiClient calls and generate HMAC-SHA256 signatures for privileged v3 API operations. `ApiClient` sends your API key with every request. Privileged organization operations additionally require an HMAC-SHA256 signature generated from your API secret. Generate authorization signatures only on your server. A leaked API secret can create sales, refunds, invoices, off-ramp intents, webhooks, and fee claims for your organization. ## Public API-key operations API-key operations include creating `DEPOSIT` orders, retrieving individual orders, generating quotes and payment instructions, searching currencies, scanning wallets, and using standalone swaps or eligible on-ramp flows. ## Signed operations Signed server-side operations include `SALE` orders, refunds, organization-wide listing, off-ramp management, invoices, fees, webhooks, and organization-wide WebSocket subscriptions. ## Generate a signature `generateAuthorizationSignature()` creates the HMAC-SHA256 `Authorization` header value used by signed v3 requests. Pass the v3 path without the `/v3` prefix. ```typescript theme={null} const authorization = apiClient.generateAuthorizationSignature( process.env.COIN_VOYAGE_API_SECRET!, "POST", "/orders" ); ``` The result is formatted as: ```text theme={null} APIKey=,signature=,timestamp= ``` Sign the exact method and path used by the request. Examples include `/orders`, `/orders/{order_id}/refund`, `/invoices`, `/invoices/{invoice_id}/archive`, and `/ws`. `ApiClient` methods that accept `apiSecret` generate and attach the appropriate signature automatically. Call `generateAuthorizationSignature()` directly only when building a raw request or an organization-wide WebSocket connection. # ApiClient fee methods Source: https://docs.coinvoyage.io/sdk/apiclient/fees Retrieve and claim CoinVoyage organization fee balances with signed ApiClient methods. Fee operations require your API secret and must run on the server. | Method | Purpose | | ------------------------------ | -------------------------------------------------------- | | `getFeeBalances(apiSecret)` | Retrieves claimable fee balances for your organization. | | `claimFees(params, apiSecret)` | Claims accrued fees to a recipient currency and address. | ```typescript theme={null} const { data, error } = await apiClient.getFeeBalances( process.env.COIN_VOYAGE_API_SECRET! ); ``` # ApiClient invoice methods Source: https://docs.coinvoyage.io/sdk/apiclient/invoices Create, publish, list, retrieve, archive, and delete CoinVoyage invoices and drafts with signed ApiClient methods. Invoice methods are available in `@coin-voyage/paykit@3.4.0` and later. Every method requires your API secret and must run on the server. Publishing an invoice creates a linked `SALE` order and emails the recipient a PDF with a hosted payment link. Treat `createInvoice()` as an external side effect, not a preview operation. ## createInvoice Creates and publishes an invoice. Include `id` to publish an existing draft, together with the complete invoice fields required by `CreateInvoiceParams`. ```typescript theme={null} const { data: invoice, error } = await apiClient.createInvoice( { invoice: { no: "INV-001", date: "2026-08-03", due_date: "2026-08-17", }, from: { name: "Acme Inc.", email: "billing@acme.com", }, recipient: { name: "Jane Doe", email: "jane@example.com", }, items: [ { name: "Consulting", unitPrice: "100.00", quantity: "2", tax: "10", }, ], }, process.env.COIN_VOYAGE_API_SECRET! ); console.log(invoice?.payment_url, invoice?.order_id); ``` **Parameters:** `params` (`CreateInvoiceParams`), `apiSecret`, `opts?`\ **Returns:** `Promise>` ## saveInvoiceDraft Creates a partial draft or updates an unpublished draft when `id` is supplied. Draft fields are validated when the invoice is published. ```typescript theme={null} const { data: draft, error } = await apiClient.saveInvoiceDraft( { invoice: { no: "INV-002" }, recipient: { email: "customer@example.com" }, }, process.env.COIN_VOYAGE_API_SECRET! ); const { data: updatedDraft } = await apiClient.saveInvoiceDraft( { id: draft!.id, items: [{ name: "Design work", unitPrice: "750.00", quantity: "1" }], }, process.env.COIN_VOYAGE_API_SECRET! ); ``` **Returns:** `Promise>` ## listInvoices Lists invoices and drafts newest first. Search matches invoice IDs, invoice numbers, recipient names or email addresses, and linked order IDs. ```typescript theme={null} const { data, error } = await apiClient.listInvoices( { limit: 20, offset: 0, search: "INV-001" }, process.env.COIN_VOYAGE_API_SECRET! ); console.log(data?.data, data?.pagination.total_count); ``` **Returns:** `Promise>` ## getInvoice ```typescript theme={null} const { data, error } = await apiClient.getInvoice( "invoice_123", process.env.COIN_VOYAGE_API_SECRET! ); ``` **Returns:** `Promise>` ## archiveInvoice Archives an invoice. Archiving retains the invoice and records `archived_at`. ```typescript theme={null} const { data, error } = await apiClient.archiveInvoice( "invoice_123", process.env.COIN_VOYAGE_API_SECRET! ); ``` **Returns:** `Promise>` ## deleteInvoice Deletes an unpublished draft. Published invoices cannot be deleted. ```typescript theme={null} const { error } = await apiClient.deleteInvoice( "draft_invoice_123", process.env.COIN_VOYAGE_API_SECRET! ); ``` **Returns:** `Promise>` ## Invoice types ```typescript theme={null} type CreateInvoiceParams = { id?: string invoice: InvoiceMeta from: InvoiceOrganizationDetails recipient: InvoiceRecipientDetails items: InvoiceItem[] } type SaveInvoiceDraftParams = { id?: string invoice?: Partial from?: Partial recipient?: Partial items?: InvoiceItem[] } type InvoiceItem = { id?: string name: string unitPrice: string quantity: string tax?: string image?: string } type InvoiceResponse = { id: string organization_id: string data: InvoiceData order_id?: string orders: Order[] payment_url: string published_at: string | null archived_at?: string | null created_at: string updated_at: string } ``` `unitPrice`, `quantity`, and `tax` are decimal strings. `tax` is a percentage from 0 through 100. See [Invoices overview](/invoices/overview) for the lifecycle and [Invoices API](/invoices/api) for the raw REST endpoints. # ApiClient off-ramp methods Source: https://docs.coinvoyage.io/sdk/apiclient/off-ramp Manage CoinVoyage off-ramp verification, linked bank accounts, and fiat payout intents with signed ApiClient methods. Use these signed server-side methods when CoinVoyage should coordinate fiat payout to a linked bank account. | Method | Purpose | | ---------------------------------------------- | --------------------------------------------------------- | | `createOffRampVerification(params, apiSecret)` | Creates a hosted verification link for KYC or KYB. | | `getOffRampVerificationStatus(apiSecret)` | Retrieves verification and terms-of-service status. | | `listBankAccounts(apiSecret)` | Lists linked bank accounts. | | `getBankAccount(bankAccountId, apiSecret)` | Retrieves one linked bank account. | | `addBankAccount(params, apiSecret)` | Adds a bank account for the verified organization. | | `listOffRampIntents(apiSecret)` | Lists off-ramp intents. | | `createOffRampIntent(params, apiSecret)` | Creates an off-ramp intent and returns execution details. | ```typescript theme={null} const { data, error } = await apiClient.listBankAccounts( process.env.COIN_VOYAGE_API_SECRET! ); ``` Off-ramp APIs are optional. They do not replace orders and are not required for on-chain settlement to your own wallet. # ApiClient on-ramp methods Source: https://docs.coinvoyage.io/sdk/apiclient/on-ramp Use CoinVoyage ApiClient methods for Stripe Link authentication and embedded card-funded crypto on-ramp sessions. Use these API-key methods for Stripe embedded on-ramp flows. Card payments support `"usd"` and `"eur"` as `source_currency` values for eligible organizations and orders. | Method | Purpose | | -------------------------------------------- | ----------------------------------------------------------------- | | `createLinkAuthIntent(params)` | Creates a Stripe Link auth intent from an email or hashed email. | | `createLinkAuthToken(params)` | Exchanges an auth intent for a Link auth token client secret. | | `createOnrampIntent(params)` | Creates a reusable on-ramp intent, optionally backed by an order. | | `getStripeCryptoCustomerStatus(params)` | Checks Stripe crypto KYC and identity-verification status. | | `createOnrampSession(params)` | Creates a headless Stripe on-ramp session from an intent. | | `checkoutStripeOnrampSession(sessionId)` | Returns the embedded checkout client secret. | | `refreshStripeOnrampSessionQuote(sessionId)` | Refreshes the quote attached to a session. | ## EUR-funded example ```typescript theme={null} const { data, error } = await apiClient.createOnrampIntent({ source_amount: "100.00", source_currency: "eur", destination_currency: { chain_id: ChainId.ETH, address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", }, wallet_address: "0xRecipientWallet", }); ``` On-ramp access is gated. Your organization must be approved before showing card payments to users. # ApiClient order methods Source: https://docs.coinvoyage.io/sdk/apiclient/orders Create CoinVoyage deposit, sale, and refund orders; list and retrieve orders; quote payments; and generate payment instructions. Order methods cover the complete server-side payment lifecycle. See [Core types](/sdk/apiclient/types) for the request and response shapes used below. ## createDepositOrder Creates a `DEPOSIT` order for a direct on-chain deposit to a recipient address. ```typescript theme={null} import { ApiClient, ChainId } from "@coin-voyage/paykit/server"; const apiClient = ApiClient({ apiKey: process.env.COIN_VOYAGE_API_KEY! }); const { data, error } = await apiClient.createDepositOrder({ amount: "10", currency: { chain_id: ChainId.SUI, address: null }, recipient: "0xYourReceivingAddressHere", metadata: { items: [{ name: "Wallet top-up" }] }, }); ``` **Parameters:** `params` (`OrderParams`), `opts?`\ **Returns:** `Promise>` ## createSaleOrder Creates a `SALE` order for a merchant checkout. It requires your API secret and must run on the server. ```typescript theme={null} const { data, error } = await apiClient.createSaleOrder( { amount: "49.99", fiat_unit: "USD", metadata: { order_id: "order_123", customer_id: "customer_456" }, }, process.env.COIN_VOYAGE_API_SECRET! ); ``` If `currency` and `recipient` are omitted, CoinVoyage settles to your dashboard settlement configuration. **Parameters:** `params` (`OrderParams`), `apiSecret` (`string`), `opts?`\ **Returns:** `Promise>` ## createRefundOrder Creates a `REFUND` order against an existing completed order. ```typescript theme={null} const { data, error } = await apiClient.createRefundOrder( "original-order-id", { amount: "25.00", fiat_unit: "USD", currency: { chain_id: ChainId.ETH, address: null }, recipient: "0xRefundAddress", metadata: { refund: { reason: "Partial refund", refund_amount: 25, currency: "USD" }, }, }, process.env.COIN_VOYAGE_API_SECRET! ); ``` **Parameters:** `orderId`, `params` (`RefundRequest`), `apiSecret`, `opts?`\ **Returns:** `Promise>` ## getOrder ```typescript theme={null} const { data, error } = await apiClient.getOrder("order_123"); ``` **Returns:** `Promise>` ## listOrders Lists orders for your organization. This is a signed server-side method. ```typescript theme={null} const { data, error } = await apiClient.listOrders( { limit: 50, offset: 0 }, process.env.COIN_VOYAGE_API_SECRET! ); ``` **Returns:** `Promise>` ## getOrderPaymentMethods ```typescript theme={null} const { data, error } = await apiClient.getOrderPaymentMethods("order_123"); ``` **Returns:** `Promise>` ## orderQuotes Generates ranked quote options for paying an order from a wallet or explicit source list. ```typescript theme={null} const { data, error } = await apiClient.orderQuotes("order_123", { filter: { wallet: { address: "0xPayerWallet", chain_type: ChainType.EVM, chain_ids: [ChainId.ETH, ChainId.BASE], }, min_balance_usd: 1, limit: 10, }, }); ``` **Returns:** `Promise>` ## createPayment Creates payment instructions for an order. Destination and amount come from the order; the request supplies the funding source. ```typescript theme={null} const { data, error } = await apiClient.createPayment("order_123", { source: { payment_rail: "CRYPTO", currency: { chain_id: ChainId.ETH, address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", }, slippage_bps: 50, }, refund_address: "0xRefundAddress", }); ``` **Returns:** `Promise>` ## x402Complete Completes an x402 payment. If `paymentSignature` is omitted, the API returns a `402` response with a `PAYMENT-REQUIRED` header. ```typescript theme={null} const { data, error } = await apiClient.x402Complete( "order_123", paymentSignature ); ``` **Returns:** `Promise>` ## Recover a Pay to Address partial payment When a Pay to Address payment reaches `PARTIAL_PAYMENT`, keep the order unfulfilled and inspect `order.payment.funding.remaining_amount`. The payer can send one or more additional deposits through the same Pay to Address flow. CoinVoyage records every deposit in `funding.transactions` and completes the order after the required amount has been received. ```typescript theme={null} const { data: order } = await apiClient.getOrder("order_123"); if (order?.status === "PARTIAL_PAYMENT") { const remaining = order.payment?.funding?.remaining_amount.ui; console.log(`Still due: ${remaining}`); } ``` Additional-deposit recovery is available only for Pay to Address. Do not assume wallet, card, or other payment methods can resume from `PARTIAL_PAYMENT`. # Swap API — quote and execute standalone swaps Source: https://docs.coinvoyage.io/sdk/apiclient/swaps Use CoinVoyage ApiClient to quote and execute standalone cross-chain and same-chain swaps from your server. Use the Swap API when you need to build a custom or headless swap experience instead of embedding the [`@coin-voyage/swapkit`](/sdk/swapkit) React widget. Standalone swaps are separate from PayKit's SALE and DEPOSIT payment flows. The server-side swap methods are currently exposed through `ApiClient` from `@coin-voyage/paykit/server`. Initialize the client with your CoinVoyage API key before requesting a quote: ```typescript theme={null} import { ApiClient, ChainId } from "@coin-voyage/paykit/server"; const apiClient = ApiClient({ apiKey: process.env.COIN_VOYAGE_API_KEY!, environment: "production", }); ``` ## swapQuote Gets a standalone swap quote for one source and destination pair. ```typescript theme={null} const { data, error } = await apiClient.swapQuote({ trade_type: "EXACT_INPUT", amount: "100", source: { chain_id: ChainId.BASE, address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", }, destination: { chain_id: ChainId.SUI, address: null }, recipient: "0xDestinationAddress", slippage_bps: 50, }); ``` ## swapExecute Creates payment instructions for a standalone swap. ```typescript theme={null} const { data, error } = await apiClient.swapExecute({ trade_type: "EXACT_INPUT", amount: "100", source: { currency: { chain_id: ChainId.BASE, address: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", }, sender_address: "0xSenderWallet", slippage_bps: 50, }, destination: { currency: { chain_id: ChainId.SUI, address: null }, recipient: "0xDestinationAddress", }, }); ``` # ApiClient core types Source: https://docs.coinvoyage.io/sdk/apiclient/types Reference the core CoinVoyage order, status, payment, and funding types returned by ApiClient. ## OrderParams ```typescript theme={null} type OrderParams = { amount: string fiat_unit?: "USD" | "EUR" | string | null currency?: { chain_id: ChainId address?: string | null } | null recipient?: string | null metadata?: OrderMetadata | null } ``` For token-denominated orders, omit `fiat_unit`. For fiat-denominated orders, set it to the fiat currency code. ## Order ```typescript theme={null} type Order = { id: string organization_id: string mode: OrderMode status: OrderStatus fulfillment: OrderFulfillment payment?: PaymentData | null metadata?: OrderMetadata hosted_url: string created_at: string updated_at: string } ``` ## OrderMode | Value | Description | | ----------- | ------------------------------------------------------------------------------------------------- | | `SALE` | Merchant sale. Settles to dashboard configuration unless `currency` and `recipient` are provided. | | `DEPOSIT` | Direct deposit to a specified recipient address. | | `REFUND` | Refund order created through `createRefundOrder()`. | | `SWAP` | Internal order created by standalone swap execution. | | `FEE_CLAIM` | Internal order created by fee claiming. | ## OrderStatus | Status | Description | | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `PENDING` | Created but not yet ready for payment. | | `AWAITING_PAYMENT` | Ready and waiting for the user to send payment. | | `AWAITING_CONFIRMATION` | Payment detected and waiting for blockchain confirmation. | | `OPTIMISTIC_CONFIRMED` | Optimistically confirmed; execution can begin. | | `EXECUTING_ORDER` | Payment is being routed to the destination. | | `COMPLETED` | Completed successfully. | | `EXPIRED` | Expired before payment was received. | | `REFUNDED` | Refunded to the configured refund address. | | `FAILED` | Failed during processing. | | `PARTIAL_PAYMENT` | Less than the required amount was received. Pay to Address can accept additional deposits; other payment methods require resolution. | ## PaymentData ```typescript theme={null} type PaymentData = { payment_rail: "CRYPTO" | "FIAT" input: QuoteInput output: QuoteOutput funding?: PaymentFunding | null recipient: string refund?: { address?: string | null tx_hash?: string | null reason?: string | null } | null source_tx_hash?: string | null destination_tx_hash?: string | null fee_tx_hash?: string | null steps: PaymentStep[] expires_at: string } ``` ## PaymentFunding `funding` tracks cumulative progress when one or more deposits fund a payment. ```typescript theme={null} type PaymentFunding = { required_amount: CurrencyAmount received_amount: CurrencyAmount remaining_amount: CurrencyAmount transactions: Array<{ tx_hash: string amount: CurrencyAmount }> } ``` For a Pay to Address partial payment, use `remaining_amount` to show what is still due and `transactions` to reconcile every deposit. # ApiClient wallet and currency search methods Source: https://docs.coinvoyage.io/sdk/apiclient/wallets-and-search Search CoinVoyage currencies and retrieve balances for up to ten wallets with ApiClient. ## currencySearch Searches supported currencies for token pickers and back-office tools. ```typescript theme={null} const { data, error } = await apiClient.currencySearch({ q: "usdc", chain_ids: [ChainId.SUI, ChainId.SOL], limit: 10, }); ``` ## portfolio Retrieves token balances and USD values for 1–10 wallets. Successful scans appear in `data.portfolio`; wallet-specific failures appear in `data.errors` without discarding other results. ```typescript theme={null} const { data, error } = await apiClient.portfolio({ wallets: [ { address: "0xFirstPayerWallet", chain_type: ChainType.EVM, chain_ids: [ChainId.ETH, ChainId.BASE], }, { address: "0xSecondPayerWallet", chain_type: ChainType.EVM, chain_ids: [4663], // Robinhood Chain }, ], filter: { min_balance_usd: 1 }, force_refresh: false, }); if (error) throw new Error(error.message); for (const result of data?.portfolio ?? []) { console.log(result.wallet.address, result.currencies); } for (const walletError of data?.errors ?? []) { console.warn(walletError.wallet.address, walletError.code); } ``` `wallets` is required and accepts between 1 and 10 entries. Set `force_refresh: true` when you need a fresh scan instead of a cached result. Migrating from 3.2: replace `wallet: { ... }` with `wallets: [{ ... }]`. The response is grouped into `portfolio` and `errors` arrays, even for one wallet. # ApiClient webhooks and WebSocket subscriptions Source: https://docs.coinvoyage.io/sdk/apiclient/webhooks-and-websockets Manage CoinVoyage webhook subscriptions and listen for real-time order events with ApiClient. ## Webhook methods All webhook-management methods are signed server-side operations. | Method | Purpose | | --------------------------------------------- | ------------------------------- | | `listWebhooks(apiSecret)` | Lists webhook subscriptions. | | `createWebhook(params, apiSecret)` | Creates a webhook subscription. | | `updateWebhook(webhookId, params, apiSecret)` | Updates a webhook subscription. | | `deleteWebhook(webhookId, apiSecret)` | Deletes a webhook subscription. | ## Order-scoped WebSocket subscriptions `subscribeOrderStatus()` opens a WebSocket connection for real-time order events. Order-scoped subscriptions authenticate with your API key. ```typescript theme={null} const socket = apiClient.subscribeOrderStatus(); socket.onOpen(() => { socket.subscribe("order_123"); }); socket.onMessage((msg) => { if (msg.type === "event") { console.log(msg.data.event, msg.data.order.status); } }); ``` ## Organization-wide subscriptions Organization-wide subscriptions require an authorization signature for `GET /ws`. ```typescript theme={null} const authorizationSignature = apiClient.generateAuthorizationSignature( process.env.COIN_VOYAGE_API_SECRET!, "GET", "/ws" ); const socket = apiClient.subscribeOrderStatus({ authorizationSignature }); socket.onOpen(() => { socket.subscribeOrg(); }); ``` See the [webhook event reference](/webhooks/events) for event identifiers and payloads. # CoinVoyage PayKit SDK — SALE and DEPOSIT payments Source: https://docs.coinvoyage.io/sdk/overview Install @coin-voyage/paykit to add SALE and DEPOSIT payment flows with PayKitProvider, PayButton, ApiClient, and status hooks. Use `@coin-voyage/paykit` when your application needs to create and fulfill CoinVoyage payment orders: * **SALE** — accept a customer payment and settle the funds to your configured destination. * **DEPOSIT** — let a user fund a specified wallet or account. PayKit provides React context providers that manage wallet connections and order state, a ready-made button component with a built-in payment modal, and a server-side API client that abstracts authenticated endpoints. Reach for the UI components for the fastest path to production, or use `ApiClient` when you need full control over the payment flow. To add standalone token swaps to your application, use [`@coin-voyage/swapkit`](/sdk/swapkit) instead. ## Installation Install the SDK alongside its peer dependency `@tanstack/react-query`: ```bash npm theme={null} npm i @coin-voyage/paykit @tanstack/react-query@^5.90.6 ``` ```bash pnpm theme={null} pnpm add @coin-voyage/paykit @tanstack/react-query@^5.90.6 ``` ```bash yarn theme={null} yarn add @coin-voyage/paykit @tanstack/react-query@^5.90.6 ``` ```bash bun theme={null} bun add @coin-voyage/paykit @tanstack/react-query@^5.90.6 ``` ## What's in the package The SDK exports several building blocks. Here is what each one does and when you use it. ### PayKitProvider `PayKitProvider` is a React context provider that wraps your application and tracks the state of the order flow. It requires your API key and optionally accepts environment, visual mode, custom theme, and wallet callback configuration. Any component tree inside `PayKitProvider` can render `PayButton` components and call `usePayStatus`. [Read the PayKitProvider reference →](/sdk/paykitprovider) ### WalletProvider `WalletProvider` sits outside `PayKitProvider` and manages wallet connections for EVM, Solana, Sui, and UTXO chains. It accepts a `config` prop that lets you supply RPC URLs, wallet adapters, and connector settings per chain type. Both providers are required to use the `PayButton` component. [Read the WalletProvider reference →](/sdk/walletprovider) ### PayButton `PayButton` is a pre-styled button that opens a payment modal when clicked. You configure it with the destination chain, token, amount, and address (or a server-generated `orderId`), and it handles the entire payment UX including wallet selection, transaction signing, and status tracking. A `PayButton.Custom` variant gives you a render prop pattern to use your own button element while keeping the modal logic. [Read the PayButton reference →](/sdk/paybutton) ### Fiat on-ramp with Stripe Link (gated) Approved organizations can offer a USD- or EUR-funded on-ramp option inside the same PayKit modal. The option appears alongside the crypto-first paths users already see, such as **Pay to Address** and **Pay with Wallet**. This flow uses Stripe Link and Stripe's [embedded crypto on-ramp](https://docs.stripe.com/crypto/onramp/embedded-components-integration-guide?platform=web). The user authenticates with Link, completes any required identity checks, selects an eligible payment method, and CoinVoyage coordinates the on-ramp session so the order can be funded without the user starting from a pre-funded crypto wallet. On-ramp access is currently gated. Contact the CoinVoyage team before enabling `options.experimentalFeatures.cardPayments`; your organization must be approved before the option is shown to users. ### ApiClient `ApiClient` is a server-side helper imported from `@coin-voyage/paykit/server`. It wraps CoinVoyage REST endpoints for orders, invoices, quotes, swaps, off-ramp verification, bank accounts, off-ramp intents, webhooks, and more behind a typed interface that returns consistent `APIResponse` objects. Use it in API routes or server actions for operations that require server-held credentials. [Read the ApiClient reference →](/sdk/apiclient) ### PayKit Headless (Preview) `@coin-voyage/paykit-headless` is a separate Preview package for agent and server runtimes that need to satisfy x402 `PAYMENT-REQUIRED` challenges without browser wallet UI. Use it only for x402 flows; regular checkout and deposit integrations should use `@coin-voyage/paykit`. [Read the PayKit Headless reference →](/sdk/paykit-headless) ### usePayStatus `usePayStatus` is a React hook that subscribes to real-time status updates for an order from within any component inside `PayKitProvider`. Use it when you need to react to payment lifecycle changes — for example, to update UI state or trigger server-side fulfillment logic — independently of the `PayButton` callbacks. `usePayStatus` requires both `WalletProvider` and `PayKitProvider` to be present in the component tree. ### useOrderStatusWS `useOrderStatusWS` is a React hook that subscribes to backend order WebSocket events with reconnect handling. Use it for custom payment status panels or dashboard-style views that need live order events outside the default PayButton UI. [Read the useOrderStatusWS reference →](/sdk/useorderstatusws) ## Recommended setup A typical provider tree looks like this: ```tsx theme={null} "use client"; import { PayKitProvider, WalletProvider } from "@coin-voyage/paykit"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; const queryClient = new QueryClient(); export function Providers({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` Place this `Providers` component at the root of your application so every page has access to the payment context. # PayButton — ready-made crypto payment UI component Source: https://docs.coinvoyage.io/sdk/paybutton Render a pre-styled payment button that opens a CoinVoyage modal for wallet selection, transaction signing, and real-time status tracking. `PayButton` is a ready-made React component that renders a themed button and manages the full payment modal experience. When a user clicks it, a modal opens that lets them select a payment method, connect their wallet, and sign the transaction. The button handles quote fetching, transaction submission, and status polling internally — your application only needs to supply the payment parameters and react to the lifecycle callbacks. ## Full example ```tsx theme={null} import { PayButton } from "@coin-voyage/paykit"; import { PaymentMethod } from "@coin-voyage/types"; import { ChainId } from "@coin-voyage/paykit/server"; { console.log("Modal Opened"); }} onClose={() => { console.log("Modal Closed"); }} closeOnSuccess={true} paymentMethod={PaymentMethod.WALLET} onPaymentCreationError={(event) => { console.log(event.errorMessage); }} onAwaitingPayment={(event) => { console.log("Awaiting payment", event); }} onConfirmingPayment={(event) => { console.log("Payment confirming", event); }} onExecutingPayment={(event) => { console.log("Payment executing", event); }} onPaymentCompleted={() => { console.log("Payment Complete"); }} onPaymentBounced={() => { console.error("Payment Bounced"); }} /> ``` **Required parameters:** You must provide either `orderId` **or** all three of `toAddress`, `toChain`, and `toAmount`. Mixing both approaches in the same button is not supported. * Use `orderId` for server-generated `SALE` orders created via `ApiClient.createSaleOrder`. * Use `toAddress` + `toChain` + `toAmount` for client-side deposit flows. ## Props reference ### Payment target An order ID previously created on the server via the `ApiClient`. Use this for `SALE` orders or any flow where the server controls order creation. When `orderId` is set, omit `toAddress`, `toChain`, and `toAmount`. The destination chain ID to deposit to. Required when not using `orderId`. The contract address of the destination token (ERC-20, SPL, or equivalent). Omit this prop (or pass `undefined`) to receive the chain's native token (ETH, SOL, SUI, etc.). The amount of the destination token the recipient should receive, in human-readable format (e.g., `100` for 100 USDC). Required when not using `orderId`. The recipient address on the `toChain`. Must be a valid address for the destination chain. Required when not using `orderId`. Metadata to attach to the order. Supports structured item details (name, description, image, quantity, price) and custom key-value fields. The verb shown on the button label, such as `"Pay"`, `"Deposit"`, or `"Purchase"`. Defaults to `"Pay"` when not set. ### Appearance Inline style overrides applied to the button element. Use this for quick branding adjustments like background color and text color. Color scheme for the payment modal. Accepted values: `"light"`, `"dark"`, or `"auto"`. Overrides the `mode` set on `PayKitProvider` for this button only. Custom theme object for this button's modal. Takes precedence over any `customTheme` set on `PayKitProvider`. When `true`, the button is rendered in a disabled state and the modal cannot be opened. When `true`, the payment modal opens automatically on mount without requiring a click. Opens the modal directly on a specific payment path instead of first showing method selection. Accepted values are `PaymentMethod.WALLET`, `PaymentMethod.DEPOSIT_ADDRESS`, and `PaymentMethod.CARD`. ### Modal lifecycle When `true`, the payment modal closes automatically after the payment completes successfully. When `true`, PayKit resets the payment state after a successful payment once the modal closes. Callback invoked when the payment modal opens. Callback invoked when the payment modal closes, whether by the user or programmatically. ### Payment lifecycle Callback invoked when the order cannot be created due to invalid parameters. Receives an event object with an `errorMessage` string describing the failure. Callback invoked when payment details are available and the order is awaiting payment. Callback invoked when the user's payment transaction is detected on-chain and awaiting confirmation. Callback invoked when the payment is confirmed and CoinVoyage is executing the destination transfer or contract call. Callback invoked when the destination transfer or contract call completes successfully. This is the appropriate place to trigger fulfillment logic or show a success message. Callback invoked when the destination call reverts and the user's funds are automatically refunded. Use this to notify the user of the failure. *** ## Open a specific payment method Use `paymentMethod` when your UI already knows which payment path the user selected. ```tsx theme={null} import { PayButton } from "@coin-voyage/paykit"; import { PaymentMethod } from "@coin-voyage/types"; ``` `PaymentMethod.CARD` supports eligible USD- and EUR-denominated orders. It still requires `options.experimentalFeatures.cardPayments` and organization approval. If card payments are not available for the order, PayKit falls back to method selection instead of opening the card flow. *** ## PayButton.Custom `PayButton.Custom` is for situations where you need complete control over the button's appearance — for example, when integrating with an existing design system or triggering the modal from multiple UI elements. It accepts a render prop as `children` that receives `show` and `hide` functions to control modal visibility. `PayButton.Custom` accepts the same payment props, modal options, and lifecycle callbacks as `PayButton` but replaces the styling props (`style`, `mode`, `customTheme`, `disabled`) with the `children` render prop. ### Usage ```tsx theme={null} import { PayButton } from "@coin-voyage/paykit"; { console.log("Awaiting payment", event); }} onConfirmingPayment={(event) => { console.log("Payment confirming", event); }} onExecutingPayment={(event) => { console.log("Payment executing", event); }} onPaymentCompleted={(event) => { console.log("Payment completed", event); }} > {({ show, hide }) => ( )} ``` ### Render prop The `children` function receives an object with two functions: Opens the payment modal. Call this from your custom button's `onClick` handler or any other event. Closes the payment modal programmatically. Useful when you need to dismiss the modal in response to an external event. ### Additional PayButton.Custom props A render function that receives `{ show, hide }`. Must return valid React elements. When `true`, the payment modal opens automatically on mount. **When to use `PayButton.Custom`:** * You need full styling control beyond what inline `style` provides. * You are integrating into an existing design system with strict component conventions. * You want to trigger the payment modal from multiple distinct UI elements on the same page. * You need to open or close the modal in response to programmatic events, not only user clicks. # PayKit Headless (Preview) - x402 payments for agents and servers Source: https://docs.coinvoyage.io/sdk/paykit-headless Use the Preview @coin-voyage/paykit-headless package to satisfy CoinVoyage x402 PAYMENT-REQUIRED challenges without browser wallet UI. `@coin-voyage/paykit-headless` is a Preview package that provides server and agent runtime helpers for CoinVoyage x402 payments. Use it when your application needs to fetch a protected resource, handle an x402 `PAYMENT-REQUIRED` challenge, sign the selected payment requirement, and retry the request without showing the PayKit browser modal. x402 support is in Preview. Supported chains, payload details, and helper APIs may change before general availability. The headless package is separate from `@coin-voyage/paykit`. Install it only for agent, server, or automation flows that need x402 payment signing. ## Installation ```bash npm theme={null} npm i @coin-voyage/paykit-headless ``` ```bash pnpm theme={null} pnpm add @coin-voyage/paykit-headless ``` ```bash yarn theme={null} yarn add @coin-voyage/paykit-headless ``` ```bash bun theme={null} bun add @coin-voyage/paykit-headless ``` ## Exports ```typescript theme={null} import { readResponseBody } from "@coin-voyage/paykit-headless"; import { createSuiX402PaymentSignatureHeader, decodeX402PaymentRequiredHeader, executeX402AgentPayment, type X402AgentPaymentRequest, } from "@coin-voyage/paykit-headless/x402"; ``` Use the package entry points shown above. Do not import internal files such as `x402/agent` or `x402/sui`. ## Agent payment flow Use `executeX402AgentPayment()` when an agent should fetch a `PAYMENT-REQUIRED` challenge, create a `PAYMENT-SIGNATURE`, and retry the protected resource. ```typescript theme={null} import { executeX402AgentPayment } from "@coin-voyage/paykit-headless/x402"; const coinType = "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"; const privateKey = process.env.AGENT_SUI_PRIVATE_KEY; if (!privateKey) { throw new Error("AGENT_SUI_PRIVATE_KEY is required"); } const url = new URL("https://example.com/api/agent/payment-required"); url.searchParams.set("preferred_chain_type", "SUI"); url.searchParams.set("preferred_chain_id", "30000000000002"); url.searchParams.set("preferred_token_address", coinType); const result = await executeX402AgentPayment({ url: url.toString(), chainType: "SUI", network: "sui:mainnet", asset: coinType, maxAmount: "20000", privateKey, }); if (!result.ok) { throw new Error(result.message); } ``` The package does not read private keys or RPC URLs from environment variables. Your application is responsible for loading the correct key and passing it to the helper. ## Supported key types | `chainType` | Expected key | | ----------- | -------------------------------------------------------- | | `EVM` | EVM private key for `eip155:*` payment requirements | | `SOL` | Solana private key for `solana:*` payment requirements | | `SUI` | Sui Ed25519 private key for `sui:*` payment requirements | The agent filters returned `accepts[]` entries by `chainType`, then by `network`, `asset`, `paymentIdentifier`, and `maxAmount` when those fields are provided. ## Payment requirement preferences `X402RequirementRequest` is optional. If you send preference fields, include both `preferred_chain_type` and `preferred_token_address`. ```json theme={null} { "preferred_chain_type": "SUI", "preferred_chain_id": 30000000000002, "preferred_token_address": "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC" } ``` Do not request only `preferred_chain_type`. If you do not need a tailored token response, omit all preference fields and choose from the returned `accepts[]`. ## Existing PAYMENT-REQUIRED header If you already fetched the challenge, pass the header directly: The examples below assume `paymentRequiredHeader`, `coinType`, and `privateKey` are already defined. ```typescript theme={null} const result = await executeX402AgentPayment({ url: "https://example.com/api/agent/payment-required", paymentRequiredHeader, chainType: "SUI", network: "sui:mainnet", asset: coinType, maxAmount: "20000", privateKey, }); ``` For Sui-only integrations that need only the signature header: ```typescript theme={null} import { createSuiX402PaymentSignatureHeader } from "@coin-voyage/paykit-headless/x402"; const { paymentSignature } = await createSuiX402PaymentSignatureHeader( paymentRequiredHeader, { network: "sui:mainnet", asset: coinType, maxAmount: "20000", privateKey, rpcUrl: process.env.SUI_RPC_URL, } ); ``` ## Sui payment signatures Sui x402 support uses a CoinVoyage-specific payload carried in the standard base64 `PAYMENT-SIGNATURE` header. The SDK: * Selects the returned `sui:mainnet` `accepts[]` entry without changing `scheme`, `network`, `asset`, `amount`, `payTo`, or `paymentIdentifier`. * Builds a Sui programmable transaction block when the backend does not provide transaction bytes. * Transfers the exact raw `amount` of the exact Sui coin type to `payTo`. * Signs the transaction with the configured Sui Ed25519 key. * Encodes the signed transaction bytes, signature, payer address, selected accept, and CoinVoyage Sui payload fields into `PAYMENT-SIGNATURE`. If the server provides prepared Sui transaction bytes in `accept.transactionBytes`, `accept.txBytes`, `accept.extra.sui.transactionBytes`, or `accept.extra.sui.txBytes`, the SDK signs those bytes instead of constructing a transfer transaction. ## Current limits * Native ETH and native SOL accepts are not supported by the headless agent. Use token accepts such as USDC. * Sui accepts require this SDK or another client that implements CoinVoyage's Sui `PAYMENT-SIGNATURE` payload. * `maxAmount` is compared against the raw token amount, not display units. # PayKitProvider — set up your global payment context Source: https://docs.coinvoyage.io/sdk/paykitprovider Configure PayKitProvider with your API key, environment, visual mode, wallet callbacks, and global PayKitOptions to control the payment modal. `PayKitProvider` is the top-level React context provider for the CoinVoyage PayKit SDK. Wrapping your application with it enables the `PayButton` component and the `usePayStatus` hook anywhere in the component tree. It connects to the CoinVoyage API using your `apiKey`, tracks order state, and exposes global configuration that applies to every payment flow on the page. ## Setup Place `PayKitProvider` inside `WalletProvider` and `QueryClientProvider`. This nesting order is required. ```tsx theme={null} "use client"; import { PayKitProvider, WalletProvider } from "@coin-voyage/paykit"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; const queryClient = new QueryClient(); export function Providers({ children }: { children: React.ReactNode }) { return ( { console.log( `Connected to ${chainId} with ${connectorId} (${type}) at ${address}` ); }} environment="production" > {children} ); } ``` Set `debugMode={true}` while you are integrating to log detailed information about order lifecycle events to the browser console. Remove it before going to production. ## Configuration options Your organization's API key, obtained from the **Developers** tab of the [CoinVoyage dashboard](https://dashboard.coinvoyage.io/developers). This key is safe to expose in client-side code — it identifies your organization but does not grant write access without a corresponding API secret. Environment to connect to. Accepted values: * `"production"` — connects to live chains and production APIs. * `"development"` — connects to the acceptance API. * `"local"` — connects to a locally running API at `http://localhost:8000/v3`. Color scheme for the payment modal. Accepted values: `"light"`, `"dark"`, or `"auto"`. When set to `"auto"`, the modal follows the user's OS preference. Override specific modal styles to match your branding. Accepts a `CustomTheme` object keyed by supported CSS custom properties. See [Styling / Theme](/sdk/styling/paykit) for an example and common properties. When `true`, the SDK logs debug information to the browser console. Useful during development and integration testing. Callback invoked when a wallet connects. Receives an object with: * `address` (string) — the connected wallet address * `chainId` (number) — the chain the wallet connected to * `connectorId` (string) — identifier for the connector used * `type` (string) — wallet type (e.g., EVM, Solana) Custom validation function run when a wallet connects. Use this to enforce additional requirements — for example, to block connections from addresses on a deny list — before the connection is accepted by the SDK. Callback invoked when a wallet disconnects. Global options applied across all `PayButton` components and payment flows managed by this provider. See [PayKitOptions](#paykitoptions) below. ## PayKitOptions The `options` prop accepts a `PayKitOptions` object that controls language, UI visibility, wallet-connect presentation, polyfills, confirmation behavior, and experimental features. Every field is optional. ```typescript theme={null} type PayKitOptions = { language?: Languages hideTooltips?: boolean hideQuestionMarkCTA?: boolean hideNoWalletCTA?: boolean hideRecentBadge?: boolean walletConnectCTA?: "link" | "modal" | "both" disclaimer?: ReactNode | string bufferPolyfill?: boolean overlayBlur?: number optimisticConfirmation?: boolean experimentalFeatures?: { cardPayments?: boolean } } ``` Sets the display language for the payment modal. When `true`, suppresses tooltip overlays throughout the modal. When `true`, hides the question mark help CTA inside the modal. When `true`, removes the "no wallet" call-to-action that appears when no wallet is connected. When `true`, hides the "Recent" badge shown next to recently used wallets. Controls how WalletConnect is presented. Accepted values: * `"link"` — show only a deep link * `"modal"` — show only the QR code modal * `"both"` — show both options Adds a disclaimer message to the bottom of the payment modal. Accepts a plain string or a React node for richer formatting. Enables the Node.js `Buffer` polyfill for bundlers that do not provide Node polyfills by default (common in Vite and other non-Next.js setups). Defaults to `true`. Amount of background blur (in pixels) applied while the payment modal is open. Set to `0` to disable blur. When `true`, the order is considered confirmed as soon as the user's transaction validates on-chain, before the destination transfer completes. This is not permitted for deposit orders, where confirmation requires destination finality. Opt in to the gated Stripe Link on-ramp option inside the modal. When your organization is approved, PayKit can show USD and EUR Card Payments as an additional payment path alongside **Pay to Address** and **Pay with Wallet**. CoinVoyage coordinates Link authentication, customer status checks, payment method collection, on-ramp session creation, and checkout. Contact the CoinVoyage team before enabling this option in production. ## Example with options ```tsx theme={null} { console.log(`Wallet ${address} connected on chain ${chainId}`); }} onDisconnect={() => { console.log("Wallet disconnected"); }} > {children} ``` # PayKit styling and theming Source: https://docs.coinvoyage.io/sdk/styling/paykit Customize the PayKit payment modal with colors, button styles, and light or dark themes. Use `mode` to choose the modal's base color scheme and `customTheme` to match your branding. Set `mode="light"` or `mode="dark"` for a fixed appearance, or use `mode="auto"` (the default) to follow the user's OS preference. Pass CSS custom properties directly to `customTheme`. All properties are optional and accept CSS values as strings, including units where needed (for example, `"12px"`). Properties you omit keep their built-in styles for the selected mode. In the [provider setup](/sdk/paykitprovider#setup), add your theme to `PayKitProvider`: ```tsx theme={null} {children} ``` ## Common theme properties | Area | CSS custom properties | | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Modal surfaces | `--ck-body-background`, `--ck-body-background-secondary`, `--ck-body-background-tertiary` | | Text | `--ck-body-color`, `--ck-body-color-muted`, `--ck-body-color-muted-hover` | | Dividers | `--ck-body-divider`, `--ck-body-divider-secondary` | | Primary buttons | `--ck-primary-button-background`, `--ck-primary-button-color`, `--ck-primary-button-border-radius`, `--ck-primary-button-font-weight`, `--ck-primary-button-box-shadow` | | Primary button hover | `--ck-primary-button-hover-background`, `--ck-primary-button-hover-box-shadow` | | Secondary buttons | `--ck-secondary-button-background`, `--ck-secondary-button-color`, `--ck-secondary-button-border-radius`, `--ck-secondary-button-font-weight`, `--ck-secondary-button-box-shadow` | | Secondary button hover | `--ck-secondary-button-hover-background` | | Overlay and shadow | `--ck-overlay-background`, `--ck-modal-box-shadow` | | Status and focus | `--ck-body-color-danger`, `--ck-body-color-valid`, `--ck-focus-color`, `--ck-spinner-color` | | Tooltips | `--ck-tooltip-background`, `--ck-tooltip-color`, `--ck-tooltip-shadow` | | Disclaimer | `--ck-body-disclaimer-background`, `--ck-body-disclaimer-color`, `--ck-body-disclaimer-link-color`, `--ck-body-disclaimer-link-hover-color` | | QR code | `--ck-qr-dot-color`, `--ck-qr-border-color` | Use `options.overlayBlur` to control the background blur in pixels separately from the overlay color. ## Light and dark themes Custom values override the base theme in both light and dark modes. With `mode="auto"`, only properties you leave unset change with the OS preference. If you override backgrounds and text colors, choose values that work together in both modes, or pass a different `customTheme` when your application's theme changes and set `mode` to match. # SwapKit styling and theming Source: https://docs.coinvoyage.io/sdk/styling/swapkit Customize the SwapKit widget with colors, typography, rounded corners, and light or dark themes. Use `mode` on `SwapKitProvider` to choose the widget's base color scheme and `theme` to match your branding. The `theme` prop accepts a `Partial` object: every property is optional and takes a CSS value as a string. SwapKit uses named properties such as `accent` and `radius`. [PayKit](/sdk/styling/paykit) uses `--ck-*` CSS custom properties through its `customTheme` prop. ## Apply a custom theme After [installing SwapKit](/sdk/swapkit#installation), pass your theme to the provider: ```tsx theme={null} "use client"; import { Swap, SwapKitProvider, WalletProvider, type SwapKitTheme, } from "@coin-voyage/swapkit"; const theme: Partial = { fontFamily: "Arial, Helvetica, sans-serif", accent: "#d6296f", accentHover: "#b8225f", accentText: "#ffffff", radius: "16px", }; export function BrandedSwap() { return ( ); } ``` ## Theme properties SwapKit maps these properties to CSS custom properties inside the widget. Use the property names in the first column when passing `theme`. | Property | CSS custom property | Controls | | ----------------- | ----------------------- | -------------------------------------------------------------------------------------- | | `fontFamily` | `--sk-font-family` | Font family for the widget and its controls. Load any custom font in your application. | | `background` | `--sk-background` | Widget background. | | `surface` | `--sk-surface` | Surface background. | | `surfaceElevated` | `--sk-surface-elevated` | Elevated surface background. | | `border` | `--sk-border` | Border color. | | `text` | `--sk-text` | Main text color. | | `textMuted` | `--sk-text-muted` | Secondary text color. | | `accent` | `--sk-accent` | Accent color for actions and focus outlines. | | `accentHover` | `--sk-accent-hover` | Accent color on hover. | | `accentText` | `--sk-accent-text` | Text color on accent backgrounds. | | `danger` | `--sk-danger` | Error color. | | `warning` | `--sk-warning` | Warning color. | | `success` | `--sk-success` | Success color. | | `overlay` | `--sk-overlay` | Overlay background. | | `shadow` | `--sk-shadow` | Box shadow. | | `radius` | `--sk-radius` | Base corner radius, including a CSS unit such as `"16px"`. | ## Light and dark themes Set `mode="light"` or `mode="dark"` for a fixed appearance. With `mode="auto"` (the default), SwapKit follows the user's OS color scheme preference. Your `theme` overrides are applied to both built-in themes. Properties you omit retain their light or dark defaults. The example above customizes the accent, font, and corner radius while letting backgrounds and text adapt automatically. If you override backgrounds and text colors, choose values that work together in both modes, or pass a different `theme` when your application's theme changes and set `mode` to match. # CoinVoyage SwapKit — embeddable token swaps Source: https://docs.coinvoyage.io/sdk/swapkit Install @coin-voyage/swapkit to add a cross-chain token swap experience to your React application. Use `@coin-voyage/swapkit` when you want to add token swap capabilities to your application. It gives your users an embeddable interface for connecting a wallet, selecting source and destination tokens, requesting a quote, configuring slippage, executing the swap, and tracking its status. SwapKit is independent from the SALE and DEPOSIT payment flows provided by PayKit. If your application needs to accept customer payments or fund a specified wallet or account, use [`@coin-voyage/paykit`](/sdk/overview). ## Installation Install SwapKit with its core peer dependencies: ```bash npm theme={null} npm i @coin-voyage/swapkit @tanstack/react-query react react-dom styled-components ``` ```bash pnpm theme={null} pnpm add @coin-voyage/swapkit @tanstack/react-query react react-dom styled-components ``` ```bash yarn theme={null} yarn add @coin-voyage/swapkit @tanstack/react-query react react-dom styled-components ``` ```bash bun theme={null} bun add @coin-voyage/swapkit @tanstack/react-query react react-dom styled-components ``` Install the wallet peer dependencies listed by `@coin-voyage/swapkit` for the chains your application supports. SwapKit uses `styled-components` and does not require Tailwind CSS. ## Add the swap widget Wrap `Swap` with the re-exported `WalletProvider` and `SwapKitProvider`: ```tsx theme={null} "use client"; import { Swap, SwapKitProvider, WalletProvider } from "@coin-voyage/swapkit"; export function SwapWidget() { return ( ); } ``` `defaultSourceCurrency` and `defaultDestinationCurrency` accept a chain ID and an optional token address. Defaults are read when the widget mounts; remount the widget with a new React `key` to apply changed defaults. ## Configure the swap experience * Use `defaultSlippageBps` to start with a custom slippage tolerance. When you omit it, SwapKit lets the quote service choose the tolerance automatically. * Use `onComplete` to receive the completed swap's `orderId` and `transactionHash`. * Use `onError` to surface execution errors in your application. * Set `mode` on `SwapKitProvider` to `"light"`, `"dark"`, or `"auto"`, and pass `theme` to customize the widget. See [Styling / Theme](/sdk/styling/swapkit) for an example and all theme properties. `SwapKitProvider` uses the public API client and accepts an API key, but never an admin signing key. For a custom swap interface or backend integration, see the [ApiClient swap methods](/sdk/apiclient/swaps). # useOrderStatusWS - order status WebSocket hook Source: https://docs.coinvoyage.io/sdk/useorderstatusws Subscribe to CoinVoyage order status events from React with automatic reconnects and connection-state callbacks. `useOrderStatusWS` subscribes to order status events over the CoinVoyage backend WebSocket from inside a React component. Use it when you need live payment state outside the default `PayButton` lifecycle callbacks, such as a custom checkout page, payment status panel, or embedded dashboard view. `useOrderStatusWS` must run inside `PayKitProvider` because it uses the configured backend API client from context. ## Import ```tsx theme={null} import { useOrderStatusWS } from "@coin-voyage/paykit"; ``` ## Basic usage ```tsx theme={null} "use client"; import { useState } from "react"; import { useOrderStatusWS } from "@coin-voyage/paykit"; export function OrderStatusPanel({ orderId }: { orderId?: string }) { const [connected, setConnected] = useState(false); useOrderStatusWS({ orderId, enabled: Boolean(orderId), onConnectedChange: setConnected, onEvent: (event) => { console.log("Order event:", event); }, onError: (error) => { console.error("Order status stream failed:", error); }, }); return

{connected ? "Connected" : "Connecting"}

; } ``` ## Options Order ID to subscribe to. The hook does not connect until `orderId` is available. Controls whether the WebSocket should be active. Set this to `false` to pause the subscription without unmounting the component. Called for each order event delivered by the backend WebSocket. Called when the WebSocket errors, authentication times out, or the backend returns an error message. Called when the connection becomes authenticated or disconnects. ## Behavior * Connects only when `enabled` is true and `orderId` is present. * Subscribes to the specified order after the WebSocket opens. * Waits for backend authentication before reporting `connected: true`. * Reconnects with exponential backoff after close, error, or authentication timeout. * Cleans up timers and closes the socket when the component unmounts or dependencies change. Use this hook for UI responsiveness only. Fulfillment should still be driven by verified webhooks on your server. # WalletProvider — configure multi-chain wallet connectors Source: https://docs.coinvoyage.io/sdk/walletprovider Configure WalletProvider with chain-specific settings for EVM, Solana, Sui, and UTXO wallets including RPC URLs, adapters, and connectors. `WalletProvider` manages wallet connections for the CoinVoyage PayKit SDK. It wraps `PayKitProvider` in your provider tree and is required to use `PayButton` and `usePayStatus`. Without it, the payment modal cannot connect to a user's wallet. You configure it through a `config` prop that accepts per-chain settings — you only need to supply configuration for the chains your application uses. ## Setup Import `WalletProvider` from `@coin-voyage/paykit` and wrap it around `PayKitProvider`: ```tsx theme={null} "use client"; import { PayKitProvider, WalletProvider } from "@coin-voyage/paykit"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; const queryClient = new QueryClient(); export function Providers({ children }: { children: React.ReactNode }) { return ( {children} ); } ``` All `config` fields are optional. You can pass `` with no props and the SDK will use its default connector configuration. ## Configuration options Object containing chain-type-specific wallet configuration. Every sub-key is optional — include only the chains you need to customize. ### config.evm Configuration for EVM-compatible chains (Ethereum, Polygon, Arbitrum, Base, etc.). Lets you configure the bundled wallet connectors and add custom ones. Configuration passed to the WalletConnect connector. * `projectId` (string) — your WalletConnect Cloud project ID. Required to enable WalletConnect across all EVM chains. Configuration passed to the Coinbase Wallet connector. * `appName` (string) — the display name shown in the Coinbase Wallet app during connection. Configuration passed to the MetaMask connector. Useful when you need to customize MetaMask-specific behavior or pass additional connector options. Optional RPC transport configuration keyed by EVM chain ID. Custom Wagmi connector factories to include with the default EVM connectors. When `true`, lazy-loads wallet SDKs unless the wallet was the most recently connected wallet. ### config.solana Configuration for the Solana chain. Provide a custom RPC endpoint or extend the list of supported wallet adapters. Custom Solana RPC URL. Use this to point to your own node or a premium RPC provider instead of the default public endpoint. Additional wallet adapter configuration. * `wallets` (array) — array of Solana wallet adapter instances to add alongside the default set. ### config.sui Configuration for the Sui chain. Custom Sui gRPC URL for connecting to a specific full node. Additional wallet adapter configuration for the Sui chain. ### config.utxo Configuration for UTXO-based chains such as Bitcoin. When `true`, defers loading UTXO wallet connectors until they are actually needed. This can reduce initial bundle parsing time in applications where UTXO payments are infrequent. Optional Bitcoin RPC transport configuration. ## Examples Use `WalletProvider` with no configuration when the SDK defaults are sufficient: ```tsx theme={null} {children} ``` Supply your WalletConnect Cloud project ID to enable QR code and deep link connections across EVM chains: ```tsx theme={null} {children} ``` Point the Solana connector at a private RPC and add a custom wallet adapter: ```tsx theme={null} import { PhantomWalletAdapter } from "@solana/wallet-adapter-wallets"; {children} ``` # CoinVoyage webhook event types and payload reference Source: https://docs.coinvoyage.io/webhooks/events Every CoinVoyage order webhook event with its subscription identifier and v3 payload structure. CoinVoyage emits webhook events at each stage of the order lifecycle. For every event you subscribe to, CoinVoyage delivers a `POST` request containing an event envelope and the current order snapshot. ## Event types Subscription identifiers and delivered payload values are the same uppercase `ORDER_*` strings. | Event | Description | | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- | | `ORDER_CREATED` | A new order was created. | | `ORDER_AWAITING_PAYMENT` | The order is ready and awaiting payment from the user. | | `ORDER_CONFIRMING` | Payment has been detected and is being confirmed on-chain. | | `ORDER_EXECUTING` | CoinVoyage is executing the destination transfer or contract call. | | `ORDER_COMPLETED` | The order completed successfully. | | `ORDER_ERROR` | An error occurred during processing. | | `ORDER_REFUNDED` | Funds were refunded to the user. | | `ORDER_EXPIRED` | The order expired before payment was received. | | `ORDER_PARTIAL_PAYMENT` | The user sent less than the required amount. Pay to Address can accept additional deposits; other payment methods need resolution. | ## Base payload Every webhook delivery uses this envelope: ```json theme={null} { "event": "ORDER_COMPLETED", "delivered_at": "2026-06-23T12:34:56Z", "order": { "id": "cabc1234567890abcdef12", "created_at": "2026-06-23T12:30:00Z", "updated_at": "2026-06-23T12:34:55Z", "organization_id": "org_123", "mode": "SALE", "status": "COMPLETED", "fulfillment": { "amount": { "ui": "49.99", "raw": "49990000", "value_usd": 49.99 }, "fiat_unit": "USD" }, "payment": { "payment_rail": "CRYPTO", "recipient": "0xMerchantWallet", "source_tx_hash": "0xabc123", "destination_tx_hash": "0xdef456", "steps": [], "expires_at": "2026-06-23T13:00:00Z" }, "metadata": { "order_id": "order_123" }, "hosted_url": "https://pay.coinvoyage.io/pay/cabc1234567890abcdef12" } } ``` | Field | Description | | ---------------- | ------------------------------------------------------------------------------------------------------- | | `event` | Uppercase `ORDER_*` identifier for the lifecycle event. | | `delivered_at` | Event publish time. The value is stable across webhook retries and WebSocket pushes for the same event. | | `order` | Current order snapshot in the v3 `OrderResponse` shape. | | `order.id` | CoinVoyage order ID. Store this for reconciliation and idempotency. | | `order.status` | Current lifecycle status. | | `order.metadata` | Optional metadata attached when the order was created. Use this to map back to your internal records. | | `order.payment` | Payment details. Present after payment instructions have been created. | ## Payment object When present, `order.payment` contains the funding input, resolved output, rail-specific instructions, transaction hashes, and expiry. ```json theme={null} { "payment_rail": "CRYPTO", "input": { "currency": { "chain_id": 1, "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", "symbol": "USDC", "decimals": 6 }, "amount": { "ui": "49.99", "raw": "49990000", "value_usd": 49.99 } }, "output": { "currency": { "chain_id": 30000000000002, "address": null, "symbol": "SUI", "decimals": 9 }, "amount": { "ui": "22.4", "raw": "22400000000", "value_usd": 49.99 } }, "funding": { "required_amount": { "ui": "49.99", "raw": "49990000", "value_usd": 49.99 }, "received_amount": { "ui": "20.00", "raw": "20000000", "value_usd": 20 }, "remaining_amount": { "ui": "29.99", "raw": "29990000", "value_usd": 29.99 }, "transactions": [ { "tx_hash": "0xabc123", "amount": { "ui": "20.00", "raw": "20000000", "value_usd": 20 } } ] }, "recipient": "0xMerchantWallet", "refund": { "address": "0xRefundAddress", "tx_hash": null, "reason": null }, "source_tx_hash": "0xabc123", "destination_tx_hash": "0xdef456", "fee_tx_hash": null, "steps": [ { "rail": "CRYPTO", "kind": "deposit", "deposit_address": "0xDepositAddress", "data": { "deposit_address": "0xDepositAddress", "currency": { "chain_id": 1, "address": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" }, "amount": "49990000" } } ], "expires_at": "2026-06-23T13:00:00Z" } ``` ## Payload examples The examples below highlight event-specific fields. Production deliveries include the full `order` snapshot described above. ```json theme={null} { "event": "ORDER_CREATED", "delivered_at": "2026-06-23T12:30:00Z", "order": { "id": "cabc1234567890abcdef12", "mode": "SALE", "status": "PENDING", "metadata": { "order_id": "order_123" } } } ``` ```json theme={null} { "event": "ORDER_AWAITING_PAYMENT", "delivered_at": "2026-06-23T12:31:00Z", "order": { "id": "cabc1234567890abcdef12", "mode": "SALE", "status": "AWAITING_PAYMENT", "payment": { "payment_rail": "CRYPTO", "recipient": "0xMerchantWallet", "steps": [], "expires_at": "2026-06-23T13:00:00Z" } } } ``` ```json theme={null} { "event": "ORDER_CONFIRMING", "delivered_at": "2026-06-23T12:32:00Z", "order": { "id": "cabc1234567890abcdef12", "mode": "SALE", "status": "AWAITING_CONFIRMATION", "payment": { "source_tx_hash": "0xabc123", "destination_tx_hash": null } } } ``` ```json theme={null} { "event": "ORDER_EXECUTING", "delivered_at": "2026-06-23T12:33:00Z", "order": { "id": "cabc1234567890abcdef12", "mode": "SALE", "status": "EXECUTING_ORDER", "payment": { "source_tx_hash": "0xabc123", "destination_tx_hash": null } } } ``` ```json theme={null} { "event": "ORDER_COMPLETED", "delivered_at": "2026-06-23T12:34:00Z", "order": { "id": "cabc1234567890abcdef12", "mode": "SALE", "status": "COMPLETED", "payment": { "source_tx_hash": "0xabc123", "destination_tx_hash": "0xdef456" } } } ``` ```json theme={null} { "event": "ORDER_ERROR", "delivered_at": "2026-06-23T12:34:00Z", "order": { "id": "cabc1234567890abcdef12", "mode": "SALE", "status": "FAILED" } } ``` ```json theme={null} { "event": "ORDER_REFUNDED", "delivered_at": "2026-06-23T12:34:00Z", "order": { "id": "cabc1234567890abcdef12", "mode": "SALE", "status": "REFUNDED", "payment": { "refund": { "address": "0xRefundAddress", "tx_hash": "0xrefund123", "reason": "execution failed" } } } } ``` ```json theme={null} { "event": "ORDER_EXPIRED", "delivered_at": "2026-06-23T13:00:00Z", "order": { "id": "cabc1234567890abcdef12", "mode": "SALE", "status": "EXPIRED" } } ``` For Pay to Address, use `payment.funding.remaining_amount` to tell the payer what is still due. Each additional deposit is appended to `payment.funding.transactions`. Keep fulfillment paused until a later event reports `ORDER_COMPLETED`. ```json theme={null} { "event": "ORDER_PARTIAL_PAYMENT", "delivered_at": "2026-06-23T12:34:00Z", "order": { "id": "cabc1234567890abcdef12", "mode": "SALE", "status": "PARTIAL_PAYMENT", "payment": { "source_tx_hash": "0xabc123", "funding": { "required_amount": { "ui": "49.99", "raw": "49990000" }, "received_amount": { "ui": "20.00", "raw": "20000000" }, "remaining_amount": { "ui": "29.99", "raw": "29990000" }, "transactions": [ { "tx_hash": "0xabc123", "amount": { "ui": "20.00", "raw": "20000000" } } ] } } } } ``` Additional-deposit recovery is available only for Pay to Address. Do not treat this webhook as resumable for other payment methods. # Set up webhooks for real-time payment notifications Source: https://docs.coinvoyage.io/webhooks/overview Register a CoinVoyage webhook endpoint, verify HMAC-SHA256 delivery signatures, and handle order lifecycle events in your server. Webhooks let you receive real-time notifications whenever an order changes status. Rather than polling the CoinVoyage API for updates, you register an HTTPS endpoint and CoinVoyage delivers a `POST` request with the event details each time something happens: payment created, awaiting payment, confirming, executing, completed, refunded, expired, failed, or partially paid. ## Set up a webhook Register and manage webhook endpoints in the CoinVoyage Dashboard under the **Developers** section. Open the [CoinVoyage Dashboard](https://dashboard.coinvoyage.io/developers), select **Developers** in the sidebar, then click **Webhooks**. Click the **Add Webhook** button to open the registration form. Provide the URL of your publicly accessible webhook handler. Your endpoint must use HTTPS in production. HTTP endpoints are not accepted. Choose which order lifecycle events should trigger delivery to your endpoint. Subscription event identifiers use uppercase `ORDER_*` format, for example `ORDER_COMPLETED`. Save the webhook. CoinVoyage generates a **Webhook Secret**. Store it securely, for example as `COIN_VOYAGE_WEBHOOK_SECRET`. You need this secret to verify the signature on every incoming request. Store the Webhook Secret in an environment variable, never in source code or version control. Your endpoint must be publicly accessible and respond with a `2xx` status code within 30 seconds. Responses outside this window are treated as delivery failures. ## Delivery payload CoinVoyage v3 delivers the same event envelope to registered webhooks and `/v3/ws` subscribers: ```json theme={null} { "event": "ORDER_COMPLETED", "delivered_at": "2026-06-23T12:34:56Z", "order": { "id": "cabc1234567890abcdef12", "mode": "SALE", "status": "COMPLETED", "metadata": { "order_id": "order_123" } } } ``` Use `event` to dispatch business logic and `order.id` as the CoinVoyage payment lifecycle ID. Use `order.metadata` to reconcile back to your internal order, invoice, account, or customer. ## Verify webhook signatures Every webhook request includes a `CoinVoyage-Webhook-Signature` header containing an HMAC-SHA256 signature of the raw request body, encoded in Base64. Always verify this signature before parsing or acting on the payload. The example below shows a complete Next.js Route Handler that verifies the signature and dispatches on the event identifier: ```typescript app/api/webhook/route.ts theme={null} import { Buffer } from "buffer"; import { createHmac, timingSafeEqual } from "crypto"; const webhookSecret = process.env.COIN_VOYAGE_WEBHOOK_SECRET!; type CoinVoyageWebhookEvent = { event: string; delivered_at: string; order: { id: string; status: string; metadata?: Record; }; }; export const POST = async (req: Request) => { const rawBody = await req.text(); const signature = req.headers.get("CoinVoyage-Webhook-Signature"); const expected = createHmac("sha256", webhookSecret) .update(rawBody) .digest("base64"); const signatureBytes = Buffer.from(signature ?? ""); const expectedBytes = Buffer.from(expected); if ( signatureBytes.length !== expectedBytes.length || !timingSafeEqual(signatureBytes, expectedBytes) ) { return new Response("Unauthorized", { status: 401 }); } const event = JSON.parse(rawBody) as CoinVoyageWebhookEvent; switch (event.event) { case "ORDER_COMPLETED": console.log("Order completed", event.order.id); break; case "ORDER_REFUNDED": console.log("Order refunded", event.order.id); break; case "ORDER_EXPIRED": console.log("Order expired", event.order.id); break; default: console.log("Unhandled webhook", event.event, event.order.id); } return new Response("Webhook received", { status: 200 }); }; ``` Read the raw request body with `req.text()` before calling `JSON.parse`. Web Fetch API request bodies can only be read once, so read once as text, verify the signature, then parse. ## Security best practices * Verify the signature before parsing or acting on the payload. * Use a constant-time comparison such as `timingSafeEqual` for signature checks. * Use HTTPS in production. * Store your Webhook Secret securely in a server-side secret store. * Return a `2xx` response quickly and move heavy work to a queue or background job. * Make event handling idempotent by `event`, `delivered_at`, and `order.id`, plus your own internal ID from `order.metadata`. * Return `200` for event types you intentionally ignore so delivery does not retry forever.