Create Whitelabel Checkout
Overview
Create a checkout and initiate payment in a single API request. This endpoint is designed for headless or embedded checkout flows where you handle the payment UI yourself instead of redirecting to SyncPay's hosted page.
Use this endpoint when:
- Building a custom checkout experience within your app
- You want full control over the payment UI
- Creating embedded payment flows
- Building mobile app integrations
Key difference from standard checkout: This endpoint returns payment instructions directly instead of a checkout URL.
Authentication
Type: API Key (required)
Required Headers
| Header | Required | Description |
|---|---|---|
Authorization |
Yes | Format: Bearer sk_test_... or Bearer sk_live_... |
Content-Type |
Yes | Must be application/json |
Request
Method & Path
POST /api/v1/payments/whitelabel
Request Body
{
"pricing": {
"base_currency": "USD",
"amount": "50.00"
},
"customer_email": "customer@example.com",
"customer_name": "John Doe",
"quote_id": "quote_1a2b3c4d5e6f",
"payment_method": "bank_transfer",
"phone_number": "+2348012345678",
"bank_account_number": "0123456789",
"success_url": "https://yoursite.com/payment/success",
"cancel_url": "https://yoursite.com/payment/cancelled",
"metadata": {
"order_id": "ORD-12345"
},
"expires_in_minutes": 60
}
Request Fields
| Field | Type | Required | Description |
|---|---|---|---|
pricing |
object | Yes | Pricing configuration (same as standard checkout) |
pricing.base_currency |
string | Yes | Base currency code |
pricing.amount |
string | Yes | Amount in base currency |
pricing.local_pricing |
object | No | Currency-specific amounts (USD base only) |
customer_email |
string | Yes | Customer's email address |
customer_name |
string | No | Customer's full name |
quote_id |
string | Yes | Quote ID from Create Quote |
payment_method |
string | Yes | Payment method ID (required if not in quote) |
phone_number |
string | Conditional | Required for mobile money payments |
bank_account_number |
string | Conditional | Required for bank transfer payments |
success_url |
string | No | Success redirect URL |
cancel_url |
string | No | Cancel redirect URL |
metadata |
object | No | Custom metadata (max 20 keys, 10KB) |
expires_in_minutes |
integer | No | Expiration time (1-1440, default: 60) |
Dependencies & Prerequisites
This endpoint requires several steps before use:
Required Steps (in order):
- Get payment methods - Use List Payment Methods
- Create a quote - Use Create Quote to get
quote_id - Collect payment details - Get customer's phone number or bank account
- Call this endpoint - Create and initiate payment in one request
Links:
- See: Create Quote for generating
quote_id - See: List Payment Methods for valid payment methods
Example Use Case
Scenario: Your fintech application enables peer-to-peer money transfers. Users need to send money directly within your app without being redirected to an external payment page. You want to maintain your brand experience while processing bank transfers, showing payment instructions directly in your interface, and handling the complete payment flow natively.
Implementation Flow:
// Step 1: Get a quote for the payment
const quoteResponse = await fetch('https://api.usesyncpay.com/api/v1/payments/quotes', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_abc123xyz...',
'Content-Type': 'application/json'
},
body: JSON.stringify({
pricing: {
base_currency: 'USD',
amount: '100.00'
},
payment_method: 'bank_transfer',
to_currency: 'NGN',
customer_email: 'user@example.com'
})
});
const quote = await quoteResponse.json();
// Step 2: Show customer the quote amount (NGN 150,000)
displayQuote({
amount: quote.amount,
currency: quote.currency,
exchangeRate: quote.exchange_rate
});
// Step 3: Customer enters bank account number in your UI
const bankAccount = await getUserBankAccount(); // "0123456789"
// Step 4: Create whitelabel checkout and initiate payment
const paymentResponse = await fetch('https://api.usesyncpay.com/api/v1/payments/whitelabel', {
method: 'POST',
headers: {
'Authorization': 'Bearer sk_live_abc123xyz...',
'Content-Type': 'application/json'
},
body: JSON.stringify({
pricing: {
base_currency: 'USD',
amount: '100.00'
},
customer_email: 'user@example.com',
customer_name: 'Jane Doe',
quote_id: quote.quote_id,
payment_method: 'bank_transfer',
bank_account_number: bankAccount,
metadata: {
transaction_id: 'TXN-789'
}
})
});
const payment = await paymentResponse.json();
// Step 5: Display payment instructions to customer
displayPaymentInstructions({
status: payment.status,
amount: payment.amount,
currency: payment.currency,
instructions: payment.payment_instructions,
reference: payment.provider_reference
});
Response
200 – Success
Returns payment details and instructions for the customer.
{
"checkout_id": "chk_1a2b3c4d5e6f",
"charge_id": "chr_9x8y7z6w5v",
"status": "PENDING",
"amount": "150000.00",
"currency": "NGN",
"payment_method": "bank_transfer",
"payment_provider": null,
"provider_reference": "REF-12345",
"payment_instructions": {
"type": "bank_transfer",
"bank_name": "Access Bank",
"account_number": "9876543210",
"account_name": "SyncPay Collections",
"reference": "REF-12345",
"instructions": "Transfer NGN 150,000 to the account above using the reference code"
},
"expires_at": "2026-01-24T15:30:00.000Z",
"created_at": "2026-01-24T14:30:00.000Z"
}
Response Fields
| Field | Type | Description |
|---|---|---|
checkout_id |
string | Checkout session ID |
charge_id |
string | Charge/payment ID for tracking |
status |
string | Payment status (PENDING, AWAITING_PAYMENT, etc.) |
amount |
string | Amount customer should pay |
currency |
string | Payment currency |
payment_method |
string | Payment method used |
payment_provider |
string|null | Payment provider (not exposed for security) |
provider_reference |
string | Transaction reference ID |
payment_instructions |
object | Instructions to display to customer |
expires_at |
string | ISO 8601 timestamp for payment expiration |
created_at |
string | ISO 8601 timestamp of creation |
Payment Instructions Format
The payment_instructions object varies by payment method:
Bank Transfer Instructions
{
"type": "bank_transfer",
"bank_name": "Access Bank",
"account_number": "9876543210",
"account_name": "SyncPay Collections",
"reference": "KORA-REF-12345",
"instructions": "Transfer NGN 150,000 to the account above using the reference code"
}
Mobile Money Instructions
{
"type": "mobile_money",
"provider": "MTN", // Mobile money provider (customer-facing)
"phone_number": "+233200123456",
"reference": "MM-REF-12345",
"instructions": "Send GHS 620 to the number above with reference MM-REF-12345"
}
Crypto Wallet Instructions
{
"type": "crypto",
"network": "TRC20",
"wallet_address": "TXY7GhbJ9kLmN3oPqR5sTuV8wXyZ1aB2cD",
"amount": "50.00",
"currency": "USDT",
"reference": "CRYPTO-REF-12345",
"instructions": "Send exactly 50.00 USDT (TRC20) to the address above"
}
400 – Bad Request
Validation errors or invalid combinations.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "Invalid request parameters",
"details": {
"quote_id": "Quote expired or not found. Please get a new quote.",
"payment_method": "payment_method does not match the quote"
}
}
}
Common causes:
- Quote expired (quotes are valid for 30 seconds)
- Missing required payment details (
phone_numberfor mobile money,bank_account_numberfor bank transfer) payment_methoddoesn't match the quotepayment_raildoesn't match the quote- Invalid quote ID
401 – Unauthorized
{
"error": {
"code": "UNAUTHORIZED",
"message": "Invalid API key"
}
}
Payment Methods & Required Fields
Different payment methods require different fields:
Bank Transfer
Required fields:
bank_account_number- Customer's bank account
Mobile Money
Required fields:
phone_number- Customer's mobile money number (with country code)
Cryptocurrency
Required fields:
quote_id- Must include wallet details
Optional fields:
- None (wallet address is in quote)
Important Notes
Quote Requirement
Unlike standard checkout, whitelabel checkout requires a valid quote:
- Quotes expire after 30 seconds
- If quote expires, you'll get a 400 error
- Generate a new quote and retry
Why? Whitelabel checkout needs locked-in rates and payment details from the quote.
Payment Reconciliation
Payments are reconciled using the provider_reference (transaction reference):
- Customer must include this reference when making payment
- Without the reference, payment may be delayed or lost
- Display the reference prominently in your UI
Status Updates
The initial status is usually PENDING or AWAITING_PAYMENT:
- Poll Get Charge Status to check for updates
- Or set up webhooks to receive automatic notifications (recommended)
- See: Webhook Documentation
Handling Different Payment Flows
Flow 1: Immediate Payment (Mobile Money)
// 1. Create whitelabel checkout
const payment = await createWhitelabelCheckout({
payment_method: 'mobile_money',
phone_number: '+233200123456',
// ...
});
// 2. Customer receives mobile money prompt on their phone
// 3. Customer approves payment on their phone
// 4. You receive webhook notification
// 5. Update your UI based on webhook
Flow 2: Manual Transfer (Bank Transfer)
// 1. Create whitelabel checkout
const payment = await createWhitelabelCheckout({
payment_method: 'bank_transfer',
bank_account_number: '0123456789',
// ...
});
// 2. Display bank account details to customer
displayBankDetails({
bankName: payment.payment_instructions.bank_name,
accountNumber: payment.payment_instructions.account_number,
reference: payment.payment_instructions.reference,
amount: payment.amount
});
// 3. Customer makes manual bank transfer
// 4. Payment is detected and processed
// 5. You receive webhook notification
// 6. Update customer's order status
Flow 3: Crypto Payment
// 1. Create whitelabel checkout
const payment = await createWhitelabelCheckout({
payment_method: 'crypto',
// ...
});
// 2. Display wallet address and amount
displayCryptoPayment({
address: payment.payment_instructions.wallet_address,
amount: payment.payment_instructions.amount,
currency: payment.payment_instructions.currency,
network: payment.payment_instructions.network
});
// 3. Customer sends crypto to address
// 4. Blockchain confirms transaction
// 5. You receive webhook notification
Testing
Test Mode Behavior
In test mode (sk_test_...):
- Whitelabel checkouts are created successfully
- Payment instructions are returned (simulated)
- Webhook events are still sent for testing
Simulating Payment Success
To test successful payments in test mode:
- Create whitelabel checkout
- Note the
charge_id - Contact support to manually mark as paid (for testing)
- Or wait for webhook test events
Comparison: Standard vs Whitelabel Checkout
| Feature | Standard Checkout | Whitelabel Checkout |
|---|---|---|
| UI | SyncPay hosted page | Your custom UI |
| Redirect | Yes, to SyncPay | No redirect |
| Quote | Optional | Required |
| Payment details | Collected by SyncPay | Collected by you |
| Branding | SyncPay branding | Your branding |
| Mobile apps | Web view required | Native integration |
| Complexity | Simple | Advanced |
| Best for | Quick integration | Custom experiences |
Related Endpoints
- Create Quote - Required before whitelabel checkout
- Create Checkout - Alternative hosted checkout
- Get Charge Status - Poll payment status
- Webhook Events - Receive automatic updates
Next Steps
After creating a whitelabel checkout:
- Display payment instructions to your customer
- Set up webhooks to receive payment updates automatically
- Poll charge status using Get Charge Status if not using webhooks
- Handle payment completion in your application logic