CA Remittance Integration Guide

How do I send cross-border remittances from Canadian customers using Cybrid-integrated payouts?

CA Remittance lets Canadian individuals send cross-border remittances using Cybrid-integrated payouts. Your
customer funds a Canadian dollar balance from their own bank, Cybrid converts it to a stablecoin, and a
remittance plan delivers the funds to a recipient abroad in the local currency — all through a single API
integration.

Under the hood, the customer funds a CAD fiat account via Interac e-Transfer (no bank linking required),
converts CAD directly to USDC_SOL in a trading account, then a remittance plan pays out to the recipient's
foreign bank account over the local rail. Senders are onboarded as individuals with KYC, and recipients are
tracked as verified counterparties.

India (INR over IFSC) is the example corridor used throughout. Other corridors are available and follow the
same flow with corridor-specific fields.

At a high level, you will:

  1. Onboard and verify the customer with KYC.
  2. Create the CAD fiat account, the USDC_SOL trading account, and the customer's deposit bank account.
  3. Fund the CAD account with an Interac e-Transfer deposit.
  4. Convert CAD directly to USDC_SOL.
  5. Create and verify the recipient counterparty and their foreign bank account.
  6. Create and execute the remittance plan to pay out.

Flow of funds

Figure: CA remittance flow of funds.

Funds move left to right across the Cybrid boundary. The customer's external Canadian bank account funds their
CAD fiat account inside Cybrid over Interac e-Transfer (no Plaid link required), where KYC and the deposit bank
account are held. A trade settles the CAD directly into a USDC_SOL trading account, and a remittance plan
converts and pays out to the recipient's foreign external bank account. The recipient is tracked as a
verified individual counterparty.

Flow of funds diagram: Canadian customer funds a CAD account via Interac e-Transfer, converts CAD to USDC_SOL, then a remittance plan pays out to a foreign bank account

Arrow colors in the diagram: green is fiat movement, orange is crypto movement, blue is trade settlement, and
tan is a book transfer.


Table of Contents

  1. Prerequisites
  2. Customer onboarding
  3. Create accounts
  4. Create deposit bank account
  5. Fund the CAD account via Interac e-Transfer
  6. Convert CAD to USDC_SOL
  7. Create the recipient counterparty
  8. Create the recipient external bank account
  9. Create and execute the remittance plan
  10. Webhooks
  11. Additional resources

1. Prerequisites

Before integrating the CA Remittance flow, configure your sandbox bank with the required features.

Bank configuration

When creating your bank in the sandbox environment, configure these settings:

Customer type support:

Enable individual customers:

  • Individual customers (individual_customers)

Fiat account assets:

Enable the fiat currency for the funding country:

  • CAD

Trading symbols:

Enable the USDC_SOL-CAD trading pair for direct CAD to USDC_SOL conversion.

⚠️

Canada: zero balances required

Due to regulatory requirements in Canada, Cybrid cannot hold balances in customer accounts
on the platform. All funds submitted to the platform in Canada must have instructions
assigned to keep the funds in transit, with zero balances maintained once transactions
complete.

ℹ️

Enable the payout symbols for your corridors

Contact Cybrid support to enable the payout symbols for the payout corridors you require (for
example, INR-USDC_SOL). Confirm corridor support via supported_payout_symbols. This is the
only enablement step for Cybrid-integrated remittance payouts — Interac e-Transfer funding and
direct CAD to USDC_SOL conversion require no separate bank feature toggles.

ℹ️

Supported corridors for C2C remittances

This guide implements a consumer-to-consumer (C2C) remittance. For the full list of destination
corridors and the participant types each supports, see
Supported Corridors. Some corridors originate
from the US only (for example, Pakistan) and are not available from Canada.


2. Customer onboarding

Create the customer, then verify their identity with KYC through the Persona SDK.

Create the customer

Endpoint: POST /api/customers
Reference: Create Customer

Set type to individual. Only type is required at creation.

POST /api/customers
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "type": "individual",
  "email_address": "[email protected]",
  "phone_number": "555-0100"
}
{
  "guid": "customer_guid",
  "type": "individual",
  "state": "storing",
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}
ℹ️

PII fields are optional at creation

