Orders & Order Types
Orders create — quick reference
Authoritative schema:
npx @koyweforest/cli orders create --schemaprints the full, up-to-date JSON schema for the create request. If the summary below ever diverges from that output, the CLI schema is the source of truth.
Endpoint (PAYIN / PAYOUT / BALANCE_TRANSFER / PAYMENT_LINK / INTER_MERCHANT_TRANSFER): POST /organizations/{orgId}/merchants/{merchantId}/orders
ONRAMP and OFFRAMP are not created via this endpoint. Crypto operations go through the deals endpoint (POST /organizations/{orgId}/merchants/{merchantId}/deals) because a deal can be paid in full or across multiple partial payments, each of which creates its own order. See the ONRAMP and OFFRAMP sections below, or use npx @koyweforest/cli flow deal.
Required fields:
type— always required.originCurrencySymbol,destinationCurrencySymbol— required unless you passquoteId(in which case omit them along withamountIn/amountOut; the quote carries all three).
type enum (7 values — ONRAMP and OFFRAMP route via /deals, the rest via /orders):
PAYIN · PAYOUT · BALANCE_TRANSFER · PAYMENT_LINK · INTER_MERCHANT_TRANSFER — via /orders
ONRAMP · OFFRAMP — via /deals (see below)
Amount specification (mutually exclusive — pick exactly one):
amountIn— amount in origin currency.amountOut— amount in destination currency.quoteId— ID of a prior quote that locks the rate. When you passquoteId, omitoriginCurrencySymbol,destinationCurrencySymbol,amountIn, andamountOut— the quote supplies them.
Conditional requirements (for types that route via /orders):
| Order type | Also required |
|---|---|
PAYIN | paymentMethods (exactly one element) |
PAYOUT | destinationAccountId |
BALANCE_TRANSFER | An active policy with a matching rule (see Passkeys & Approvals). Auto-sets isMerchantSelfOrder=true if not provided. |
PAYMENT_LINK | paymentMethods may be [] |
INTER_MERCHANT_TRANSFER | destinationMerchantId |
For ONRAMP / OFFRAMP see the Deals sections — those types require destinationAccountId and a network from the enum below.
network enum (ONRAMP/OFFRAMP, passed on the deal):
ETHEREUM · POLYGON · SOLANA · TRON · BSC · BITCOIN · BASE · ALGORAND
paymentMethods shape:
[
{ "method": "KHIPU" }
][
{ "method": "PSE", "extra": { "bankAccount": { "name": "BANCOLOMBIA" } } }
]Policy & MFA: Operations gated by policy return POL00007 (428 Precondition Required) and the order enters ON_HOLD until approved. Pass --mfa-token <token> to confirm inline, or use npx @koyweforest/cli flow order --wait to block on polling.
See the full CLI reference for orders create, the Error Code Catalog for every OR* / BAA* / POL* code, and the complete orders API spec.
Orders are the core transaction entities in Koywe Payments. Each order type serves a specific purpose in your payment operations.
Order Types Overview
Koywe Payments supports seven distinct order types:
| Type | Purpose | Origin | Destination | Use Case |
|---|---|---|---|---|
| PAYIN | Accept customer payments | External (customer) | Virtual balance | E-commerce checkout |
| PAYOUT | Pay providers | Virtual balance | External (provider bank) | Vendor payments |
| BALANCE_TRANSFER | Currency exchange | Virtual balance (currency A) | Virtual balance (currency B) | Convert COP to USD |
| ONRAMP | Buy crypto | Virtual balance (fiat) | Crypto wallet | Buy USDC with COP |
| OFFRAMP | Sell crypto | Crypto wallet | Virtual balance (fiat) | Sell BTC for COP |
| PAYMENT_LINK | Payment link | External (customer) | Virtual balance | Invoice payment |
| INTER_MERCHANT_TRANSFER | Move funds between merchants in the same organization | Virtual balance (merchant A) | Virtual balance (merchant B) | Treasury operations within one organization |
Order Status Lifecycle
All orders follow a similar status progression:
Status Descriptions:
| Status | Description | Actions Available |
|---|---|---|
PENDING | Order created, waiting for payment | Cancel, Check status |
ON_HOLD | Held pending MFA verification or human approval under an active policy (POL00007 at creation). See Policy & MFA and Passkeys & Approvals. | Approve / reject via dashboard, provide --mfa-token, or poll with npx @koyweforest/cli flow order --wait |
PROCESSING | Payment being processed | Check status |
PAID | Payment confirmed, settlement in progress | Check status |
COMPLETED | Funds delivered, order complete | View details |
FAILED | Payment or settlement failed | Retry, Contact support |
CANCELLED | Order cancelled (or approval rejected) | View details |
EXPIRED | Payment window expired | Create new order |
PAYIN - Accept Customer Payments
Accept payments from customers into your virtual balance.
When to Use
- E-commerce checkout
- Service payments
- Subscription billing
- Invoice payments
- Donation collection
Flow Diagram
Required Fields
{
"type": "PAYIN", // Order type
"originCurrencySymbol": "COP", // Fiat currency
"destinationCurrencySymbol": "COP", // Same as origin
"amountIn": 50000, // Amount to collect
"description": "Payment for Order #12345", // Customer-facing description
"paymentMethods": [ // At least one payment method
{
"method": "PSE", // Payment method code
"extra": { "bankAccount": { "name": "BANCOLOMBIA" } } // Per-method extras
}
],
"successUrl": "https://yoursite.com/success", // Redirect after success
"failedUrl": "https://yoursite.com/failed" // Redirect after failure
}Optional Fields
{
"contactId": "cnt_abc123", // Link to customer contact
"externalId": "order-12345", // Your internal reference
"metadata": { "orderId": "12345" }, // Custom data
"dueDate": "2025-11-14T23:59:59Z" // Payment deadline
}Example
async function createPayinOrder(token, orgId, merchantId) {
const response = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders`,
{
type: 'PAYIN',
originCurrencySymbol: 'COP',
destinationCurrencySymbol: 'COP',
amountIn: 50000,
description: 'Payment for Order #12345',
externalId: `order-${Date.now()}`,
paymentMethods: [
{
method: 'PSE',
extra: { bankAccount: { name: 'BANCOLOMBIA' } }
}
],
successUrl: 'https://yoursite.com/success',
failedUrl: 'https://yoursite.com/failed'
},
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
const order = await createPayinOrder(token, orgId, merchantId);
console.log('Payment URL:', order.paymentUrl);
// Redirect customer to order.paymentUrlComplete PAYIN Integration Guide →
PAYOUT - Pay Providers
Send payments from your virtual balance to external bank accounts.
When to Use
- Vendor payments
- Contractor payouts
- Refunds to customers
- Affiliate commissions
- Partner settlements
Flow Diagram
Required Fields
{
"type": "PAYOUT", // Order type
"originCurrencySymbol": "COP", // Fiat currency
"destinationCurrencySymbol": "COP", // Same as origin
"amountIn": 100000, // Amount to send
"destinationAccountId": "ba_xyz789", // Provider's bank account ID
"description": "Payment for Invoice #456" // Internal description
}Optional Fields
{
"contactId": "cnt_provider123", // Link to provider contact
"externalId": "invoice-456", // Your internal reference
"metadata": { "invoiceId": "456" } // Custom data
}Example
async function createPayoutOrder(token, orgId, merchantId, contactId, bankAccountId) {
// 1. Check balance first
const balances = await getMerchantBalances(token, orgId, merchantId);
const copBalance = balances.find(b => b.currencySymbol === 'COP');
if (copBalance.availableBalance < 100000) {
throw new Error('Insufficient balance');
}
// 2. Create payout order
const response = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders`,
{
type: 'PAYOUT',
originCurrencySymbol: 'COP',
destinationCurrencySymbol: 'COP',
amountIn: 100000,
contactId: contactId,
destinationAccountId: bankAccountId,
description: 'Payment for Invoice #456',
externalId: `invoice-456-${Date.now()}`
},
{
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
}
}
);
return response.data;
}
const payout = await createPayoutOrder(token, orgId, merchantId, 'cnt_provider', 'ba_xyz789');
console.log('Payout created:', payout.id);Important: Always check your virtual account balance before creating PAYOUT orders. The order will fail if you have insufficient funds.
Complete PAYOUT Integration Guide →
BALANCE_TRANSFER - Currency Exchange
Transfer funds between different currency virtual accounts (instant currency exchange).
When to Use
- Convert COP to USD for international payments
- Rebalance currency holdings
- Lock in exchange rates
- Prepare funds for specific currency payouts
Flow Diagram
Required Fields
{
"type": "BALANCE_TRANSFER", // Order type
"originCurrencySymbol": "COP", // Source currency
"destinationCurrencySymbol": "USD", // Target currency
"amountIn": 1000000 // Amount to convert (1M COP)
}Example
async function transferCurrency(token, orgId, merchantId) {
// 1. Get quote for exchange rate
const quote = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/quotes`,
{
type: 'BALANCE_TRANSFER',
originCurrencySymbol: 'COP',
destinationCurrencySymbol: 'USD',
amountIn: 1000000
},
{ headers: { 'Authorization': `Bearer ${token}` } }
);
console.log('Exchange rate:', quote.data.exchangeRate);
console.log('Will receive:', quote.data.amountOut, 'USD');
// 2. Create transfer order — passing quoteId locks rate & amount, so omit
// originCurrencySymbol / destinationCurrencySymbol / amountIn / amountOut
const response = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders`,
{
type: 'BALANCE_TRANSFER',
quoteId: quote.data.id
},
{ headers: { 'Authorization': `Bearer ${token}` } }
);
return response.data;
}
const transfer = await transferCurrency(token, orgId, merchantId);
console.log('Transfer completed instantly:', transfer.status); // "COMPLETED"Instant Settlement: BALANCE_TRANSFER orders complete instantly. No waiting for external confirmations.
Complete Balance Transfer Guide →
ONRAMP - Buy Cryptocurrency
Convert fiat currency to cryptocurrency.
Uses Deals Endpoint: ONRAMP operations use the /deals endpoint. Deals can be paid in full or partially, with each payment creating orders that execute the crypto purchase.
When to Use
- Buy crypto for treasury
- Offer crypto purchases to users
- Hedge with stablecoins
- Pay crypto-native providers
Supported Cryptocurrencies
| Symbol | Name | Networks |
|---|---|---|
| USDC | USD Coin | Ethereum, Polygon, BSC |
| USDT | Tether | Ethereum, Polygon, BSC |
| ETH | Ethereum | Ethereum |
| BTC | Bitcoin | Bitcoin |
| MATIC | Polygon | Polygon |
| BNB | BNB | BSC |
Required Fields for Quote
{
"type": "ONRAMP",
"originCurrencySymbol": "COP", // Fiat currency
"destinationCurrencySymbol": "USDC", // Crypto currency
"amountIn": 50000 // Fiat amount
}Required Fields for Deal
{
"type": "ONRAMP",
"destinationAccountId": "wallet_abc123", // Crypto wallet ID
"quoteId": "quote_xyz789" // Quote ID
}Simplified: Deals only need the destination account and quote ID. The amounts and currencies come from the quote.
Example
async function buyUSDC(token, orgId, merchantId, walletId) {
// 1. Get quote
const quote = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/quotes`,
{
type: 'ONRAMP',
originCurrencySymbol: 'COP',
destinationCurrencySymbol: 'USDC',
amountIn: 50000
},
{ headers: { 'Authorization': `Bearer ${token}` } }
);
console.log('Will receive:', quote.data.amountOut, 'USDC');
console.log('Exchange rate:', quote.data.exchangeRate);
console.log('Network fee:', quote.data.networkFee);
// 2. Create deal (not order!)
const response = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/deals`,
{
type: 'ONRAMP',
destinationAccountId: walletId,
quoteId: quote.data.id
},
{ headers: { 'Authorization': `Bearer ${token}` } }
);
return response.data;
}
const deal = await buyUSDC(token, orgId, merchantId, 'wallet_abc123');
console.log('ONRAMP deal created:', deal.id);
console.log('Deal can be paid in full or partially');Partial Payments: ONRAMP deals can be paid in multiple installments. Each payment creates an order that purchases the proportional amount of crypto. Great for dollar-cost averaging!
Balance Required: By default, sufficient balance in your virtual account is required to close an ONRAMP deal. Deals execute automatically when funds are credited to your account. Pre-approved merchants can operate without upfront balance.
OFFRAMP - Sell Cryptocurrency
Convert cryptocurrency to fiat currency.
Uses Deals Endpoint: OFFRAMP operations use the /deals endpoint. Unlike ONRAMP, OFFRAMP deals must be paid completely (no partial payments). The deal creates orders when fully funded.
When to Use
- Sell crypto holdings
- Convert crypto payments to fiat
- Realize crypto gains
- Prepare fiat for operations
Required Fields for Quote
{
"type": "OFFRAMP",
"originCurrencySymbol": "USDC", // Crypto currency
"destinationCurrencySymbol": "COP", // Fiat currency
"amountIn": 10 // Crypto amount (10 USDC)
}Required Fields for Deal
{
"type": "OFFRAMP",
"destinationAccountId": "va_cop_12345", // Virtual account ID
"quoteId": "quote_xyz789" // Quote ID
}Complete Payment Only: OFFRAMP deals must be funded completely. Partial payments are not supported.
Example
async function sellUSDC(token, orgId, merchantId) {
// 1. Get quote
const quote = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/quotes`,
{
type: 'OFFRAMP',
originCurrencySymbol: 'USDC',
destinationCurrencySymbol: 'COP',
amountIn: 10 // 10 USDC
},
{ headers: { 'Authorization': `Bearer ${token}` } }
);
console.log('Will receive:', quote.data.amountOut, 'COP');
// 2. Create deal (not order!)
const response = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/deals`,
{
type: 'OFFRAMP',
destinationAccountId: 'va_cop_12345', // Virtual account for COP
quoteId: quote.data.id
},
{ headers: { 'Authorization': `Bearer ${token}` } }
);
return response.data;
}
const deal = await sellUSDC(token, orgId, merchantId);
console.log('OFFRAMP deal created:', deal.id);
console.log('Deposit address:', deal.depositAddress);
console.log('Send full crypto amount to complete');
// Funds will be credited to COP virtual account after crypto receivedComplete Funding: After creating the deal, you must send the full crypto amount to the provided deposit address. Once received, the deal creates orders and credits your virtual account with fiat.
PAYMENT_LINK - Payment Links
Generate shareable payment links with a complete Koywe-branded checkout flow - no contact or payment method needed upfront!
What Makes PAYMENT_LINK Special?
Customer enters their own information in the checkout
Customer selects their preferred payment method
Beautiful, secure checkout hosted by Koywe
Send via email, WhatsApp, SMS, or QR code
When to Use
- E-commerce checkout: Simple payment collection without complex integration
- Invoice payments: Send payment link for issued invoices
- Email/WhatsApp: Share payment requests directly
- QR code payments: Generate QR for in-person payments
- One-time collections: Quick payment requests without storing customer data
How It Works
Differences from PAYIN
| Feature | PAYIN | PAYMENT_LINK |
|---|---|---|
| Contact | Optional but recommended | Not needed - customer enters data |
| Payment Method | Must be specified | Customer selects from all available |
| Checkout UI | Redirect to payment provider | Koywe-branded checkout flow |
| Data Collection | You collect customer data | Koywe collects customer data |
| Best For | Integrated checkout experience | Standalone payment requests |
Minimal Required Fields
That’s it! No contact, no payment method, no customer details needed. Just amount and description.
{
"type": "PAYMENT_LINK", // Order type
"originCurrencySymbol": "COP", // Currency
"destinationCurrencySymbol": "COP", // Same as origin
"amountIn": 75000, // Amount to collect
"description": "Invoice #789" // Shows to customer
}Optional Fields
{
"dueDate": "2025-11-14T23:59:59Z", // Link expiry
"externalId": "invoice-789", // Your reference
"successUrl": "https://yoursite.com/success", // Redirect after payment
"failedUrl": "https://yoursite.com/failed" // Redirect on failure
}Complete Example
// Minimal example - just create and share!
async function createPaymentLink(amount, description) {
const response = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders`,
{
type: 'PAYMENT_LINK',
originCurrencySymbol: 'COP',
destinationCurrencySymbol: 'COP',
amountIn: amount,
description: description
},
{ headers: { 'Authorization': `Bearer ${token}` } }
);
return response.data;
}
// Usage
const link = await createPaymentLink(75000, 'Invoice #789 - Web Design');
console.log('Share this link:', link.paymentUrl);
// Send via email, WhatsApp, SMS, or generate QR code
await sendEmail(customerEmail, {
subject: 'Payment Request - Invoice #789',
body: `Please pay here: ${link.paymentUrl}`
});// Full example with all options
async function createPaymentLinkWithOptions(invoiceData) {
const dueDate = new Date();
dueDate.setHours(dueDate.getHours() + 24); // Expires in 24h
const response = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${merchantId}/orders`,
{
type: 'PAYMENT_LINK',
originCurrencySymbol: 'COP',
destinationCurrencySymbol: 'COP',
amountIn: invoiceData.amount,
description: invoiceData.description,
externalId: invoiceData.invoiceNumber,
dueDate: dueDate.toISOString(),
successUrl: 'https://yoursite.com/payment/success',
failedUrl: 'https://yoursite.com/payment/failed'
},
{ headers: { 'Authorization': `Bearer ${token}` } }
);
return response.data;
}
// Usage
const link = await createPaymentLinkWithOptions({
amount: 150000,
description: 'Monthly Subscription - November 2025',
invoiceNumber: 'INV-2025-11-789'
});
console.log('Payment link:', link.paymentUrl);def create_payment_link(amount, description):
response = requests.post(
f'https://api-sandbox.koywe.com/api/v1/organizations/{org_id}/merchants/{merchant_id}/orders',
json={
'type': 'PAYMENT_LINK',
'originCurrencySymbol': 'COP',
'destinationCurrencySymbol': 'COP',
'amountIn': amount,
'description': description
},
headers={'Authorization': f'Bearer {token}'}
)
return response.json()
# Usage
link = create_payment_link(75000, 'Invoice #789 - Web Design')
print(f"Share this link: {link['paymentUrl']}")Response:
{
"id": "ord_abc123",
"type": "PAYMENT_LINK",
"status": "PENDING",
"paymentUrl": "https://checkout.koywe.com/pay/ord_abc123",
"amountIn": 75000,
"originCurrencySymbol": "COP",
"description": "Invoice #789 - Web Design",
"createdAt": "2025-11-13T15:00:00Z"
}Customer Experience: When customers open the payment link, they’ll see a Koywe-branded checkout where they can:
- Review payment details
- Enter their personal information
- Select their preferred payment method (PSE, PIX, Nequi, etc.)
- Complete the payment
Order Metadata and External IDs
External ID (Idempotency)
Use externalId to ensure idempotent order creation:
{
"externalId": "order-12345-20251113" // Your unique identifier
}Benefits:
- Safe to retry failed requests
- Prevents duplicate orders
- Links to your internal systems
Metadata
Store custom data with orders:
{
"metadata": {
"orderId": "12345",
"customerId": "cust_67890",
"source": "mobile_app",
"campaign": "summer_sale"
}
}Use cases:
- Track order source
- Store campaign information
- Link to internal systems
- Custom reporting fields
INTER_MERCHANT_TRANSFER - Move Funds Between Merchants
Move virtual-balance funds from one merchant to another merchant in the same organization, in the same currency, without hitting an external bank. Useful for treasury operations, fee settlement, and internal rebalancing across merchants you control.
When to Use
- Consolidating balances across your own merchants within one organization
- Settling internal commissions between a parent and child merchant
- Rebalancing liquidity so a specific merchant has enough funds for an upcoming payout
Flow Diagram
Required Fields
type—INTER_MERCHANT_TRANSFERdestinationMerchantId— Required. Merchant inside the same organization. Cross-organization transfers are rejected (IMT00002).originCurrencySymbol/destinationCurrencySymbol— must be the same currency (IMT00008; same-currency is MVP behaviour).amountInoramountOut— one of the two (same rule as the other/orderstypes).
Example — Create Order
const response = await axios.post(
`https://api-sandbox.koywe.com/api/v1/organizations/${orgId}/merchants/${sourceMerchantId}/orders`,
{
type: 'INTER_MERCHANT_TRANSFER',
destinationMerchantId: 'mer_destination_abc',
originCurrencySymbol: 'USD',
destinationCurrencySymbol: 'USD',
amountIn: 1000,
description: 'July commission settlement'
},
{ headers: { 'Authorization': `Bearer ${token}` } }
);
// Sandbox completes instantly like BALANCE_TRANSFER:
console.log(response.data.status); // "COMPLETED"Example — Response
{
"id": "ord_imt_xyz789",
"type": "INTER_MERCHANT_TRANSFER",
"status": "COMPLETED",
"originMerchantId": "mer_source_123",
"destinationMerchantId": "mer_destination_abc",
"originCurrencySymbol": "USD",
"destinationCurrencySymbol": "USD",
"amountIn": 1000,
"amountOut": 1000,
"description": "July commission settlement",
"createdAt": "2026-04-24T12:00:00Z",
"completedAt": "2026-04-24T12:00:01Z"
}Listing & Filtering
The OpenAPI spec’s orders list filter enum omits INTER_MERCHANT_TRANSFER today. Despite that, the query parameter ?type=INTER_MERCHANT_TRANSFER does filter correctly against the API — the omission is a spec gap, not a runtime one.
# Via CLI (the CLI reflects all 7 types correctly):
npx @koyweforest/cli orders list --type INTER_MERCHANT_TRANSFER --format table
# Via raw HTTP:
curl -H "Authorization: Bearer $TOKEN" \
"https://api-sandbox.koywe.com/api/v1/organizations/$ORG/merchants/$MERCHANT/orders?type=INTER_MERCHANT_TRANSFER"Common Errors
| Code | Cause | Fix |
|---|---|---|
IMT00001 | Destination merchant not found | Check destinationMerchantId exists |
IMT00002 | Destination merchant is in a different organization | Transfers are restricted to same-org merchants |
IMT00003 | Source and destination merchant are the same | Use BALANCE_TRANSFER for same-merchant currency exchange |
IMT00004 | Destination merchant is disabled | Enable the merchant or pick another destination |
IMT00005 | Insufficient balance in source merchant account | Fund the source account or reduce the amount |
IMT00007 | destinationMerchantId missing | Required field for INTER_MERCHANT_TRANSFER |
IMT00008 | Origin and destination currency don’t match | Same-currency only (MVP) |
See the full Error Code Catalog for every IMT* code.