Endpoint: POST /v1/sdk/wallets/provision
When to call: When a bank customer opts-in to enable crypto wallet functionality.
Authentication: JWT Bearer Token with wallet_manager role
Request:
POST /v1/sdk/wallets/provision HTTP/1.1
Host: api.saturnx.money
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json
{
"customer_id": "BANK_CUST_001",
"customer_email": "customer@bank.pk",
"customer_phone": "+923001234567",
"blockchain": "ethereum",
"kyc_level": "ENHANCED"
}Request Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
customer_id | string | Yes | Bank's internal customer identifier |
customer_email | string | Yes | Customer email address |
customer_phone | string | Yes | Customer phone number (E.164 format) |
blockchain | string | No | Blockchain network (default: "ethereum") |
kyc_level | string | No | KYC level: BASIC, ENHANCED, COMPREHENSIVE |
Response (200 OK):
{
"wallet_id": "wlt_abc123xyz",
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"blockchain": "ethereum",
"status": "ACTIVE",
"kyc_level": "ENHANCED",
"created_at": "2025-11-19T10:30:00Z"
}Response Fields:
| Field | Type | Description |
|---|---|---|
wallet_id | string | SaturnX wallet identifier (use in future API calls) |
wallet_address | string | Blockchain address (DO NOT expose to senders) |
blockchain | string | Blockchain network |
status | string | ACTIVE, BLOCKED, BLACKLISTED, PENDING |
kyc_level | string | Inherited KYC level |
created_at | string | ISO 8601 timestamp |
Error Responses:
// 400 Bad Request - Invalid input
{
"error": "INVALID_REQUEST",
"message": "customer_email is not a valid email address",
"field": "customer_email"
}
// 409 Conflict - Wallet already exists
{
"error": "WALLET_EXISTS",
"message": "Wallet already provisioned for customer_id BANK_CUST_001",
"existing_wallet_id": "wlt_abc123xyz"
}
// 403 Forbidden - KYC requirement not met
{
"error": "KYC_REQUIRED",
"message": "Customer KYC verification required before wallet provisioning",
"required_kyc_level": "ENHANCED"
}Endpoint: GET /v1/sdk/wallets/{wallet_id}
When to call: To check wallet status, balance, or retrieve details.
Request:
GET /v1/sdk/wallets/wlt_abc123xyz HTTP/1.1
Host: api.saturnx.money
Authorization: Bearer {JWT_TOKEN}Response (200 OK):
{
"wallet_id": "wlt_abc123xyz",
"customer_id": "BANK_CUST_001",
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"blockchain": "ethereum",
"status": "ACTIVE",
"kyc_level": "ENHANCED",
"balance": {
"total_value_usd": "1250.00",
"assets": [
{
"currency": "USDC",
"amount": "1000.00",
"value_usd": "1000.00",
"value_pkr": "280000.00"
},
{
"currency": "USDT",
"amount": "250.00",
"value_usd": "250.00",
"value_pkr": "70000.00"
}
]
},
"created_at": "2025-11-19T10:30:00Z",
"last_activity_at": "2025-11-19T14:22:00Z"
}Endpoint: POST /v1/sdk/payment-requests
When to call: When recipient customer wants to request payment from a sender.
Request:
POST /v1/sdk/payment-requests HTTP/1.1
Host: api.saturnx.money
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json
{
"wallet_id": "wlt_abc123xyz",
"amount": {
"type": "crypto",
"value": "1000",
"currency": "USDC"
},
"purpose": "Family support payment",
"expires_in_hours": 24
}Request Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
wallet_id | string | Yes | Recipient wallet ID |
amount.type | string | Yes | "crypto", "fiat", or "flexible" |
amount.value | string | Conditional | Required if type is "crypto" or "fiat" |
amount.currency | string | Conditional | Required if not "flexible" |
purpose | string | No | Payment description/purpose |
expires_in_hours | integer | No | Link expiry (default: 24 hours) |
Response (200 OK):
{
"payment_request_id": "preq_xyz789abc",
"payment_link": "https://saturnx.money/pay/xyz789abc",
"qr_code_url": "https://cdn.saturnx.money/qr/xyz789abc.png",
"escrow_address": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
"amount": {
"type": "crypto",
"value": "1000",
"currency": "USDC"
},
"status": "ACTIVE",
"created_at": "2025-11-19T15:00:00Z",
"expires_at": "2025-11-20T15:00:00Z"
}Important Notes:
escrow_addressis a proxy address - NOT the recipient's actual wallet- This maintains recipient privacy
- Payment link can be shared via any channel (WhatsApp, email, SMS, etc.)
Endpoint: GET /v1/sdk/payment-requests
Query Parameters:
wallet_id(required): Filter by walletstatus(optional): Filter by status (ACTIVE, PAID, EXPIRED, CANCELLED)page(optional): Page number (default: 0)limit(optional): Results per page (default: 20, max: 100)
Request:
GET /v1/sdk/payment-requests?wallet_id=wlt_abc123xyz&status=ACTIVE&page=0&limit=20 HTTP/1.1
Host: api.saturnx.money
Authorization: Bearer {JWT_TOKEN}Response (200 OK):
{
"payment_requests": [
{
"payment_request_id": "preq_xyz789abc",
"payment_link": "https://saturnx.money/pay/xyz789abc",
"amount": {
"type": "crypto",
"value": "1000",
"currency": "USDC"
},
"status": "ACTIVE",
"created_at": "2025-11-19T15:00:00Z",
"expires_at": "2025-11-20T15:00:00Z"
}
],
"pagination": {
"page": 0,
"limit": 20,
"total_count": 45,
"total_pages": 3
}
}Endpoint: GET /v1/sdk/transactions
When to call: When customer views transaction history in banking app.
Query Parameters:
wallet_id(required): Filter by wallettype(optional): Filter by type (RECEIVED, EXCHANGED, BLOCKED)page(optional): Page number (default: 0)limit(optional): Results per page (default: 20, max: 100)
Request:
GET /v1/sdk/transactions?wallet_id=wlt_abc123xyz&page=0&limit=20 HTTP/1.1
Host: api.saturnx.money
Authorization: Bearer {JWT_TOKEN}Response (200 OK):
{
"transactions": [
{
"transaction_id": "tx_def456ghi",
"type": "RECEIVED",
"amount": "1000.00",
"currency": "USDC",
"sender": {
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"phone_number": "+14155551234",
"custodial_provider": "Binance"
},
"timestamp": "2025-11-19T15:30:00Z",
"status": "COMPLETED",
"tx_hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"blockchain_explorer_url": "https://etherscan.io/tx/0x1234567890abcdef..."
},
{
"transaction_id": "tx_ghi789jkl",
"type": "EXCHANGED",
"amount": "500.00",
"currency": "USDC",
"exchange_rate": "280.50",
"received_amount": "140250.00",
"received_currency": "PKR",
"timestamp": "2025-11-18T10:15:00Z",
"status": "SETTLED",
"settlement_reference": "PKR_SETTLE_001"
}
],
"pagination": {
"page": 0,
"limit": 20,
"total_count": 127,
"total_pages": 7
}
}Endpoint: GET /v1/sdk/orders/exchange-rate
When to call: When customer wants to check current exchange rate before converting.
Query Parameters:
from_currency(required): Source crypto (USDC, USDT)to_currency(required): Destination fiat (PKR)amount(optional): Amount to convert (for exact quote)
Request:
GET /v1/sdk/orders/exchange-rate?from_currency=USDC&to_currency=PKR&amount=1000 HTTP/1.1
Host: api.saturnx.money
Authorization: Bearer {JWT_TOKEN}Response (200 OK):
{
"from_currency": "USDC",
"to_currency": "PKR",
"rate": "280.75",
"spread": "0.005",
"estimated_amount": "279346.25",
"valid_until": "2025-11-19T16:00:00Z",
"rate_locked": false
}Response Fields:
| Field | Type | Description |
|---|---|---|
rate | string | Exchange rate (1 USDC = X PKR) |
spread | string | SaturnX fee/spread (0.5% = 0.005) |
estimated_amount | string | PKR amount recipient will receive |
valid_until | string | Rate validity expiration |
rate_locked | boolean | Whether rate is locked |
Endpoint: POST /v1/sdk/orders
When to call: When customer initiates crypto-to-PKR conversion.
Request:
POST /v1/sdk/orders HTTP/1.1
Host: api.saturnx.money
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json
{
"order_type": "EXCHANGE",
"wallet_id": "wlt_abc123xyz",
"amount": "1000",
"from_currency": "USDC",
"to_currency": "PKR",
"bank_account": {
"iban": "PK36SCBL0000001123456702",
"account_number": "0000001123456702",
"bank_code": "SCBL",
"account_holder_name": "Muhammad Ahmed"
},
"lock_rate": false
}Request Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
order_type | string | Yes | Order type (EXCHANGE) |
wallet_id | string | Yes | Source wallet |
amount | string | Yes | Amount to convert |
from_currency | string | Yes | Source currency (USDC, USDT) |
to_currency | string | Yes | Destination currency (PKR) |
bank_account | object | Yes | Pakistani bank account details |
lock_rate | boolean | No | Lock rate for 15 minutes (default: false) |
Response (200 OK):
{
"order_id": "ord_mno123pqr",
"order_type": "EXCHANGE",
"from_amount": "1000.00",
"from_currency": "USDC",
"to_amount": "280187.50",
"to_currency": "PKR",
"exchange_rate": "280.75",
"spread": "0.005",
"status": "PENDING",
"rate_locked": false,
"created_at": "2025-11-19T16:00:00Z",
"estimated_settlement": "2025-11-19T18:00:00Z"
}Status Values:
PENDING- Exchange initiated, awaiting processingPROCESSING- Exchange in progressSETTLED- PKR transferred to bank accountFAILED- Exchange failed (reason in error message)CANCELLED- Cancelled by user or timeout
Endpoint: GET /v1/sdk/orders
Query Parameters:
wallet_id(required)order_type(optional): Filter by order type (EXCHANGE)page(optional)limit(optional)
Request:
GET /v1/sdk/orders?wallet_id=wlt_abc123xyz&order_type=EXCHANGE&page=0&limit=20 HTTP/1.1
Host: api.saturnx.money
Authorization: Bearer {JWT_TOKEN}Response: Similar to transaction history but specific to exchanges.
Endpoint: POST /v1/sdk/wallets/{wallet_id}/block
When to call: When bank needs to temporarily freeze wallet for:
- Suspicious activity investigation
- Non-trusted transfer detected
- Risk threshold exceeded
- Bank policy violation
Request:
POST /v1/sdk/wallets/wlt_abc123xyz/block HTTP/1.1
Host: api.saturnx.money
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json
{
"reason": "SUSPICIOUS_ACTIVITY",
"details": "Multiple high-value transactions from unknown sources within 24 hours",
"auto_unblock_hours": 72
}Request Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
reason | string | Yes | Reason code (see below) |
details | string | Yes | Human-readable explanation |
auto_unblock_hours | integer | No | Automatic unblock after N hours |
Reason Codes:
NON_TRUSTED_TRANSFER- Transfer from non-whitelisted sourceSUSPICIOUS_ACTIVITY- Detected suspicious patternsRISK_THRESHOLD_EXCEEDED- High risk scoreBANK_POLICY_VIOLATION- Violated bank's policiesPENDING_INVESTIGATION- Under investigation
Response (200 OK):
{
"wallet_id": "wlt_abc123xyz",
"status": "BLOCKED",
"reason": "SUSPICIOUS_ACTIVITY",
"blocked_at": "2025-11-19T16:30:00Z",
"auto_unblock_at": "2025-11-22T16:30:00Z"
}Blocked Wallet Behavior:
- ✅ Can receive funds (funds accepted but frozen)
- ❌ Cannot send or withdraw funds
- ❌ Cannot initiate exchanges
- ❌ Cannot create payment requests
Endpoint: POST /v1/sdk/wallets/{wallet_id}/unblock
Request:
POST /v1/sdk/wallets/wlt_abc123xyz/unblock HTTP/1.1
Host: api.saturnx.money
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json
{
"unblock_reason": "Investigation completed - no violations found"
}Response (200 OK):
{
"wallet_id": "wlt_abc123xyz",
"status": "ACTIVE",
"unblocked_at": "2025-11-20T10:00:00Z"
}Endpoint: POST /v1/sdk/wallets/{wallet_id}/blacklist
When to call: When regulatory authority (e.g., State Bank of Pakistan, FIA) requires permanent wallet freeze.
⚠️ CRITICAL: Blacklisting adds the wallet address to USDC smart contract blacklist. This is irreversible without regulatory approval.
Request:
POST /v1/sdk/wallets/wlt_abc123xyz/blacklist HTTP/1.1
Host: api.saturnx.money
Authorization: Bearer {JWT_TOKEN}
Content-Type: application/json
{
"regulatory_authority": "State Bank of Pakistan",
"regulatory_reference_number": "SBP/AML/2025/001234",
"reason": "AML violation - proceeds of crime",
"effective_date": "2025-11-19"
}Request Parameters:
| Field | Type | Required | Description |
|---|---|---|---|
regulatory_authority | string | Yes | Regulatory body name |
regulatory_reference_number | string | Yes | Official case/reference number |
reason | string | Yes | Blacklist reason |
effective_date | string | Yes | ISO date (YYYY-MM-DD) |
Response (200 OK):
{
"wallet_id": "wlt_abc123xyz",
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"status": "BLACKLISTED",
"regulatory_authority": "State Bank of Pakistan",
"regulatory_reference": "SBP/AML/2025/001234",
"blacklisted_at": "2025-11-19T17:00:00Z",
"usdc_contract_blacklist_applied": true
}Blacklisted Wallet Behavior:
- ❌ Cannot receive any USDC transactions
- ❌ Cannot send any USDC transactions
- ❌ All USDC frozen at smart contract level
- ✅ Enforced on-chain - not just platform-level
Blocking vs Blacklisting:
┌───────────────────────────────────────────────────────────┐
│ BLOCKING vs BLACKLISTING │
├───────────────────────────────────────────────────────────┤
│ BLOCKING (Temporary) │
│ • Trigger: Policy violation, suspicious activity │
│ • Scope: Platform level (SaturnX) │
│ • Duration: Temporary (hours/days) │
│ • Can Receive: YES (but frozen) │
│ • Can Send: NO │
│ • Reversible: YES (bank can unblock) │
│ │
│ BLACKLISTING (Permanent) │
│ • Trigger: Regulatory request (SBP, FIA, court order) │
│ • Scope: USDC smart contract (on-chain) │
│ • Duration: Permanent until regulatory clearance │
│ • Can Receive: NO (all transactions blocked) │
│ • Can Send: NO (all transactions blocked) │
│ • Reversible: Requires regulatory approval │
└───────────────────────────────────────────────────────────┘Bank must provide webhook endpoint during onboarding:
https://bank-backend.example.pk/webhooks/saturnx/{event_type}Webhook Security:
- HTTPS required
- HMAC signature verification (see Authentication section)
- IP whitelist recommended
- Timestamp validation (reject old/replayed events)
Webhook: POST /webhooks/saturnx/payment-received
When triggered: When funds arrive in bank's escrow wallet for a payment request.
Payload:
{
"event_id": "evt_abc123xyz",
"event_type": "payment.received",
"timestamp": "2025-11-19T15:30:00Z",
"data": {
"payment_request_id": "preq_xyz789abc",
"wallet_id": "wlt_abc123xyz",
"customer_id": "BANK_CUST_001",
"amount": "1000.00",
"currency": "USDC",
"blockchain": "ethereum",
"tx_hash": "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef",
"from_address": "0x8ba1f109551bD432803012645Ac136ddd64DBA72",
"to_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"confirmations": 12,
"sender_info": {
"first_name": "John",
"last_name": "Doe",
"email": "john.doe@example.com",
"phone_number": "+14155551234",
"custodial_provider": "Binance"
},
"status": "RECEIVED_IN_ESCROW"
}
}Bank Backend Response:
HTTP/1.1 200 OK
Content-Type: application/json
{
"received": true,
"processed_at": "2025-11-19T15:30:05Z"
}Next Steps:
- SaturnX performs risk assessment
- If approved, funds transfer from escrow to recipient wallet
- Bank receives
transfer-completewebhook
Webhook: POST /webhooks/saturnx/risk-check-complete
When triggered: After risk assessment is complete (approved, rejected, or manual review).
Payload:
{
"event_id": "evt_def456ghi",
"event_type": "risk_check.complete",
"timestamp": "2025-11-19T15:35:00Z",
"data": {
"payment_request_id": "preq_xyz789abc",
"wallet_id": "wlt_abc123xyz",
"customer_id": "BANK_CUST_001",
"risk_check_result": "APPROVED",
"risk_score": 25,
"risk_level": "LOW",
"checks_performed": [
"ON_CHAIN_ANALYSIS",
"SANCTION_SCREENING",
"SOURCE_VERIFICATION"
],
"decision_reason": "Low risk - auto-approved",
"next_action": "TRANSFER_TO_RECIPIENT"
}
}Risk Check Results:
APPROVED- Payment approved, will transfer to recipientREJECTED- Payment rejected, held in escrowMANUAL_REVIEW_REQUIRED- Flagged for manual reviewBLOCKED- Wallet automatically blocked
Webhook: POST /webhooks/saturnx/transfer-complete
When triggered: After funds transfer from escrow to recipient wallet.
Payload:
{
"event_id": "evt_ghi789jkl",
"event_type": "transfer.complete",
"timestamp": "2025-11-19T15:40:00Z",
"data": {
"payment_request_id": "preq_xyz789abc",
"wallet_id": "wlt_abc123xyz",
"customer_id": "BANK_CUST_001",
"amount": "1000.00",
"currency": "USDC",
"transfer_tx_hash": "0xabcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890",
"status": "COMPLETED",
"completed_at": "2025-11-19T15:40:00Z"
}
}Next Steps:
- Bank can notify customer via push notification/SMS/email
- Customer can view updated balance in banking app
Webhook: POST /webhooks/saturnx/exchange-complete
When triggered: After crypto-to-PKR exchange is settled to bank account.
Payload:
{
"event_id": "evt_jkl012mno",
"event_type": "exchange.complete",
"timestamp": "2025-11-19T18:00:00Z",
"data": {
"order_id": "ord_mno123pqr",
"order_type": "EXCHANGE",
"wallet_id": "wlt_abc123xyz",
"customer_id": "BANK_CUST_001",
"from_amount": "1000.00",
"from_currency": "USDC",
"to_amount": "280187.50",
"to_currency": "PKR",
"exchange_rate": "280.75",
"settlement_reference": "PKR_SETTLE_001",
"bank_account": {
"iban": "PK36SCBL0000001123456702",
"account_holder_name": "Muhammad Ahmed"
},
"status": "SETTLED",
"settled_at": "2025-11-19T18:00:00Z"
}
}Webhook: POST /webhooks/saturnx/wallet-blocked
When triggered: When wallet is automatically blocked by risk system or manually by bank.
Payload:
{
"event_id": "evt_pqr345stu",
"event_type": "wallet.blocked",
"timestamp": "2025-11-19T16:30:00Z",
"data": {
"wallet_id": "wlt_abc123xyz",
"customer_id": "BANK_CUST_001",
"reason": "SUSPICIOUS_ACTIVITY",
"details": "Multiple high-value transactions from unknown sources",
"blocked_at": "2025-11-19T16:30:00Z",
"auto_unblock_at": "2025-11-22T16:30:00Z",
"triggered_by": "AUTOMATIC"
}
}Webhook: POST /webhooks/saturnx/transaction-failed
When triggered: When on-chain transaction fails or is rejected.
Payload:
{
"event_id": "evt_stu678vwx",
"event_type": "transaction.failed",
"timestamp": "2025-11-19T15:45:00Z",
"data": {
"payment_request_id": "preq_xyz789abc",
"wallet_id": "wlt_abc123xyz",
"customer_id": "BANK_CUST_001",
"failure_reason": "INSUFFICIENT_GAS",
"error_message": "Transaction failed due to insufficient gas fees",
"failed_at": "2025-11-19T15:45:00Z"
}
}All JSON properties use snake_case as per ADR-0004:
{
"wallet_id": "wlt_abc123xyz",
"customer_id": "BANK_CUST_001",
"created_at": "2025-11-19T10:00:00Z"
}{
"wallet_id": "wlt_abc123xyz",
"customer_id": "BANK_CUST_001",
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"blockchain": "ethereum",
"status": "ACTIVE",
"kyc_level": "ENHANCED",
"created_at": "2025-11-19T10:00:00Z"
}Status Values:
ACTIVE- Wallet operationalBLOCKED- Temporarily frozenBLACKLISTED- Permanently blacklisted (regulatory)PENDING- Provisioning in progress
KYC Levels:
BASIC- Name, DOB, emailENHANCED- + ID document, selfie, addressCOMPREHENSIVE- + proof of address, source of funds
{
"wallet_id": "wlt_abc123xyz",
"total_value_usd": "1250.00",
"assets": [
{
"currency": "USDC",
"amount": "1000.00",
"value_usd": "1000.00",
"value_pkr": "280000.00"
},
{
"currency": "USDT",
"amount": "250.00",
"value_usd": "250.00",
"value_pkr": "70000.00"
}
]
}{
"transaction_id": "tx_def456ghi",
"type": "RECEIVED",
"amount": "1000.00",
"currency": "USDC",
"status": "COMPLETED",
"tx_hash": "0x1234567890abcdef...",
"timestamp": "2025-11-19T15:30:00Z"
}Transaction Types:
RECEIVED- Incoming crypto paymentEXCHANGED- Crypto-to-fiat conversionBLOCKED- Transaction blocked by compliance
Transaction Statuses:
PENDING- Awaiting confirmationRECEIVED_IN_ESCROW- In escrow walletCOMPLETED- In recipient walletFAILED- Transaction failedREJECTED- Rejected by compliance
All errors follow this structure:
{
"error": "ERROR_CODE",
"message": "Human-readable error description",
"field": "field_name",
"details": {
"additional_context": "value"
},
"request_id": "req_abc123xyz",
"timestamp": "2025-11-19T16:00:00Z"
}| HTTP Status | Error Code | Description | Action |
|---|---|---|---|
| 400 | INVALID_REQUEST | Invalid input parameters | Fix request body |
| 401 | UNAUTHORIZED | Invalid/missing JWT token | Refresh authentication |
| 403 | FORBIDDEN | Insufficient permissions | Check user roles |
| 404 | NOT_FOUND | Resource not found | Verify IDs |
| 409 | CONFLICT | Resource already exists | Handle duplicate |
| 422 | VALIDATION_FAILED | Business logic validation failed | Review validation errors |
| 429 | RATE_LIMIT_EXCEEDED | Too many requests | Implement backoff |
| 500 | INTERNAL_ERROR | Server error | Retry with exponential backoff |
| 503 | SERVICE_UNAVAILABLE | Temporarily unavailable | Retry later |
Invalid Input:
{
"error": "INVALID_REQUEST",
"message": "customer_email is not a valid email address",
"field": "customer_email",
"request_id": "req_abc123xyz"
}Wallet Not Found:
{
"error": "NOT_FOUND",
"message": "Wallet with ID wlt_abc123xyz not found",
"field": "wallet_id",
"request_id": "req_def456ghi"
}Insufficient Balance:
{
"error": "INSUFFICIENT_BALANCE",
"message": "Insufficient USDC balance for exchange",
"details": {
"requested_amount": "1000.00",
"available_balance": "500.00",
"currency": "USDC"
},
"request_id": "req_ghi789jkl"
}Rate Limit:
{
"error": "RATE_LIMIT_EXCEEDED",
"message": "Rate limit exceeded: 100 requests per minute",
"details": {
"limit": 100,
"window": "1 minute",
"retry_after": "45 seconds"
},
"request_id": "req_jkl012mno"
}| Endpoint Pattern | Rate Limit | Window |
|---|---|---|
/v1/sdk/wallets/* | 100 req/min | Per tenant |
/v1/sdk/payment-requests/* | 200 req/min | Per tenant |
/v1/sdk/orders/* | 50 req/min | Per tenant |
/v1/sdk/transactions/* | 200 req/min | Per tenant |
| Webhooks (incoming) | Unlimited | - |
All responses include rate limit headers:
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 87
X-RateLimit-Reset: 1735776000Implement exponential backoff when rate limit is hit:
async function callApiWithRetry(apiCall, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
return await apiCall();
} catch (error) {
if (error.status === 429) {
const retryAfter = parseInt(error.headers['retry-after']) || Math.pow(2, attempt);
await sleep(retryAfter * 1000);
} else {
throw error;
}
}
}
throw new Error('Max retries exceeded');
}Base URL: https://api-sandbox.saturnx.money
Test Credentials:
- JWT tokens provided during onboarding
- Test tenant ID:
test-bank-001 - Test webhook endpoint configured during setup
SaturnX provides test wallets with pre-funded balances:
{
"wallet_id": "wlt_test_001",
"wallet_address": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"blockchain": "ethereum",
"balance": {
"USDC": "10000.00",
"USDT": "10000.00"
}
}Generate test payment links that automatically simulate sender payments:
curl -X POST https://api-sandbox.saturnx.money/v1/sdk/payment-requests \
-H "Authorization: Bearer TEST_JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"wallet_id": "wlt_test_001",
"amount": {"type": "crypto", "value": "100", "currency": "USDC"},
"purpose": "Test payment"
}'| Term | Definition |
|---|---|
| Bank-Custodied Wallet | Crypto wallet owned and controlled by bank on behalf of customer |
| Escrow Wallet | Bank-owned wallet where payments arrive for risk checks before recipient |
| Payment Link | Unique URL allowing sender to pay recipient without knowing wallet address |
| Risk Check Tier | Amount-based compliance check level (Fast Track, Standard, Comprehensive) |
| Blocking | Temporary wallet freeze by bank (platform-level) |
| Blacklisting | Permanent wallet freeze by regulatory authority (on-chain USDC contract) |
| Travel Rule | FATF requirement to exchange customer information for transactions >$1,000 USD |
| Whitelisted Custodial Provider | Approved sender wallet service (Binance, Coinbase, etc.) |
| MPC Wallet | Multi-Party Computation wallet (secure custody via DFNS) |
| PKR | Pakistani Rupee (fiat currency) |
| USDC | USD Coin (stablecoin) |
| USDT | Tether (stablecoin) |