Not all fields are required to create a customer. Email and phone help identify customers beyond
the customer_guid. Do not provide the customer's name at creation — Persona collects it during
KYC, and the legal name returned by Persona becomes the customer's name on file.

Customer states

StateDescription
storingPlatform is storing customer details
unverifiedCustomer created, awaiting identity verification
verifiedCustomer identity verified
rejectedCustomer verification rejected
frozenCustomer account frozen

Poll GET /api/customers/{customer_guid} until state transitions to unverified.

ℹ️

Polling interval

Use exponential backoff starting at 1 second. In sandbox, state transitions typically complete
within seconds. In production, some operations may take longer.

Verify the customer

Endpoint: POST /api/identity_verifications
Reference: Create Identity Verification

Create the Persona inquiry. Cybrid returns a persona_inquiry_id you pass to the Persona SDK to launch the
capture flow — government ID and selfie (KYC). Use method: id_and_selfie:

POST /api/identity_verifications
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "type": "kyc",
  "method": "id_and_selfie",
  "customer_guid": "customer_guid"
}
{
  "guid": "identity_verification_guid",
  "type": "kyc",
  "method": "id_and_selfie",
  "customer_guid": "customer_guid",
  "state": "storing",
  "outcome": null,
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}
ℹ️

Preparing customers before verification

Preparing customers before verification greatly increases success rates.
See: Verify an Individual Customer

⚠️

Customer must be unverified

Identity verification can only be initiated for customers in the unverified state. The customer
state transitions from storing to unverified before you can start the verification process.

Identity verification states

StateDescription
storingPlatform is storing verification details
waitingWaiting for the customer to complete verification
pendingVerification submitted, awaiting processing
reviewingVerification under manual review
expiredVerification session expired
completedVerification completed (check the outcome field)

Poll GET /api/identity_verifications/{identity_verification_guid} until state transitions to
completed, then confirm outcome is passed.

Integrate the Persona SDK

Persona offers Hosted Flow, Embedded Flow, and native iOS/Android SDKs. The example below uses the Embedded Flow via NPM — see Persona's integration methods to pick the option that fits your app.

Recommended integration

Cybrid recommends the embedded flow with the native iOS and Android SDKs for the best customer experience.

Install the SDK:

npm install [email protected] --save

Initialize the client with the persona_inquiry_id from the identity verification response:

import Persona from 'persona';

const client = new Persona.Client({
  inquiryId: 'persona_inquiry_id',
  language: 'en',
  onReady: () => client.open(),
  onComplete: ({ inquiryId, status, fields }) => {
    // Poll Cybrid or subscribe to identity_verification.completed
  },
  onCancel: ({ inquiryId, sessionToken }) => { /* user dismissed */ },
  onError: (error) => { /* log SDK error */ }
});

Key callbacks:

  • onReady — SDK ready to open.
  • onComplete — user finished the flow; verification can still fail downstream, confirm via the Cybrid API.
  • onCancel — user dismissed the flow.
  • onError — SDK error.

The language parameter accepts ISO 639-1 codes.
See Persona's supported languages for the full list.

ℹ️

Resume incomplete sessions

If the customer abandons the flow before completing, generate a new session token via
POST /api/persona_sessions and pass it to the SDK as sessionToken instead of inquiryId.
See Resume Persona Inquiry Session.

ℹ️

Sandbox testing with expected_behaviours

In sandbox, set expected_behaviours on the identity verification request to force a deterministic
outcome — useful for end-to-end tests without driving the Persona flow.

ValueEffect
passed_immediatelyVerification completes with outcome: passed.
failed_immediatelyVerification completes with outcome: failed.
tax_id_not_checkedTax ID check is skipped during verification.

Example expected_behaviours request body:

{
  "type": "kyc",
  "method": "id_and_selfie",
  "customer_guid": "customer_guid",
  "expected_behaviours": ["passed_immediately"]
}
❗️

Sandbox only

The expected_behaviours field is rejected in production with a 400 error:
Parameter expected_behaviours not permitted in production. Remove the field before deploying.

When the customer returns from the Persona SDK workflow, confirm the customer record has transitioned to
verified by querying GET /api/customers/{customer_guid}. Once verified, the customer can proceed with
account funding and trading.

Related: Create a Customer ·
Verify an Individual Customer

