Getting Started
Welcome to the Payment Processor API documentation. This API allows you to accept cryptocurrency payments and manage payouts programmatically.
Base URL
https://api.shadowpay.world
Quick Start
To get started, you'll need:
- An API key for authentication (passed via the
X-API-KeyHTTP header) - A configured wallet chain for your chosen blockchain network
- Choose a symbol and chain from the available currencies list
symbol + chain on write requests. The API resolves that to the canonical internal currency identifier for accounting and blockchain execution.
Example: Create Your First Payment
curl -X POST "https://api.shadowpay.world/post_create_order" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "X-API-Key: YOUR_API_KEY" \
-d "order_id=ORDER_001" \
-d "amount=100" \
-d "symbol=USDC" \
-d "chain=sepolia" \
-d "base_currency=EUR"
Response:
{
"payment_url": "https://shadowpay.world/d4e5f6g7-h8i9-j0k1-l2m3-n4o5p6q7r8s9?token=4f2b3c..."
}
Authentication
All endpoints require your merchant API key passed via the X-API-Key HTTP header. The only exception is the hosted payment page, which uses the opaque token query parameter embedded in payment_url.
X-API-Key- Your secret API key passed as an HTTP header (format: sk_live_xxx or sk_test_xxx)token- Opaque public payment access token automatically included inpayment_url
Create Payment Order
Creates a new cryptocurrency payment order and returns a payment URL for the customer.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
order_id |
string | ✓ | Unique identifier for this order |
amount |
string | ✓ | Payment amount in merchant's base currency (set during account creation, e.g., "100" for €100 or $100) |
symbol |
string | ✓ | Merchant-facing asset symbol for the selected chain (for example USDC or USDT) |
currency |
string | Canonical internal asset identifier for compatibility or advanced integrations. Send either symbol or currency, but not both. |
|
chain |
string | ✓ | Blockchain network (e.g., "ethereum", "sepolia", "base", "arbitrum") |
base_currency |
string | ✓ | Base fiat currency for the amount (e.g., "EUR", "USD") |
Example Request
curl -X POST "https://api.shadowpay.world/post_create_order" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "X-API-Key: sk_live_abc123xyz789" \
-d "order_id=ORDER_12345" \
-d "amount=100.50" \
-d "symbol=USDC" \
-d "chain=ethereum" \
-d "base_currency=EUR"
Response
{
"payment_url": "https://shadowpay.world/a1b2c3d4-e5f6-7890-abcd-ef1234567890?token=9a8b7c..."
}
Error Responses
400- Invalid amount format, invalid asset input, or cryptocurrency not available401- Invalid authentication credentials404- Cryptocurrency not found500- No wallet chain configured or internal error
symbol + chain for normal merchant integrations. Store the full payment_url exactly as returned, including its ?token=... query string.
Get Payment Info
Retrieves detailed information about a payment, including current status, amounts, and transaction events.
token query parameter from payment_url or your merchant X-API-Key header.
Parameters
| Parameter | Type | Description |
|---|---|---|
payment_id |
UUID | The unique payment identifier from the payment URL path |
token |
string | Opaque public access token from the payment_url query string. Optional only if you send X-API-Key. |
Example Request
curl -X GET "https://api.shadowpay.world/get_payment_infos/a1b2c3d4-e5f6-7890-abcd-ef1234567890?token=9a8b7c..."
Response
{
"crypto_price": 0.92,
"amount": 100.0,
"confirmed_amount": 50.0,
"received_amount": 80.0,
"expires_at": "2025-10-24T10:45:00.000Z",
"updated_at": "2025-10-24T10:35:00.000Z",
"qrcode": "data:image/png;base64,iVBORw0KGgo...",
"chain": "sepolia",
"status": "partially_received",
"payment_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"currency": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
"symbol": "USDC",
"asset_type": "token",
"base_currency": "EUR",
"amount_base_currency": 92.0,
"amount_base_paid": 46.0,
"crypto_amount_paid": 50.0,
"payment_uri": "ethereum:0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb@11155111/transfer?address=0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238&uint256=100000000",
"qr_format": "eip681",
"transaction_events": [
{
"tx_hash": "0xabc123...",
"amount": 50.0,
"status": "confirmed",
"block_number": 12340,
"created_at": "2025-10-24T10:31:00.000Z",
"updated_at": "2025-10-24T10:33:00.000Z"
},
{
"tx_hash": "0xdef456...",
"amount": 30.0,
"status": "seen",
"block_number": 12341,
"created_at": "2025-10-24T10:35:00.000Z",
"updated_at": "2025-10-24T10:35:00.000Z"
}
]
}
Payment Status Values
The status field in the response can have one of the following values:
| Status | Description |
|---|---|
created |
Payment order created |
pending |
Payment is active and awaiting funds |
partially_received |
Some crypto received on blockchain, but not full amount |
received |
Full crypto amount received on blockchain (unconfirmed) |
partially_confirmed |
Some amount confirmed on blockchain |
confirmed |
Payment fully confirmed on blockchain (final state) |
expired |
Payment window expired (default: 15 minutes) |
failed |
Payment failed or cancelled |
Get Payout Info
Retrieves detailed information about a payout/withdrawal, including current status, amounts, and transaction events.
X-API-Key header. Payout reads are not public.
Path Parameters
| Parameter | Type | Description |
|---|---|---|
payout_id |
UUID | The unique payout identifier returned from create withdrawal |
Example Request
curl -X GET "https://api.shadowpay.world/get_payout_infos/b1c2d3e4-f5g6-7890-hijk-lm1234567890" \
-H "X-API-Key: sk_live_abc123xyz789"
Response
{
"crypto_price": 0.92,
"amount": 50.0,
"status": "confirmed",
"chain": "sepolia",
"destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"currency": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238",
"symbol": "USDC",
"asset_type": "token",
"base_currency": "EUR",
"amount_base_currency": 46.0,
"tx_hash": "0xabc123def456...",
"created_at": "2025-10-24T11:00:00.000Z",
"confirmed_at": "2025-10-24T11:05:00.000Z",
"updated_at": "2025-10-24T11:05:00.000Z",
"transaction_events": [
{
"tx_hash": "0xabc123def456...",
"amount": 50.0,
"status": "confirmed",
"block_number": 12450,
"created_at": "2025-10-24T11:02:00.000Z",
"updated_at": "2025-10-24T11:05:00.000Z"
}
]
}
Payout Status Values
The status field in the response can have one of the following values:
| Status | Description |
|---|---|
pending |
Payout request created, waiting for processing |
queued |
Payout queued for execution |
sent |
Transaction sent to blockchain |
confirmed |
Transaction confirmed on blockchain (final state) |
failed |
Payout failed |
cancelled |
Payout cancelled |
List Available Currencies
Returns a list of available cryptocurrencies with their current exchange rates and network information.
Headers
| Parameter | Type | Required | Description |
|---|---|---|---|
X-API-Key |
string | ✓ | Your API key for authentication |
Example Request
curl -X POST "https://api.shadowpay.world/post_currencies" \
-H "X-API-Key: YOUR_API_KEY"
Response
[
{
"symbol": "USDC",
"name": "USD Coin",
"network": "ethereum",
"rate": 1.00,
"deposit_address": "0x1234567890abcdef1234567890abcdef12345678",
"available": true
},
{
"symbol": "USDC",
"name": "USD Coin (Sepolia Testnet)",
"network": "sepolia",
"rate": 1.00,
"deposit_address": "0x1234567890abcdef1234567890abcdef12345678",
"available": true
},
{
"symbol": "USDT",
"name": "Tether",
"network": "ethereum",
"rate": 0.99,
"deposit_address": "0x4567890123def4567890123def4567890123def",
"available": true
}
]
symbol field contains only the currency symbol (e.g., "USDC", "USDT") without the chain. Use the network field to identify which blockchain the currency is available on. The same currency can appear multiple times with different networks.
symbol and network you choose here as the inputs to /post_create_order and /post_create_withdrawal. You do not need token addresses for normal merchant integrations.
Create Withdrawal
Creates a withdrawal request to send cryptocurrency from your merchant wallet to a specified address.
order_id will return the existing payout if it's still active.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
order_id |
string | ✓ | Unique identifier for this withdrawal order |
amount |
string | ✓ | Withdrawal amount in merchant's base currency (set during account creation, e.g., "100" for €100 or $100) |
address_to |
string | ✓ | Destination blockchain address |
chain |
string | ✓ | Blockchain network (ethereum, sepolia, arbitrum, base) |
symbol |
string | ✓ | Merchant-facing asset symbol for the selected chain (for example USDC or USDT) |
currency |
string | Canonical internal asset identifier for compatibility or advanced integrations. Send either symbol or currency, but not both. |
|
base_currency |
string | ✓ | Base fiat currency for the amount (e.g., "EUR", "USD") |
Example Request
curl -X POST "https://api.shadowpay.world/post_create_withdrawal" \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "X-API-Key: sk_live_abc123xyz789" \
-d "order_id=WITHDRAWAL_001" \
-d "amount=50" \
-d "address_to=0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb" \
-d "chain=sepolia" \
-d "symbol=USDC" \
-d "base_currency=EUR"
Response
{
"payout_id": "b1c2d3e4-f5g6-7890-hijk-lm1234567890",
"status": "queued",
"payout_type": "withdrawal",
"amount": 50.0,
"symbol": "USDC",
"chain": "sepolia",
"source_wallet_address": "0x1234567890abcdef1234567890abcdef12345678",
"destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"created_at": "2025-10-24T10:30:00.000Z"
}
Withdrawal Statuses
pending- Withdrawal row exists but has not yet been queued by the servicequeued- Withdrawal accepted and queued for executionsent- Transaction sent to blockchainconfirmed- Transaction confirmed on blockchainfailed- Withdrawal failedcancelled- Withdrawal cancelled
symbol + chain for merchant-facing integrations. Canonical currency remains available only for compatibility or advanced callers.
Webhooks
Webhooks allow you to receive real-time notifications when payment and payout events occur in your account. Instead of polling our API, webhooks push event data directly to your server.
Setup
To receive webhooks, you need to:
- Configure your webhook URL through the dashboard or internal admin tooling
- Store the generated
webhook_secretsecurely - Use that secret to verify incoming webhook signatures
http or https, must not embed basic-auth credentials, and must not target localhost. Delivery also rejects destinations that resolve only to private or unroutable IPs unless they are in Tailscale or other trusted CIDRs configured by the operator.
Webhook Events
We currently send webhooks for the following events:
| Event Type | Description |
|---|---|
payment.confirmed |
Payment has been fully confirmed on the blockchain |
payout.confirmed |
Payout transaction successfully completed on the blockchain |
payout.failed |
Payout transaction failed on the blockchain |
Webhook Payload Structure
Payment Webhook Example
{
"event_type": "payment.confirmed",
"event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"created_at": "2024-12-03T10:30:45.123456+00:00",
"payment": {
"id": "d4e5f6g7-h8i9-j0k1-l2m3-n4o5p6q7r8s9",
"merchant_order_id": "ORDER_12345",
"crypto_amount": "100000000",
"amount_base": "100.00",
"crypto_currency": "USDC",
"base_currency": "EUR",
"status": "confirmed",
"created_at": "2024-12-03T10:15:30.000000+00:00",
"crypto_amount_paid": "100000000",
"amount_base_paid": "100.00"
}
}
Payout Webhook Example
{
"event_type": "payout.confirmed",
"event_id": "b2c3d4e5-f6g7-8901-bcde-f12345678901",
"created_at": "2024-12-03T11:45:20.123456+00:00",
"metadata": {
"tx_hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"block_number": 12345678,
"confirmed_at": "2024-12-03T11:45:15.000000+00:00"
},
"payout": {
"id": "e5f6g7h8-i9j0-k1l2-m3n4-o5p6q7r8s9t0",
"merchant_reference_id": "PAYOUT_456",
"payout_type": "withdrawal",
"crypto_amount": "50000000",
"crypto_currency": "USDC",
"amount_base": "50.00",
"base_currency": "EUR",
"status": "confirmed",
"created_at": "2024-12-03T11:30:15.000000+00:00",
"destination_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb"
}
}
Webhook Security
All webhook requests include cryptographic signatures to verify their authenticity. This prevents spoofing and ensures the request genuinely comes from Shadow Pay.
Signature Headers
Each webhook request includes two security headers:
X-Webhook-Timestamp- Unix timestamp when the webhook was sentX-Webhook-Signature- HMAC-SHA256 signature of the payload
Signature Verification Process
To verify a webhook is authentic:
- Extract
X-Webhook-TimestampandX-Webhook-Signaturefrom headers - Reconstruct the signed message:
{timestamp}.{json_payload} - Compute HMAC-SHA256 using your webhook secret:
HMAC-SHA256(message, webhook_secret) - Compare the computed signature with the received signature using a timing-safe comparison
# Signature verification logic
message = f"{timestamp}.{json.dumps(payload, separators=(',', ':'), sort_keys=True)}"
expected_signature = hmac.new(webhook_secret, message.encode('utf-8'), hashlib.sha256).hexdigest()
is_valid = hmac.compare_digest(expected_signature, received_signature)
Retry Behavior
If your endpoint is unavailable or returns an error, we will automatically retry the webhook delivery:
- Retry Schedule: 30s, 1m, 2m, 4m, 8m, 16m, 32m, 64m, 112.5m (total ~4 hours)
- Maximum Attempts: 9 delivery attempts total
- Success Codes: 200-299 HTTP status codes
- Permanent Failures: 400, 401, 403, 404, 410, 422 (no retries)
- Transient Failures: 5xx errors, timeouts, connection errors (will retry)
- Callback Validation Failures: Invalid or blocked destinations fail permanently without retry
Idempotency
Each webhook has a unique event_id. We guarantee at-least-once delivery, so you may receive duplicate webhooks. Use the event_id to make your webhook handling idempotent and prevent duplicate processing.
Testing Webhooks
During development:
- Use tools like ngrok to expose your local server
- Implement signature verification before deploying to production
- Contact support to trigger test webhook events
Error Handling
The API uses standard HTTP response codes to indicate success or failure.
HTTP Status Codes
| Code | Description |
|---|---|
200 |
Success - Request completed successfully |
400 |
Bad Request - Invalid parameters or request format |
401 |
Unauthorized - Invalid or missing authentication credentials |
404 |
Not Found - Payment, order, or cryptocurrency not found |
500 |
Internal Server Error - Something went wrong on the server |
Error Response Format
{
"detail": "Invalid authentication credentials"
}
Common Error Examples
Authentication Error (401)
{
"detail": "Invalid authentication credentials"
}
Payment Not Found (404)
{
"detail": "Payment a1b2c3d4-e5f6-7890-abcd-ef1234567890 not found"
}
Invalid Amount (400)
{
"detail": "Invalid amount format"
}