Integrate Payoneer with Bolt.new using the Payoneer REST API through an API route that keeps your API credentials server-side. Payoneer specializes in cross-border B2B payments and mass payouts to international contractors — the right choice when your platform pays workers or suppliers globally rather than collecting payments from customers. API access requires a Payoneer business account with developer access approval. All API calls use HTTPS and work from the WebContainer for outbound requests.
Build Global Contractor Payout Flows in Your Bolt.new App with Payoneer
Payoneer solves a specific problem that Stripe doesn't: paying international workers, freelancers, and suppliers at scale across 200+ countries. While Stripe and Braintree are primarily designed for collecting payments from customers, Payoneer is built for disbursement — sending money out to payees globally. If you're building a freelance marketplace, staffing platform, cross-border supplier network, or any app where your business regularly pays people in different countries, Payoneer is the specialized tool for that workflow.
The Payoneer API enables your Bolt.new app to programmatically register payees (the recipients who will be paid), check their verification status, create payment requests, and trigger mass payouts. Payees register their own Payoneer accounts to receive funds — they choose how they want to be paid (bank transfer, prepaid card, digital wallet) based on what's available in their country. Your platform simply sends the payment to their Payoneer account, and Payoneer handles local disbursement. This removes the complexity of managing country-specific banking relationships, exchange rates, and compliance requirements.
Payoneer API access is not instant — it requires a business account in good standing and an application to the Payoneer developer program. This is standard for platforms handling B2B financial flows. Once approved, you receive a program ID (identifies your platform), a username, and an API key for authentication. The API uses OAuth 2.0 with client credentials, making it compatible with Bolt.new's API routes — all calls are standard HTTPS requests that work from the WebContainer during development. Plan for the application process when evaluating Payoneer for your project timeline.
Integration method
Payoneer integrates with Bolt.new through API routes that authenticate using OAuth 2.0 (client credentials flow) and call Payoneer's REST API for payee management and payout initiation. The OAuth token request and all payout API calls are outbound HTTPS requests that work from the WebContainer during development. Payoneer's webhooks for payout status updates require a deployed public URL. API access requires a Payoneer business account with approved developer access, which involves an application process.
Prerequisites
- A Payoneer business account (payoneer.com) with verified business status
- Approved Payoneer developer access — submit an application through developer.payoneer.com describing your use case
- Payoneer API credentials: Program ID, username, and API key (provided after developer access approval)
- A Bolt.new project with Next.js API routes for server-side Payoneer API calls
- Payee Payoneer accounts — recipients must have their own Payoneer accounts to receive payments
Step-by-step guide
Apply for Payoneer API Access and Get Credentials
Apply for Payoneer API Access and Get Credentials
Payoneer API access requires an approved developer application — unlike Stripe which provides instant test keys, Payoneer evaluates your use case before granting API access. This is standard for financial platforms handling cross-border disbursements and is part of Payoneer's compliance requirements. Start by registering a business account at payoneer.com if you don't have one. Once your account is verified with business documents, go to developer.payoneer.com and submit a developer access application. You'll need to describe your platform (marketplace, staffing, content platform, etc.), the expected volume of payouts, the countries your payees are in, and your technical integration plan. Payoneer reviews applications and typically responds within a few business days. Upon approval, you receive three credentials: a Program ID (numeric identifier for your platform on Payoneer's system), a Username (your API authentication username), and an API Key (your API authentication password). These credentials authenticate using HTTP Basic Auth for OAuth 2.0 token requests. The Payoneer API has two environments: sandbox at `https://api.sandbox.payoneer.com` and production at `https://api.payoneer.com`. Store both your credentials and the environment base URL in your Bolt project's `.env` file. Note that Payoneer's sandbox is less fully-featured than production — some payout flows may behave differently.
1# .env — Payoneer API credentials2# Received from Payoneer after developer access approval3PAYONEER_PROGRAM_ID=your-program-id4PAYONEER_USERNAME=your-api-username5PAYONEER_API_KEY=your-api-key67# API base URLs8PAYONEER_API_BASE=https://api.sandbox.payoneer.com # sandbox9# PAYONEER_API_BASE=https://api.payoneer.com # production1011# For Next.js server-side: process.env.PAYONEER_API_KEY12# Never use VITE_ prefix or NEXT_PUBLIC_ — these are secret server-side credentialsPro tip: While waiting for Payoneer developer access approval, build and test your UI components with mock API responses. Payoneer's API structure is well-documented at developer.payoneer.com — you can design the full integration before receiving credentials.
Expected result: You have Payoneer API credentials stored in your .env file and access to the Payoneer sandbox environment for testing.
Implement OAuth 2.0 Token Authentication
Implement OAuth 2.0 Token Authentication
Payoneer uses OAuth 2.0 client credentials flow for API authentication. Before calling any Payoneer API endpoint, you must request an access token by sending your Program ID, username, and API key to Payoneer's token endpoint. The access token is a Bearer token that you include in the Authorization header of subsequent API requests. Access tokens expire (typically after 3600 seconds — 1 hour) and must be refreshed. For a production implementation, cache the access token in memory or in your database and only request a new one when it expires. For development purposes in Bolt, requesting a fresh token on each API call is simpler to implement and debug. The token endpoint uses HTTP Basic Auth: the username is your Payoneer username and the password is your API key, encoded as a Base64 `username:password` string in the Authorization header. This is a standard HTTPS request that works perfectly from Bolt's WebContainer — no native modules required, just fetch() with the right headers. Create a server-side utility function `getPayoneerToken()` that handles token acquisition and caching, then use it from your payout API routes.
Create a Payoneer authentication utility at lib/payoneer.ts. Add a getPayoneerToken() function that requests an OAuth 2.0 access token from the Payoneer token endpoint using the PAYONEER_USERNAME and PAYONEER_API_KEY environment variables. Cache the token in memory with its expiry time and return the cached token if it's still valid. Export a payoneerFetch(path, options) helper that automatically gets a token and adds the Authorization: Bearer header to requests targeting the PAYONEER_API_BASE URL.
Paste this in Bolt.new chat
1// lib/payoneer.ts — server-side only2let cachedToken: { access_token: string; expires_at: number } | null = null;34export async function getPayoneerToken(): Promise<string> {5 // Return cached token if still valid (with 60s buffer)6 if (cachedToken && Date.now() < cachedToken.expires_at - 60_000) {7 return cachedToken.access_token;8 }910 const username = process.env.PAYONEER_USERNAME!;11 const apiKey = process.env.PAYONEER_API_KEY!;12 const programId = process.env.PAYONEER_PROGRAM_ID!;13 const apiBase = process.env.PAYONEER_API_BASE!;1415 const credentials = Buffer.from(`${username}:${apiKey}`).toString('base64');1617 const response = await fetch(`${apiBase}/v4/oauth2/token`, {18 method: 'POST',19 headers: {20 'Authorization': `Basic ${credentials}`,21 'Content-Type': 'application/x-www-form-urlencoded',22 },23 body: new URLSearchParams({24 grant_type: 'client_credentials',25 scope: `read:payments write:payments read:payees write:payees`,26 }),27 });2829 if (!response.ok) {30 throw new Error(`Payoneer auth failed: ${response.status} ${await response.text()}`);31 }3233 const data = await response.json();34 cachedToken = {35 access_token: data.access_token,36 expires_at: Date.now() + (data.expires_in * 1000),37 };3839 return data.access_token;40}4142export async function payoneerFetch(43 path: string,44 options: RequestInit = {}45): Promise<Response> {46 const token = await getPayoneerToken();47 const apiBase = process.env.PAYONEER_API_BASE!;4849 return fetch(`${apiBase}${path}`, {50 ...options,51 headers: {52 'Authorization': `Bearer ${token}`,53 'Content-Type': 'application/json',54 ...options.headers,55 },56 });57}Pro tip: Module-level token caching (as shown) works well for serverless functions with warm instances. For cold-start-heavy deployments where each request is a fresh instance, consider caching the token in Supabase or Redis to avoid an extra round-trip on every payment request.
Expected result: Calling getPayoneerToken() returns a valid Payoneer Bearer token. Calling payoneerFetch() automatically handles authentication for any Payoneer API endpoint.
Create Payment Requests and Initiate Payouts
Create Payment Requests and Initiate Payouts
With authentication handled, build the API route that creates payment requests in Payoneer. Payoneer's payment flow distinguishes between a payment request (creating a payment record) and the actual fund transfer. For mass payouts, the payees must have existing Payoneer accounts registered with the email addresses you use. The API creates a payment transaction specifying the payee's Payoneer account identifier (typically their email address associated with their Payoneer account), the payment amount in USD or the specified currency, an internal reference ID for your records, and an optional memo or description visible to the payee. Payoneer processes the payment asynchronously — the API accepts the payment request immediately and returns a payment ID, then processes the actual fund transfer in the background. The final payment status (completed, declined, on hold) arrives via webhook or can be polled via the payments status endpoint. Build the API route to handle single payments for individual contractor payouts and optionally batch payments for mass disbursements. All Payoneer API calls use HTTPS and work from Bolt's WebContainer for the outbound requests — the payment creation happens synchronously in your API route and returns quickly with a payment ID.
Create a Payoneer payment API route at app/api/payoneer/send-payment/route.ts. Accept POST requests with: payee_email (string), amount (number), currency (string, default 'USD'), description (string), and internal_reference_id (string). Use the payoneerFetch helper from lib/payoneer.ts to call the Payoneer payments endpoint and create a payment request. Return the Payoneer payment_id and status. Also update the corresponding record in Supabase with payment_id and status 'processing'.
Paste this in Bolt.new chat
1// app/api/payoneer/send-payment/route.ts2import { NextResponse } from 'next/server';3import { payoneerFetch } from '@/lib/payoneer';4import { createClient } from '@supabase/supabase-js';56export async function POST(request: Request) {7 const {8 payee_email,9 amount,10 currency = 'USD',11 description,12 internal_reference_id,13 } = await request.json();1415 if (!payee_email || !amount || !internal_reference_id) {16 return NextResponse.json(17 { error: 'payee_email, amount, and internal_reference_id are required' },18 { status: 400 }19 );20 }2122 const paymentPayload = {23 client_reference_id: internal_reference_id,24 description,25 payee: {26 id: {27 type: 'email',28 value: payee_email,29 },30 },31 amount: {32 value: amount,33 currency,34 },35 };3637 const response = await payoneerFetch('/v4/payments', {38 method: 'POST',39 body: JSON.stringify(paymentPayload),40 });4142 if (!response.ok) {43 const errorData = await response.json().catch(() => ({}));44 return NextResponse.json(45 { error: 'Payoneer payment failed', details: errorData },46 { status: response.status }47 );48 }4950 const paymentData = await response.json();5152 // Update Supabase record with Payoneer payment details53 const supabase = createClient(54 process.env.NEXT_PUBLIC_SUPABASE_URL!,55 process.env.SUPABASE_SERVICE_ROLE_KEY!56 );5758 await supabase59 .from('payments')60 .update({61 payoneer_payment_id: paymentData.id,62 status: 'processing',63 initiated_at: new Date().toISOString(),64 })65 .eq('reference_id', internal_reference_id);6667 return NextResponse.json({68 success: true,69 payment_id: paymentData.id,70 status: paymentData.status,71 });72}Pro tip: Payoneer's client_reference_id is your internal order or payment ID — it appears in reports and is used for reconciliation. Use a unique, stable identifier like your Supabase record UUID rather than a timestamp.
Expected result: Calling POST /api/payoneer/send-payment with payee details creates a Payoneer payment request and returns a payment ID. The Supabase record is updated with the Payoneer payment ID and processing status.
Build a Payout Dashboard and Handle Status Webhooks
Build a Payout Dashboard and Handle Status Webhooks
A payout management dashboard gives you visibility into payment status across all your payees — which payments are processing, which completed successfully, and which encountered issues. Build a server component that fetches payment records from Supabase (which stores your payments along with Payoneer payment IDs and statuses) and displays them with filtering and search. For real-time status updates, Payoneer sends webhook notifications when payment status changes — a payment moves from 'processing' to 'completed' or 'declined'. Configure your webhook endpoint URL in the Payoneer developer portal. The webhook handler receives a JSON payload with the payment ID, new status, and event type. Your handler updates the corresponding Supabase record and can trigger downstream actions — sending confirmation emails, releasing funds in escrow, updating user dashboards. Like all webhook-based integrations, Payoneer webhooks are server-to-server HTTP calls that cannot reach Bolt's WebContainer browser-local environment. Deploy to Netlify or Bolt Cloud first, then register your webhook URL in the Payoneer partner portal. For development, you can poll the payment status endpoint (`GET /v4/payments/{paymentId}`) to check status without webhooks during testing.
Create a payout dashboard page at app/admin/payouts/page.tsx. Fetch all payment records from the Supabase 'payments' table sorted by created_at descending. Display them in a table with columns: payee email, amount, currency, status (color-coded: green for completed, yellow for processing, red for failed), Payoneer payment ID, and initiated date. Add a status filter dropdown (All/Processing/Completed/Failed). Also create a webhook handler at app/api/payoneer/webhook/route.ts that updates payment status in Supabase when Payoneer sends a status change notification.
Paste this in Bolt.new chat
1// app/api/payoneer/webhook/route.ts2import { NextRequest, NextResponse } from 'next/server';3import { createClient } from '@supabase/supabase-js';45export async function POST(request: NextRequest) {6 // Payoneer webhook payload7 const body = await request.json();89 const { event_type, payment } = body;1011 if (!payment?.id) {12 return NextResponse.json({ received: true });13 }1415 const supabase = createClient(16 process.env.NEXT_PUBLIC_SUPABASE_URL!,17 process.env.SUPABASE_SERVICE_ROLE_KEY!18 );1920 let status: string;21 switch (event_type) {22 case 'PAYMENT_COMPLETED':23 status = 'completed';24 break;25 case 'PAYMENT_DECLINED':26 status = 'failed';27 break;28 case 'PAYMENT_ON_HOLD':29 status = 'on_hold';30 break;31 default:32 status = 'processing';33 }3435 await supabase36 .from('payments')37 .update({38 status,39 payoneer_status: payment.status,40 completed_at: status === 'completed' ? new Date().toISOString() : null,41 updated_at: new Date().toISOString(),42 })43 .eq('payoneer_payment_id', payment.id);4445 return NextResponse.json({ received: true });46}Pro tip: Configure the webhook URL in the Payoneer partner portal after deploying. During development, use the Payoneer API to poll GET /v4/payments/{id} to check payment status manually — this works from the WebContainer without needing a public URL.
Expected result: The admin payout dashboard shows all payment records with color-coded statuses. After deploying and configuring the webhook URL, Payoneer sends status updates that automatically update payment records in Supabase.
Common use cases
Freelance Marketplace Contractor Payouts
Build a platform where clients post projects, freelancers complete work, and your app automatically releases payment to the freelancer's Payoneer account upon client approval. The payout dashboard shows pending payments, processing status, and completion confirmations.
Build a payout management system for my freelance marketplace. Create an API route that initiates a Payoneer payment to a contractor's email address (their Payoneer account) when a project is marked as completed. Accept: contractor_email, amount, currency, and project_id. Store payment records in Supabase with status tracking. Show a /admin/payouts dashboard with pending, processing, and completed payments sorted by date.
Copy this prompt to try it in Bolt.new
Mass Payout to Survey or Content Respondents
Send small payments (micro-payments) to hundreds of users who completed surveys, content tasks, or market research. Payoneer's mass payout API batches multiple payment requests in a single API call, making bulk disbursements efficient.
Create a bulk payout feature that sends Payoneer payments to all survey respondents who haven't been paid yet. Fetch unpaid respondents from Supabase where paid_at is null, group them into batches of 50, and call the Payoneer mass payout API for each batch. Update Supabase with the Payoneer payment ID and set paid_at timestamp. Show progress in a /admin/mass-payout page with success/failure counts.
Copy this prompt to try it in Bolt.new
Supplier Payment Automation
Automate monthly payments to international suppliers based on invoice data stored in your system. The payment runs automatically on a schedule, reads approved invoices from Supabase, and triggers Payoneer payouts for each supplier.
Build an automated supplier payment system. Create an API route at /api/payoneer/process-invoices that fetches all approved invoices from Supabase where status is 'approved' and payment_method is 'payoneer'. For each invoice, call the Payoneer payment API to send the invoice amount to the supplier's Payoneer account stored in their profile. Update invoice status to 'payment_initiated' with the Payoneer payment ID. Log all payment attempts.
Copy this prompt to try it in Bolt.new
Troubleshooting
Payoneer token request returns 401 Unauthorized
Cause: The Basic Auth credentials are incorrect — the username or API key doesn't match the Payoneer developer portal, or the credentials are for a different environment (sandbox vs production).
Solution: Verify your PAYONEER_USERNAME and PAYONEER_API_KEY in .env match exactly what's shown in the Payoneer developer portal. Confirm PAYONEER_API_BASE points to the correct environment URL (sandbox.payoneer.com for sandbox, api.payoneer.com for production). Check that the Base64 encoding is correct — use Buffer.from('username:apikey').toString('base64') in Node.js.
1// Debug authentication encoding2const credentials = Buffer.from(`${process.env.PAYONEER_USERNAME}:${process.env.PAYONEER_API_KEY}`).toString('base64');3console.log('Auth header:', `Basic ${credentials}`);Payment creation returns 'Payee not found' or 404 for the payee
Cause: The payee's email address is not associated with an active Payoneer account, or the payee hasn't completed Payoneer registration and identity verification.
Solution: The payee must have an active, verified Payoneer account registered with the email address you're sending the payment to. Send the payee a Payoneer registration invitation through your platform before attempting to pay them. Check the payee's registration status via the Payoneer API GET /v4/payees endpoint before initiating payments.
Payoneer webhooks are not arriving at the deployed endpoint
Cause: The webhook URL is misconfigured in the Payoneer partner portal, or webhooks are configured for the wrong environment (sandbox vs production). The WebContainer preview URL cannot receive Payoneer webhooks.
Solution: Verify the webhook URL in the Payoneer partner portal points to your deployed endpoint (not the WebContainer preview URL). Ensure the deployed app is accessible at the configured URL. Use Payoneer's webhook test tool in the portal to send a test notification and verify receipt. Webhooks require a publicly accessible HTTPS URL — deploy to Netlify or Bolt Cloud first.
Best practices
- Never expose Payoneer API credentials (username, API key, program ID) in client-side code — all Payoneer API calls must go through server-side API routes
- Cache OAuth access tokens with their expiry times to avoid requesting a new token on every API call — tokens typically last 3600 seconds
- Store the Payoneer payment ID returned on creation and use it for status polling and reconciliation — it's your primary reference for the payment lifecycle
- Verify payee registration status before initiating payouts to avoid failed payment requests for unregistered email addresses
- Use idempotent client_reference_ids tied to your database record IDs to safely retry failed payment requests without creating duplicate payments
- Deploy before testing webhooks — Payoneer sends server-to-server notifications that cannot reach the WebContainer's browser-local environment
- Build a retry mechanism for failed payouts with exponential backoff — network issues and temporary Payoneer service interruptions are handled gracefully with retries
Alternatives
PayPal Payouts has broader consumer recognition and simpler API access — better when payees prefer PayPal over creating a dedicated Payoneer account.
Stripe Connect handles marketplace payment splitting and payouts with better developer experience and instant API access — the first choice for new platforms without existing Payoneer relationships.
Wave is a free accounting and invoicing platform with payment features suited for small businesses managing contractor invoices rather than automated mass payouts.
Frequently asked questions
How do I get Payoneer API access for my Bolt.new app?
Register a business account at payoneer.com with verified business documents, then apply for developer access at developer.payoneer.com describing your platform and use case. Payoneer reviews applications and typically responds within several business days. API access is not instant like Stripe — factor this approval timeline into your development schedule. Once approved, you receive a Program ID, username, and API key.
Can I test Payoneer API calls from Bolt's WebContainer preview?
Yes, for outbound API calls — creating payment requests, fetching payment status, and listing payees are all standard HTTPS requests that work from the WebContainer. The limitation is for webhooks: Payoneer sends status change notifications (payment completed, declined) as server-to-server HTTP calls that cannot reach the WebContainer's browser-local environment. Deploy to Netlify or Bolt Cloud to receive webhook notifications, or poll the status API during development.
How is Payoneer different from Stripe for a Bolt.new app?
Stripe is primarily for collecting payments from customers (inbound money flow). Payoneer is for paying people — contractors, suppliers, sellers, survey respondents (outbound money flow). Stripe has native Bolt.new integration with instant API access; Payoneer requires an application process and manual API route setup. Choose Stripe for collecting payments, Payoneer for paying international workers and suppliers at scale.
Do payees need a Payoneer account to receive payments through my Bolt app?
Yes. Payees must have an active, verified Payoneer account registered with the email address you send payments to. Your platform should include a payee onboarding flow that prompts contractors or suppliers to create a Payoneer account before they can receive payments. You can check payee registration status via the Payoneer API before initiating a payment.
Does the Payoneer npm package work in Bolt's WebContainer?
There is no official Payoneer Node.js SDK as a widely-used npm package — the integration uses direct HTTP calls to the REST API using fetch(). This is actually simpler: the payoneerFetch() helper function covers all API calls without any npm dependency. Your only dependency is the built-in fetch API and the Buffer class, both available in Node.js without any installation.
Talk to an Expert
Our team has built 600+ apps. Get personalized help with your project.
Book a free consultation