Checkpoint

The customer is verified. You can now create their accounts and move on to funding.


3. Create accounts

Create the CAD fiat account and the USDC_SOL trading account for the customer.

Endpoint: POST /api/accounts
Reference: Create Account

Create the CAD fiat account

POST /api/accounts
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "type": "fiat",
  "customer_guid": "customer_guid",
  "asset": "CAD",
  "name": "CAD Account"
}
{
  "guid": "fiat_account_guid",
  "type": "fiat",
  "customer_guid": "customer_guid",
  "asset": "CAD",
  "name": "CAD Account",
  "state": "storing",
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}

Create the USDC_SOL trading account

POST /api/accounts
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "type": "trading",
  "customer_guid": "customer_guid",
  "asset": "USDC_SOL",
  "name": "USDC_SOL Trading Account"
}
{
  "guid": "trading_account_usdc_sol_guid",
  "type": "trading",
  "customer_guid": "customer_guid",
  "asset": "USDC_SOL",
  "name": "USDC_SOL Trading Account",
  "state": "storing",
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}

Account states

StateDescription
storingPlatform is storing account details
createdAccount created and ready for use

Poll GET /api/accounts/{account_guid} until each account's state transitions to created.

ℹ️

Account types

Fiat accounts hold currencies like CAD, while trading accounts hold cryptocurrencies like
USDC_SOL. The USDC_SOL trading account is the source account for the remittance plan in
step 9.

ℹ️

Customer fiat accounts require a deposit bank account

Every customer fiat account requires a main deposit bank account before any funding transfer
can complete. Create it immediately after the fiat account — see
step 4 and
Customer Deposit Bank Accounts.

Related: Creating Platform Accounts


4. Create deposit bank account

Create a main deposit bank account for the customer's CAD fiat account. This is required before any
funding transfer can complete.

Endpoint: POST /api/deposit_bank_accounts
Reference: Create Deposit Bank Account

POST /api/deposit_bank_accounts
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "type": "main",
  "account_guid": "fiat_account_guid",
  "customer_guid": "customer_guid"
}
{
  "guid": "deposit_bank_account_guid",
  "type": "main",
  "customer_guid": "customer_guid",
  "account_guid": "fiat_account_guid",
  "asset": "CAD",
  "state": "storing",
  "unique_memo_id": "unique_memo_id",
  "counterparty_name": "John Doe",
  "account_details": [
    { "account_number": "111222333" }
  ],
  "routing_details": [
    { "routing_number_type": "CPA", "routing_number": "000100002" }
  ],
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}

Deposit bank account states

StateDescription
storingPlatform is storing account details
createdDeposit bank account created

Poll GET /api/deposit_bank_accounts/{deposit_bank_account_guid} until state transitions to created.

⚠️

Deposit bank account enforcement (effective 2026-06-17)

From 2026-06-17, every customer fiat account must have a main deposit bank account before any
funding transfer (deposit or withdrawal) can be created. Provision the deposit bank account
immediately after creating the customer's fiat account — see
Customer Deposit Bank Accounts.

ℹ️

Routing details

The response includes account_details (account number), routing_details (CPA routing number),
counterparty_name (account holder name), and counterparty_address (account holder address)
for incoming EFT deposits.

ℹ️

No deposit bank account error

Funding transfers against a customer fiat account that has no main deposit bank account fail
with HTTP 422 and message_code no_deposit_bank_account. Remediate by creating the main
deposit bank account on the fiat account, then retry the transfer.

Related: Customer Deposit Bank Accounts


5. Fund the CAD account via Interac e-Transfer

Fund the CAD fiat account with an Interac e-Transfer deposit. No Plaid bank linking is required — the
customer authorizes the deposit through their own bank using a gateway_url.

ℹ️

Availability

Interac e-Transfer is only available to Canadian banks on the Cybrid Platform and only works
with CAD on CAD-based fiat accounts.

Create the funding quote

Endpoint: POST /api/quotes
Reference: Create Quote

POST /api/quotes
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "product_type": "funding",
  "customer_guid": "customer_guid",
  "asset": "CAD",
  "side": "deposit",
  "deliver_amount": 10000
}
{
  "guid": "quote_guid",
  "product_type": "funding",
  "customer_guid": "customer_guid",
  "asset": "CAD",
  "side": "deposit",
  "deliver_amount": 10000,
  "receive_amount": 10000,
  "fee": 0,
  "expires_at": "2026-07-19T12:00:30.000Z",
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}
ℹ️

