osuvox Payments API
The integrated payments API for server-side checkout flows, webhooks, and split payouts.
Overview
osuvox.net is a cryptocurrency payment processor that enables you to accept Bitcoin (BTC), Litecoin (LTC), and Dogecoin (DOGE) payments through a simple REST API.
https://www.osuvox.net/api/v1Monitoring runs internally via the worker/queue system and is not exposed as a public API endpoint. Status changes are communicated to merchants exclusively via webhooks.
TXID guarantee: The transaction ID is guaranteed in payment.confirmed and payment.completed webhooks. payment.detected may have a null txid
until confirmations progress.
Looking for hosted checkout or a no-code option? See osuvox Hosted Checkout or osuvox Links.
Hosted Checkout vs Full Integration
Hosted Checkout
- Redirect to
payment_url - No custom checkout UI required
- Fastest path to launch
- Fulfillment via webhooks
Full Integration
- Render address/QR yourself
- Full UI control
- More implementation work
- Fulfillment via webhooks
Key Features
- Unique addresses - Each payment gets its own receiving address
- Real-time webhooks - Get notified instantly when payments arrive
- Split payments - Automatically distribute funds to multiple wallets
- Internal monitoring - Monitoring runs via the worker/queue system; webhooks are the only external signal
- Live-only API - Use small amounts during setup and build carefully
- Hosted payment pages - Beautiful checkout pages with QR codes
Confirmation Times
| Coin | Default Confirmations | Approximate Time |
|---|---|---|
BTC | 3 | ~30 minutes |
LTC | 6 | ~15 minutes |
DOGE | 6 | ~6 minutes |
Quick Start
Get up and running in 5 minutes:
Step 1: Get API Keys
Sign up at osuvox.net and copy your API keys from Dashboard → API Keys
Set OSUVOX_API_BASE and OSUVOX_WEBHOOK_URL in your server environment if provided
by your deployment platform.
Step 2: Configure Webhook URL
Set your webhook endpoint in Dashboard → Settings. Copy your webhook secret.
Step 3: Create a Payment
curl -X POST https://www.osuvox.net/api/v1/payments \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"amount": "0.001",
"coin": "BTC",
"external_id": "order_123",
"webhook_url": "https://your-site.com/webhooks/osuvox"
}'Always pass external_id. Use webhook_url unless you set OSUVOX_WEBHOOK_URL as a default.
Step 4: Handle Webhooks
When payment is confirmed, we'll POST to your webhook URL:
// Your webhook endpoint receives:
{
"id": "evt_abc123",
"type": "payment.confirmed",
"data": {
"payment": {
"id": "pay_xyz789",
"external_id": "order_123",
"status": "confirmed"
}
}
}Authentication
Authenticate requests by including your API key in the Authorization header:
Authorization: Bearer sk_live_your_api_keyLive Mode
sk_live_... Live-only API. Use small amounts during setup and build carefully.
Security: Never expose API keys in client-side code. Make all API calls from your server.
Implementation Notes (Important)
- OSUVOX_API_BASE: If set, use it as the base API URL. Do not hard-code the URL.
- OSUVOX_WEBHOOK_URL: If set, use it as the default webhook URL unless explicitly overridden per payment.
- Fulfillment trigger: Use
payment.confirmedorpayment.completed. If you need split payout finality, usepayment.completed. - TXID guarantee: Fulfillment should rely on confirmed/completed events because they always include
txid. - Dedupe: Store webhook event IDs in your database. In-memory dedupe is not sufficient for retries or multi-instance deployments.
- external_id: Treat
external_idas the canonical linkage between your order and Osuvox payment for all payment flows. - Reconciliation:
GET /paymentsis for reconciliation only. Webhooks are the primary signal; avoid polling as a primary mechanism.
Get Prices
/api/v1/pricesRetrieves the current cached exchange rates for all supported cryptocurrencies in USD. This endpoint is public and does not require authentication.
Rate Limiting
Strictly limited to 1 request per minute per IP address. This endpoint is intended for checking rates before initiating payments.
Example Request
curl https://www.osuvox.net/api/v1/pricesResponse
{
"data": {
"BTC": 43250.25,
"LTC": 72.45,
"DOGE": 0.078
},
"currency": "USD",
"timestamp": 1705152000000
}Create Payment
/api/v1/paymentsCreates a new payment and generates a unique cryptocurrency address.
Request Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | string | number | Yes | Payment amount in cryptocurrency |
coin | string | Yes | BTC, LTC, or DOGE |
external_id | string | No | Your order/reference ID (max 255 chars). Treat as required for fulfillment and reconciliation. |
description | string | No | Payment description (max 500 chars) |
metadata | object | No | Custom key-value data. Passed through to webhooks. |
webhook_url | string | No | Override default webhook URL for this payment |
expires_in | number | No | Seconds until expiration (default: 600, max: 86400) |
confirmations | number | No | Override default confirmation requirement |
Example Request
curl -X POST https://www.osuvox.net/api/v1/payments \
-H "Authorization: Bearer sk_live_xxx" \
-H "Content-Type: application/json" \
-d '{
"amount": "0.001",
"coin": "BTC",
"external_id": "order_12345",
"description": "Premium subscription",
"metadata": {
"customer_id": "cust_abc",
"plan": "premium"
}
}'Response
{
"id": "pay_abc123def456",
"status": "pending",
"coin": "BTC",
"amount": "0.00100000",
"amount_received": "0",
"address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"external_id": "order_12345",
"description": "Premium subscription",
"payment_url": "https://www.osuvox.net/pay/pay_abc123def456",
"qr_code_url": "https://www.osuvox.net/api/qr/pay_abc123def456",
"confirmations_required": 3,
"confirmations": 0,
"expires_at": "2026-01-11T22:30:00Z",
"created_at": "2026-01-11T21:30:00Z",
"metadata": {
"customer_id": "cust_abc",
"plan": "premium"
}
}Response Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique payment ID (pay_xxx) |
address | string | Unique receiving address for this payment |
payment_url | string | Hosted payment page URL - redirect customers here (webhooks remain the source of truth) |
qr_code_url | string | QR code image URL (PNG, 300x300) |
expires_at | string | ISO 8601 timestamp when payment expires |
Get Payment
/api/v1/payments/{id}Retrieves details for a specific payment.
Example Request
curl https://www.osuvox.net/api/v1/payments/pay_abc123def456 \
-H "Authorization: Bearer sk_live_xxx"Response
{
"id": "pay_abc123def456",
"status": "confirmed",
"coin": "BTC",
"amount_requested": "0.00100000",
"amount_received": "0.00100000",
"address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"txid": "a1b2c3d4e5f6789012345678901234567890abcdef",
"external_id": "order_12345",
"description": "Premium subscription",
"confirmations_required": 3,
"confirmations": 6,
"metadata": { "customer_id": "cust_abc" },
"payment_url": "https://www.osuvox.net/pay/pay_abc123def456",
"qr_code_url": "https://www.osuvox.net/api/qr/pay_abc123def456",
"expires_at": "2026-01-11T22:30:00Z",
"created_at": "2026-01-11T21:30:00Z",
"detected_at": "2026-01-11T21:45:00Z",
"confirmed_at": "2026-01-11T22:15:00Z",
"completed_at": null
}List Payments
/api/v1/paymentsReturns a paginated list of payments.
Query Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | number | 20 | Results per page (max 100) |
offset | number | 0 | Pagination offset |
status | string | — | Filter by status |
coin | string | — | Filter by cryptocurrency |
external_id | string | — | Find payment by your order ID |
from | string | — | Filter by created_at ≥ (ISO 8601) |
to | string | — | Filter by created_at ≤ (ISO 8601) |
Example: Find by Order ID
curl "https://www.osuvox.net/api/v1/payments?external_id=order_12345" \
-H "Authorization: Bearer sk_live_xxx"Response
{
"data": [
{
"id": "pay_abc123def456",
"status": "confirmed",
"coin": "BTC",
"amount_requested": "0.00100000",
"amount_received": "0.00100000",
"external_id": "order_12345",
"created_at": "2026-01-11T21:30:00Z"
}
],
"total": 1,
"limit": 20,
"offset": 0
}Payment Statuses
| Status | Description | Action |
|---|---|---|
pending | Payment created, awaiting transaction | Show payment page to customer |
detecting | Transaction detected (0 confirmations) | Show "Payment received, confirming..." |
confirming | Transaction confirming (1+ confirmations) | Show confirmation progress |
confirmed | Required confirmations reached | Fulfill the order |
completed | Split payments executed | Record payout transactions |
expired | Payment window closed | Cancel order, offer retry |
failed | Error occurred | Contact support |
Webhooks
Webhooks notify your server when payment events occur. These events are generated by internal monitoring (no public monitoring endpoint). Configure your webhook URL in Dashboard → Settings.
For reliability, store event.id in your database and ignore duplicates on retry.
Webhook Payload Format
All webhooks use a Stripe-like nested structure with unique event IDs for deduplication:
{
"id": "evt_abc123def456789",
"type": "payment.confirmed",
"created_at": "2026-01-11T22:15:00Z",
"data": {
"payment": {
"id": "pay_abc123def456",
"status": "confirmed",
"coin": "BTC",
"amount": "0.00100000",
"amount_received": "0.00100000",
"address": "bc1qxy2kgdygjrsqtzq2n0yrf2493p83kkfjhx0wlh",
"txid": "a1b2c3d4e5f6...",
"external_id": "order_12345",
"description": "Premium subscription",
"confirmations": 3,
"confirmations_required": 3,
"metadata": {
"customer_id": "cust_abc",
"plan": "premium"
},
"expires_at": "2026-01-11T22:30:00Z",
"created_at": "2026-01-11T21:30:00Z",
"detected_at": "2026-01-11T21:45:00Z",
"confirmed_at": "2026-01-11T22:15:00Z",
"completed_at": null
}
}
}TXID requirement: payment.confirmed always includes data.payment.txid. payment.completed includes data.payment.txid and payout txid values. payment.detected can be null. If multiple UTXOs are involved, data.payment.metadata.txids may include
the full list.
Fallback: If your handler ever receives a confirmed/completed webhook without a txid, defer fulfillment and reconcile with GET /payments/{id}. This should be rare but keeps your integration safe.
Data Persistence: Your external_id and metadata provided during payment creation are guaranteed to be returned in the data.payment object of every webhook.
HTTP Headers
| Header | Description |
|---|---|
X-Osuvox-Signature | Timestamped HMAC signature for verification |
X-Osuvox-Event | Event type (e.g., payment.confirmed) |
X-Osuvox-Delivery | Unique delivery ID |
Persistent Dedupe
Store webhook event.id in a durable table. In-memory dedupe is not sufficient for
retries or multi-instance deployments.
-- Example schema
create table osuvox_events (
id text primary key,
created_at timestamptz default now()
);Webhook Events
| Event | Description | Action |
|---|---|---|
payment.detected | Transaction seen (0 confirmations) | Show "Payment received" (txid may be null) |
payment.confirmed | Required confirmations reached | Fulfill the order (txid always present) |
payment.completed | Split payouts executed | Record payout transactions (payout txid always present) |
payment.expired | Payment window closed | Cancel order |
payment.failed | Error occurred | Log error, alert team |
payment.completed with Payouts
When split payments are executed, the webhook includes payout details. Each payout entry includes
its broadcast txid.
{
"id": "evt_ghi789",
"type": "payment.completed",
"created_at": "2026-01-11T22:20:00Z",
"data": {
"payment": {
"id": "pay_abc123",
"status": "completed"
},
"payouts": [
{
"address": "bc1q_vendor...",
"amount": "0.00085000",
"txid": "payout_tx_1...",
"label": "Vendor",
"percentage": 85
},
{
"address": "bc1q_platform...",
"amount": "0.00015000",
"txid": "payout_tx_2...",
"label": "Platform Fee",
"percentage": 15
}
]
}
}Signature Verification
All webhooks include a timestamped HMAC-SHA256 signature in the X-Osuvox-Signature header:
X-Osuvox-Signature: t=1704931200,v1=a1b2c3d4e5f6...🔴 CRITICAL: Use Raw Request Body
You must verify the signature using the raw, unparsed bytes of the HTTP request body exactly as received from our servers. If you parse the JSON into an object and re-stringify it, the verification will fail because:
- JSON key order is not guaranteed after parsing.
- Whitespace differences (e.g. spaces vs no spaces) change the HMAC hash.
Retry signing: Each delivery attempt is signed with a fresh timestamp. Keep your verification tolerance short (recommended: 5 minutes) to reduce replay risk while still allowing retries.
Node.js (Express) Example
import crypto from 'crypto';
// Use express.raw() to get the Buffer for the webhook route
app.post('/webhooks/osuvox', express.raw({type: 'application/json'}), (req, res) => {
const signature = req.headers['x-osuvox-signature'];
const payload = req.body.toString(); // <--- THIS IS THE RAW STRING
if (!verifySignature(payload, signature, WEBHOOK_SECRET)) {
return res.status(401).send('Invalid signature');
}
const event = JSON.parse(payload); // Parse ONLY after verification
// ... process event
});
function verifySignature(payload, signatureHeader, secret) {
const parts = signatureHeader.split(',');
const timestamp = parts.find(p => p.startsWith('t=')).slice(2);
const providedSig = parts.find(p => p.startsWith('v1=')).slice(3);
const signedPayload = `${timestamp}.${payload}`;
const expectedSig = crypto.createHmac('sha256', secret)
.update(signedPayload)
.digest('hex');
return crypto.timingSafeEqual(
Buffer.from(expectedSig),
Buffer.from(providedSig)
);
}SvelteKit / Next.js Example
export const POST = async ({ request }) => {
const payload = await request.text(); // <--- GET RAW TEXT DIRECTLY
const signature = request.headers.get('x-osuvox-signature');
const secret = process.env.OSUVOX_WEBHOOK_SECRET;
if (!verifySignature(payload, signature, secret)) {
return new Response('Unauthorized', { status: 401 });
}
const event = JSON.parse(payload);
// ... process event
return new Response('OK');
};Python Example
import hmac
import hashlib
import time
def verify_webhook_signature(payload: str, signature_header: str, secret: str) -> bool:
parts = dict(p.split('=', 1) for p in signature_header.split(','))
timestamp = int(parts['t'])
provided_sig = parts['v1']
# Check timestamp (5 minute tolerance)
if abs(time.time() - timestamp) > 300:
return False
# Compute signature
signed_payload = f"{timestamp}.{payload}"
expected_sig = hmac.new(
secret.encode(),
signed_payload.encode(),
hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected_sig, provided_sig)
# Flask handler
@app.route('/webhook', methods=['POST'])
def webhook():
signature = request.headers.get('X-Osuvox-Signature')
payload = request.get_data(as_text=True)
if not verify_webhook_signature(payload, signature, WEBHOOK_SECRET):
return 'Invalid signature', 401
event = request.json
if event['type'] == 'payment.confirmed':
fulfill_order(event['data']['payment']['external_id'])
return 'OK', 200Split Rules
Automatically distribute payments to multiple wallets. Perfect for marketplaces, affiliate programs, and revenue sharing.
Splits are configured in your dashboard under Splits and apply account-wide to all payments (API, hosted checkout, and direct payments).
Note: Split percentages can total up to 100. Any remainder becomes a platform gap allocation.
Minimums: The smallest split output must stay above dust thresholds after fees. The minimum transaction depends on your smallest split and network conditions.
Errors
Error Response Format
{
"error": {
"code": "invalid_amount",
"message": "Amount must be a positive number",
"param": "amount"
}
}HTTP Status Codes
| Code | Description |
|---|---|
200 | Success |
201 | Created |
400 | Bad Request - Invalid parameters |
401 | Unauthorized - Invalid API key |
404 | Not Found - Resource doesn't exist |
429 | Too Many Requests - Rate limited |
500 | Server Error |
Error Codes
| Code | Description |
|---|---|
invalid_api_key | API key is invalid or revoked |
invalid_amount | Amount is invalid or out of range |
invalid_coin | Unsupported cryptocurrency |
payment_not_found | Payment ID doesn't exist |
payment_expired | Payment has already expired |
rate_limit_exceeded | Too many requests |
Rate Limits
We enforce rate limits to ensure API stability and prevent abuse.
Standard API (Authenticated)
100 / min
Per API key (Live/Test). Applies to /payments and other authenticated endpoints.
Prices API (Public)
1 / min
Per IP address. Applies to /prices endpoint.
Rate Limit Headers
| Header | Description |
|---|---|
X-RateLimit-Limit | Total requests allowed (100) |
X-RateLimit-Remaining | Requests remaining in window |
X-RateLimit-Reset | Unix timestamp when window resets |
Webhook Retry Policy
If your webhook endpoint fails, we retry with exponential backoff:
| Attempt | Delay |
|---|---|
| 1 | Immediate |
| 2 | 2 minutes |
| 3 | 4 minutes |
| 4 | 8 minutes |
| 5 | 16 minutes |
After 5 failures, retry manually from Dashboard → Webhooks.
TypeScript Types
Copy these types into your TypeScript project:
// osuvox-types.ts
export type OsuvoxCoin = 'BTC' | 'LTC' | 'DOGE';
export type PaymentStatus =
| 'pending'
| 'detecting'
| 'confirming'
| 'confirmed'
| 'completed'
| 'expired'
| 'failed';
export type WebhookEventType =
| 'payment.detected'
| 'payment.confirmed'
| 'payment.completed'
| 'payment.expired'
| 'payment.failed';
export interface CreatePaymentParams {
amount: string | number;
coin: OsuvoxCoin;
external_id?: string;
description?: string;
metadata?: Record<string, unknown>;
webhook_url?: string;
expires_in?: number;
confirmations?: number;
success_url?: string; // Hosted checkout: redirect after payment confirmed
cancel_url?: string; // Hosted checkout: redirect if cancelled/expired
}
export interface GetPricesResponse {
data: Record<OsuvoxCoin, number>;
currency: 'USD';
timestamp: number;
}
export interface OsuvoxPayment {
id: string;
status: PaymentStatus;
coin: OsuvoxCoin;
amount_requested: string;
amount_received: string;
address: string;
txid: string | null;
external_id: string | null;
description: string | null;
confirmations: number;
confirmations_required: number;
metadata: Record<string, unknown> | null;
payment_url: string;
qr_code_url: string;
expires_at: string | null;
created_at: string;
detected_at: string | null;
confirmed_at: string | null;
completed_at: string | null;
}
export interface OsuvoxWebhookEvent {
id: string;
type: WebhookEventType;
created_at: string;
data: {
payment: OsuvoxPayment;
payouts?: Array<{
address: string;
amount: string;
txid: string | null;
label: string;
percentage: number;
}>;
};
}
export interface OsuvoxError {
error: {
code: string;
message: string;
param?: string;
};
}Need Help?
Check the Webhook Logs to debug delivery issues, or contact support@osuvox.net.