Amounts are in base units

Amounts are in base units. For CAD, 10000 = $100.00 (cents). For USDC_SOL, 10000000 = 10.00
USDC_SOL (micro-dollars).

ℹ️

Deliver vs receive amount

Use deliver_amount by default — this is the amount the customer inputs (what they are sending).
The response returns receive_amount with fees deducted (in production; no fees in sandbox).
Alternatively, specify receive_amount to lock in the amount the recipient receives after fees,
meaning the customer pays receive_amount + fees.

⚠️

Quotes expire in 30 seconds

Quotes are valid for 30 seconds. Use the expires_at field on the quote response to determine
when a quote expires. Trades or transfers against expired quotes fail.

Execute the transfer

Endpoint: POST /api/transfers
Reference: Create Transfer

Create the transfer with transfer_type set to funding and payment_rail set to etransfer. Do not
include external_bank_account_guid — it is not used on the e-transfer rail.

POST /api/transfers
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "quote_guid": "quote_guid",
  "transfer_type": "funding",
  "payment_rail": "etransfer",
  "source_participants": [
    {"type": "customer", "guid": "customer_guid", "amount": 10000}
  ],
  "destination_participants": [
    {"type": "customer", "guid": "customer_guid", "amount": 10000}
  ]
}
{
  "guid": "transfer_guid",
  "transfer_type": "funding",
  "customer_guid": "customer_guid",
  "quote_guid": "quote_guid",
  "asset": "CAD",
  "side": "deposit",
  "state": "storing",
  "estimated_amount": 10000,
  "fee": 0,
  "payment_rail": "etransfer",
  "identifiers": [],
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}

Display the gateway URL

Wait for the transfer to transition to the pending state. Once pending, read the gateway_url from the
identifiers array on the transfer — the identifier has type set to gateway_url and value set to the
URL. Display it to the customer so they can select their bank and authorize the deposit.

ℹ️

Domain allowlisting

The gateway_url uses the domain etransfer.interac.ca. Allowlist this domain in your
application if needed.

⚠️

Asynchronous processing

Generating the gateway_url can take time in both sandbox and production due to sponsor bank
response times. Use webhook registration and polling as a backup to handle the asynchronous
gateway_url generation.

ℹ️

Deposit request expiry

The customer must authorize the deposit before the e-transfer request expires. Deposit requests
are valid for 1 day.

⚠️

Sandbox behavior

In sandbox, Interac e-Transfer deposits complete automatically and the gateway_url returned is
not a valid URL because the sponsor bank does not support the interactive flow in sandbox.

Wait for completion

Poll GET /api/transfers/{transfer_guid} or subscribe to the transfer.completed webhook. Once the
transfer is completed, the CAD funds are available on the platform.

Transfer states

StateDescription
storingPlatform is storing transfer details
pendingTransfer pending; gateway_url is available in identifiers
holdingTransfer on hold (compliance review)
reviewingTransfer under manual review
completedTransfer completed
failedTransfer failed
cancellingCancellation requested (transient); resolves to failed (failure_code cancelled) or completed

Webhook Events: transfer.pending, transfer.completed, transfer.failed, transfer.cancelling

Handle terminal failed states via the failure_code:

  • payment_request_expired — the customer did not authorize before the URL expired.
  • cancelled — the customer rejected the e-transfer request.

Related: Interac e-Transfer ·
Transfer Process ·
Transfer Participants

Checkpoint

The e-Transfer funding transfer is completed and CAD is available in the customer's fiat account. You are
ready to convert it to USDC_SOL.


6. Convert CAD to USDC_SOL

Convert CAD directly to USDC_SOL in the USDC_SOL trading account using the USDC_SOL-CAD trading symbol.
Direct conversion is enabled for Canada — no intermediate USDC swap is required. This settlement funds the
source account for the remittance plan.

Create the trading quote

Endpoint: POST /api/quotes
Reference: Create Quote

POST /api/quotes
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "product_type": "trading",
  "customer_guid": "customer_guid",
  "symbol": "USDC_SOL-CAD",
  "side": "buy",
  "deliver_amount": 10000
}
{
  "guid": "quote_guid",
  "product_type": "trading",
  "customer_guid": "customer_guid",
  "symbol": "USDC_SOL-CAD",
  "side": "buy",
  "receive_amount": 7200000,
  "deliver_amount": 10000,
  "fee": 0,
  "issued_at": "2026-07-19T12:00:00.000Z",
  "expires_at": "2026-07-19T12:00:30.000Z",
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}
⚠️

Quotes expire in 30 seconds

Quotes are valid for 30 seconds. Use the expires_at field on the quote response to determine
when a quote expires. Trades or transfers against expired quotes fail.

Execute the trade

Endpoint: POST /api/trades
Reference: Create Trade

POST /api/trades
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "quote_guid": "quote_guid"
}
{
  "guid": "trade_guid",
  "trade_type": "platform",
  "customer_guid": "customer_guid",
  "quote_guid": "quote_guid",
  "symbol": "USDC_SOL-CAD",
  "side": "buy",
  "state": "storing",
  "receive_amount": 7200000,
  "deliver_amount": 10000,
  "fee": 0,
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}

Trade states

StateDescription
storingPlatform is storing trade details
pendingTrade pending execution
executedTrade executed on exchange
settlingTrade settling
completedTrade completed; USDC_SOL is available in the trading account
cancelledTrade cancelled
failedTrade failed

Webhook Events: trade.pending, trade.completed, trade.failed

Poll GET /api/trades/{trade_guid} or subscribe to the trade.completed webhook until state
transitions to completed.

Related: Execute Trades

Checkpoint

The trade is completed and USDC_SOL is available in the trading account. This balance is the source for the
remittance plan.


7. Create the recipient counterparty

Create the recipient as a customer-level individual counterparty and verify it via watchlists. The
counterparty must reach the verified state before you can create the recipient external bank account and
the remittance plan.

Create the counterparty

Endpoint: POST /api/counterparties
Reference: Create Counterparty

Include only the required fields: type, name, address.city, and address.country_code. Add
customer_guid for the customer-scoped counterparty.

POST /api/counterparties
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "type": "individual",
  "customer_guid": "customer_guid",
  "name": {
    "first": "John",
    "last": "Doe"
  },
  "address": {
    "city": "Mumbai",
    "country_code": "IN"
  }
}
{
  "guid": "recipient_counterparty_guid",
  "type": "individual",
  "customer_guid": "customer_guid",
  "state": "storing",
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}
ℹ️

Required fields only

For individual counterparties the required fields are type, name.first, name.last,
address.city, and address.country_code. Add customer_guid for customer-scoped counterparties
created with a bank token. The API spec marks only country_code as required on address, but the
Cybrid platform enforces city + country_code together — see
Create a Counterparty.

ℹ️

Corridor-specific fields

For Cybrid-managed remittance corridors, the destination country may require additional fields
on the recipient counterparty (date_of_birth, identification_numbers, address street +
subdivision, etc.). Refer to the relevant payout guide for the destination country before
omitting fields in production. See Supported Corridors.

Verify the counterparty

Endpoint: POST /api/identity_verifications
Reference: Create Identity Verification

Counterparty verification screens the recipient's PII through watchlist checks against government sanctions
lists, PEP databases, and adverse media sources. It is an API call that does not require an SDK.

POST /api/identity_verifications
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "type": "counterparty",
  "method": "watchlists",
  "counterparty_guid": "recipient_counterparty_guid"
}
{
  "guid": "identity_verification_guid",
  "type": "counterparty",
  "method": "watchlists",
  "counterparty_guid": "recipient_counterparty_guid",
  "customer_guid": "customer_guid",
  "state": "storing",
  "outcome": null,
  "failure_codes": [],
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}

Counterparty states

StateDescription
storingPlatform is storing counterparty details
unverifiedCounterparty created, awaiting verification
verifiedCounterparty verified via watchlist check
rejectedCounterparty rejected

Poll GET /api/identity_verifications/{identity_verification_guid} until state transitions to
completed. When outcome is passed, the counterparty transitions to verified; when failed, inspect
failure_codes (for example watchlist_check_failure, pep_check_failure, media_check_failure) and the
counterparty transitions to rejected. The platform checks counterparty verification results at
one-minute intervals, so completion may take up to one minute.

Webhook Events: identity_verification.waiting, identity_verification.completed

Related: Verify a Counterparty ·
Creating Counterparties


8. Create the recipient external bank account

Create the recipient's foreign external bank account tied to the verified counterparty. The example below
uses India (INR over IFSC); required fields vary by destination corridor.

Endpoint: POST /api/external_bank_accounts
Reference: Create External Bank Account

ℹ️

Use raw routing details for foreign payouts

Use account_kind raw_routing_details for the recipient's foreign bank account and tie it to
the verified counterparty_guid. The payment_rail and bank_code_type depend on the
destination corridor — IFSC for India, and other rails (SPEI, PIX, SEPA, and so on) for
other corridors.

ℹ️

Accept-Version for foreign raw-routing accounts

Creating foreign raw-routing external bank accounts requires Accept-Version: 2025-10-01 or
later. Corridor-specific bank-account fields vary by destination; refer to the relevant payout
guide before omitting fields in production.

POST /api/external_bank_accounts
Content-Type: application/json
Accept-Version: 2025-10-01
Authorization: Bearer YOUR_TOKEN

{
  "name": "Recipient INR Account",
  "account_kind": "raw_routing_details",
  "customer_guid": "customer_guid",
  "counterparty_guid": "recipient_counterparty_guid",
  "asset": "INR",
  "counterparty_bank_account_details": [
    {
      "account_identifier": "account_number",
      "payment_rail": "IFSC",
      "bank_code": "bank_ifsc_code",
      "bank_code_type": "IFSC"
    }
  ]
}
{
  "guid": "recipient_external_bank_account_guid",
  "name": "Recipient INR Account",
  "asset": "INR",
  "account_kind": "raw_routing_details",
  "environment": "sandbox",
  "bank_guid": "bank_guid",
  "customer_guid": "customer_guid",
  "counterparty_guid": "recipient_counterparty_guid",
  "state": "storing",
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}
ℹ️

Counterparty must be verified

The referenced counterparty must be in the verified state. Creating an external bank account
against an unverified counterparty fails with the unverified_counterparty failure code.

External bank account states

StateDescription
storingPlatform is storing external bank account details
completedExternal bank account created
unverifiedExternal bank account created but not yet verified
failedExternal bank account creation failed
refresh_requiredExternal bank account needs to be refreshed
deletingPlatform is deleting the external bank account
deletedExternal bank account deleted

Poll GET /api/external_bank_accounts/{external_bank_account_guid} until state transitions to completed.

Related: Foreign Fiat External Bank Accounts · Add External Bank Accounts

Checkpoint

The recipient counterparty is verified and their foreign external bank account is completed. You have
everything needed to create and execute the remittance plan.


9. Create and execute the remittance plan

Create a remittance plan with the USDC_SOL trading account as the source and the recipient's foreign
external bank account as the destination, then execute it. Cybrid converts USDC_SOL to INR and pays out
over the local rail.

Create the remittance plan

Endpoint: POST /api/plans
Reference: Create Plan

POST /api/plans
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "type": "remittance",
  "customer_guid": "customer_guid",
  "source_account": {
    "guid": "trading_account_usdc_sol_guid",
    "amount": 7200000
  },
  "destination_account": {
    "guid": "recipient_external_bank_account_guid"
  },
  "purpose_of_transaction": "family_support"
}
{
  "guid": "plan_guid",
  "type": "remittance",
  "customer_guid": "customer_guid",
  "state": "storing",
  "expires_at": "2026-07-19T12:00:30.000Z",
  "source_account": {
    "guid": "trading_account_usdc_sol_guid",
    "type": "trading",
    "asset": "USDC_SOL",
    "requested_amount": 7200000
  },
  "destination_account": {
    "guid": "recipient_external_bank_account_guid",
    "type": "external_bank_account",
    "asset": "INR"
  },
  "stages": [],
  "fees": [],
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}

Plan states

StateDescription
storingPlatform is storing plan details
planningPlatform is building the plan
completedPlan is ready for execution
failedPlan creation failed

Poll GET /api/plans/{plan_guid} until state transitions to completed, then execute it before it expires.

Webhook Events: plan.planning, plan.completed, plan.failed

⚠️

Enable the remittance feature

Contact Cybrid support to enable the remittance feature on your bank before using this operation.

ℹ️

Specify the amount on the source account

You must specify an amount on either the source or destination account — the amount drives the
plan's quote. If source amount is specified, the plan ensures that amount is debited. If
destination amount is specified, the plan ensures that exact amount is deposited. Include amount
under source_account by default, expressed in the source account asset's base units (USDC_SOL).

ℹ️

Purpose of transaction

purpose_of_transaction is corridor-dependent. Some destinations reject a plan without an
accepted purpose (invalid_purpose_of_transaction). Refer to the destination payout guide for
accepted values.

ℹ️

Supporting documents (corridor-dependent)

Some corridors require supporting documents attached to the plan's supporting_documents (for
example, an invoice carrying a document_reference_number). This is most common for B2B payouts
— see the "Upload a supporting document" step in the CA B2B and US B2B integration guides, and
File Upload, before going live.

ℹ️

Travel rule information

Travel rule information is required only when the originator is not the holder of the trading
account or when the receiver is not the holder of the external bank account.

⚠️

Reversals may incur fees

If the final withdrawal to the destination bank account fails after currency conversions have
occurred, any reversals may incur fees.

Execute the plan

Endpoint: POST /api/executions
Reference: Create Execution

POST /api/executions
Content-Type: application/json
Authorization: Bearer YOUR_TOKEN

{
  "plan_guid": "plan_guid"
}
{
  "guid": "execution_guid",
  "type": "remittance",
  "plan_guid": "plan_guid",
  "customer_guid": "customer_guid",
  "state": "storing",
  "source_account": {
    "guid": "trading_account_usdc_sol_guid",
    "type": "trading",
    "asset": "USDC_SOL"
  },
  "destination_account": {
    "guid": "recipient_external_bank_account_guid",
    "type": "external_bank_account",
    "asset": "INR"
  },
  "stages": [],
  "fees": [],
  "created_at": "2026-07-19T12:00:00.000Z",
  "updated_at": "2026-07-19T12:00:00.000Z"
}

Execution states

StateDescription
storingPlatform is storing execution details
executingPlatform is executing the plan
completedPlan execution completed successfully
failedPlan execution failed

Webhook Events: execution.executing, execution.completed, execution.failed

Poll GET /api/executions/{execution_guid} or subscribe to the execution.completed webhook until state
transitions to completed. On failure, inspect the failure_code (for example compliance_rejection,
invalid_destination, party_name_invalid, external_vendor_error).

ℹ️

Creating an execution initiates the transaction

Creating an execution initiates the actual transaction. Ensure the plan has been reviewed and is
ready before executing.

⚠️

Plans expire quickly

Remittance plans expire (default ~60 seconds, plan_expired error). Use the expires_at field
on the plan response to confirm when the plan expires, and execute it before then.

ℹ️

Executed amounts

The executed amounts in the response represent the actual amounts transferred, which should match
the quoted amounts from the plan.

Related: Sending Cross-Border Payments · Payout Prices


Webhooks

Subscribe to webhooks for real-time notifications instead of polling. Configure webhook endpoints in your
bank settings. Accounts, deposit bank accounts, and external bank accounts have no webhook
events in this integration — use polling for those resources.

ℹ️

Webhook events

This integration uses the following webhook events:

EventTrigger
identity_verification.waitingIdentity verification is waiting for the customer to start.
identity_verification.completedIdentity verification completed (check outcome).
transfer.pendingE-Transfer funding transfer is pending; gateway_url available.
transfer.completedFunding transfer completed.
transfer.failedFunding transfer failed.
transfer.cancellingFunding transfer is being cancelled.
trade.pendingCAD to USDC_SOL trade is pending.
trade.completedTrade completed.
trade.failedTrade failed.
plan.planningRemittance plan is being built.
plan.completedRemittance plan is ready for execution.
plan.failedRemittance plan creation failed.
execution.executingRemittance execution is running.
execution.completedRemittance payout completed.
execution.failedRemittance execution failed.

See: Webhook Events


Additional resources



Did this page help you?