SplitRoute API Documentation - AI Code Context

CODE
1
2# SplitRoute API Documentation - AI Code Context
3
4## Overview
5SplitRoute is a payment processing API for Nano cryptocurrency that enables instant revenue sharing and payment distribution.
6Create standard, perpetual, or simulated invoices, split payments among multiple recipients, and automate payouts.
7
8\```
9Key Benefits:
10- Split payments instantly to creators, partners & teams
11- Up to 10x cheaper than PayPal & Stripe
12- Payouts in under 1 minute
13- Unlimited recipients per invoice
14\```
15
16## Authentication
17
18All authenticated endpoints require an API key in the header:
19
20\```http
21X-API-Key: your_api_key_here
22\```
23
24### API Key Tiers
25
26| Tier | Rate Limit | Features | Notes |
27| :---------- | :------------------------ | :----------------------------- | :---------------------------------- |
28| No AUTH | 1000 requests/hour shared | Basic API access | Shared limit across all unauth users |
29| FREE | 1000 requests/hour | Basic API access, Dashboard | Individual limit |
30| BASIC | 2000 requests/hour | Basic API access, Dashboard | Individual limit |
31| PRO | 5000 requests/hour | Advanced features, Dashboard | Individual limit |
32| ENTERPRISE | 20000+ requests/hour | Custom limits, Support | Individual limit |
33
34### Register for a Free API Key
35
36\```
37POST /api/v1/api-keys/register
38
39Request:
40{
41 "email": "[email protected]",
42 "name": "your.key.name"
43}
44
45Response (Example):
46{
47 "key": "SR_SECRET_d069a1d9a53ff1a77296d0223eb000ac", # Your secret API key
48 "name": "your.key.name",
49 "tier": "FREE",
50 "status": "ACTIVE",
51 "rate_limit_per_hour": 1000,
52 "remaining_invoices": -1, # Credits system (often -1 for unlimited on certain tiers)
53 "created_at": "2025-03-25T19:06:33.107954Z",
54 "expires_at": "2124-03-01T19:06:33.107943Z", # For time-limited tiers
55 "is_active": true
56}
57\```
58
59### Upgrade to Paid Tier
60
61\```
62POST /api/v1/api-keys/purchase
63
64Headers:
65X-API-Key: your_current_api_key
66
67Request:
68{
69 "tier": "BASIC", # Required. One of: BASIC, PRO, ENTERPRISE
70 "billing_period": "monthly" # Optional. One of: monthly, annual (default: monthly)
71}
72
73Response:
74# Returns a standard Invoice object (see below) to pay for the upgrade.
75# The invoice's user_data field contains details about the purchase.
76\```
77
78## Core Endpoints
79
80### Create an Invoice
81
82Creates a standard (expiring) or perpetual (non-expiring) invoice. Can also simulate creation.
83
84\```
85POST /api/v1/invoices
86
87Headers:
88X-API-Key: your_api_key # Optional for free tier, required for paid features/limits
89
90# Request Body:
91# Use EITHER destinations OR forward_account
92{
93 "nominal_amount": 29.9, # Required for STANDARD invoices. Ignored for PERPETUAL_OPTIONAL.
94 "nominal_currency": "EUR", # Required for STANDARD invoices. Ignored for PERPETUAL_OPTIONAL. (e.g., USD, EUR, XNO)
95 "destinations": [ # Option 1: Define multiple payout destinations (rules applied on successful payment)
96 {
97 "account": "nano_1primary...", # Nano address
98 "primary": true # Required for one destination if using this structure without equal_split logic. Receives remainder.
99 },
100 {
101 "account": "nano_2percent...",
102 "percentage": 10, # Percentage of nominal_amount (after fixed amounts & fees)
103 "description": "Partner fee" # Optional description
104 },
105 {
106 "account": "nano_3fixed...",
107 "nominal_amount": 5 # Fixed amount in nominal_currency
108 }
109 ],
110 # "forward_account": "nano_...", # Option 2: Alternative to 'destinations'. Forwards 100% (after fees) to this single address.
111 "webhook_url": "https://www.example.com/webhook", # Optional: URL for real-time status updates
112 "webhook_secret": "your_webhook_secret", # Optional: Secret to verify webhook signature
113 "reference": "custom-order-id-123", # Optional: Your internal reference string
114 "show_qr": true, # Optional: Include base64 QR code in response (default: false)
115 "is_perpetual": false, # Optional: Create a non-expiring invoice (default: false). nominal_amount/currency are ignored if true.
116 "simulate": false # Optional: Calculate result without creating invoice (default: false)
117 # "user_data": "any custom string data", # Optional: Custom data, included in webhooks
118}
119
120# Response Body (Full Invoice Object - also used for GET endpoints):
121{
122 "invoice_id": "INV_2025_03_d43d2995c143be1b59df", # Public ID
123 "secret_id": "INV_SECRET_27b6888c19fa0a83d01da", # Secret ID (keep private)
124 "account_address": "nano_1payment...", # Address for payer to send funds
125 "invoice_type": "STANDARD", # STANDARD | PERPETUAL_OPTIONAL
126 "nominal_currency": "EUR", # Original request currency (null for PERPETUAL_OPTIONAL)
127 "formatted_currency": "XNO", # Payment currency (Nano)
128 "required": { # Amount details required (null for PERPETUAL_OPTIONAL `amount` fields)
129 "unit_amount": "30567346000000000000000000000000", # Amount in raw units (smallest Nano unit)
130 "formatted_amount": "30.567346", # Human-readable XNO amount
131 "nominal_amount": "29.9" # Original requested nominal amount
132 },
133 "received": { # Amount details received so far
134 "unit_amount": "0",
135 "formatted_amount": "0",
136 "nominal_amount": "0" # Approximate nominal value received
137 },
138 "exchange_rate": "0.978168", # Exchange rate used (1 nominal = X XNO), null if not applicable
139 "expires_at": "2025-03-25T19:29:10.159811Z", # Expiry time (null for PERPETUAL)
140 "paid_at": null, # Timestamp when fully paid (null if not paid)
141 "forwarded_at": null, # Timestamp when funds forwarded (null if not forwarded)
142 "done_at": null, # Timestamp when processing complete (null if not done)
143 "timestamp_created": "2025-03-25T19:14:10.159811Z", # Creation time
144 "destinations": [ # Calculated distribution amounts (includes service fee)
145 {
146 "type": "primary", # Destination type (primary, percentage, fixed, service_fee)
147 "account": "nano_1primary...",
148 "unit_amount": "22246178670000000000000000000000",
149 "formatted_amount": "22.24617867",
150 "nominal_amount": "21.760" # Approx. nominal value for this destination
151 },
152 {
153 "type": "percentage",
154 "account": "nano_2percent...",
155 "unit_amount": "3056734600000000000000000000000",
156 "formatted_amount": "3.0567346",
157 "nominal_amount": "2.990",
158 "description": "Partner fee"
159 },
160 {
161 "type": "fixed",
162 "account": "nano_3fixed...",
163 "unit_amount": "5111596000000000000000000000000",
164 "formatted_amount": "5.111596",
165 "nominal_amount": "5.000"
166 },
167 {
168 "type": "service_fee", # Automatically added service fee destination
169 "account": "nano_1nowapi...", # SplitRoute's fee account
170 "unit_amount": "152836730000000000000000000000",
171 "formatted_amount": "0.15283673",
172 "nominal_amount": "0.149",
173 "description": "Service fee"
174 }
175 ],
176 "service_fee_rate": "0.50", # Service fee percentage applied (e.g., 0.50 for 0.5%)
177 "show_qr": true, # Matches request parameter
178 "is_simulation": true, # True if created with simulate: true
179 "is_pending": false, # True if partially paid (but not fully)
180 "is_paid": false, # True if required amount received (or any amount for PERPETUAL_OPTIONAL)
181 "is_forwarded": false, # True if funds sent to destinations/forward_account
182 "is_done": false, # True if processing complete (paid & forwarded/settled or expired)
183 "is_expired": false, # True if expires_at passed and not paid (STANDARD only)
184 "is_overpaid": false, # True if received > required (STANDARD only)
185 "is_perpetual": false, # Matches request parameter
186 "qr_code": "base64_string_with_encoded_qr-code", # Optional: Base64 encoded QR code PNG if show_qr=true
187 "uri_rfc_8905": "payto:nano/nano_1payment...?amount=...", # RFC 8905 payment URI
188 "uri_nano": "nano:nano_1payment...?amount=..." # Common Nano payment URI
189 # user_data is typically NOT included in the response, only in webhooks
190}
191\```
192
193### Get Invoice by ID
194
195Retrieves the full Invoice object by its public ID.
196
197\```
198GET /api/v1/invoices/{invoice_id}
199
200Response: Full Invoice Object (see Create Invoice Response)
201\```
202
203### Get Invoice by Secret ID
204
205Retrieves the full Invoice object by its secret ID.
206
207\```
208GET /api/v1/invoices/secret/{secret_id}
209
210Response: Full Invoice Object (see Create Invoice Response)
211\```
212
213### Get Supported Currencies
214
215Lists currencies usable in the `nominal_currency` field.
216
217\```
218GET /api/v1/currencies
219
220Response:
221[
222 {
223 "code": "USD",
224 "name": "US Dollar",
225 "is_fiat": true,
226 "decimal_places": 6, # Max precision for calculations
227 "min_amount": "0.000001",
228 "max_amount": "25000.00"
229 },
230 # ... other fiat currencies (EUR, GBP, etc.)
231 {
232 "code": "XNO",
233 "name": "Nano",
234 "is_fiat": false,
235 "decimal_places": 6, # Max precision for display/nominal input
236 "min_amount": "0.000001",
237 "max_amount": "25000.00" # Equivalent value limit
238 }
239 # Potentially other cryptos if supported
240]
241\```
242
243## Payment Distribution Types
244
245When creating an invoice using `destinations`, you define how funds are split:
246
247### 1. Primary Destination (`primary: true`)
248Receives the remainder after Service Fee, Fixed Amounts, and Percentage Amounts are calculated. Only one primary allowed.
249\```json
250{ "account": "nano_1abc...", "primary": true, "description": "Main recipient" }
251\```
252
253### 2. Percentage-Based Distribution (`percentage`)
254Receives a specified percentage of the `nominal_amount` (after fixed amounts & fees are deducted).
255\```json
256{ "account": "nano_1def...", "percentage": 25.5, "description": "Partner (25.5%)" }
257\```
258
259### 3. Fixed Amount Distribution (`nominal_amount`)
260Receives a specific amount in the invoice's `nominal_currency`. Calculated after Service Fee but before Percentages.
261\```json
262{ "account": "nano_1ghi...", "nominal_amount": 5.00, "description": "Platform fee ($5.00)" }
263\```
264
265### Calculation Waterfall & Example
266
267Distributions are calculated in this order from the total received:
2681. **Service Fee:** (e.g., 0.5% for FREE tier)
2692. **Fixed Amounts:** Destinations with `nominal_amount`.
2703. **Percentage Amounts:** Destinations with `percentage` (calculated on remaining balance).
2714. **Primary Destination:** Receives the final remaining balance.
272
273**Example:** Invoice for $100.00 USD (Free Tier - 0.5% fee)
274\```json
275{
276 "nominal_amount": 100.00, "nominal_currency": "USD",
277 "destinations": [
278 { "account": "nano_1primary...", "primary": true, "description": "Seller" },
279 { "account": "nano_2partner...", "percentage": 20, "description": "Partner (20%)" },
280 { "account": "nano_3platform...", "nominal_amount": 10.00, "description": "Platform Fee ($10)" }
281 ]
282}
283\```
284Distribution:
2851. Service Fee: $0.50 (to SplitRoute) -> Remaining: $99.50
2862. Fixed (Platform): $10.00 -> Remaining: $89.50
2873. Percentage (Partner): 20% of $89.50 = $17.90 -> Remaining: $71.60
2884. Primary (Seller): $71.60
289
290## Invoice Lifecycle (Standard Invoices)
291
2921. **Created**: Ready for payment (`is_paid`, `is_expired`, etc. are false).
2932. **Pending**: Partially paid (`is_pending: true`).
2943. **Paid**: Full required amount received (`is_paid: true`).
2954. **Forwarded**: Funds sent to destinations (`is_forwarded: true`).
2965. **Done**: Processing complete (Paid & Forwarded) (`is_done: true`).
2976. **Expired**: Expired before full payment (`is_expired: true`, alternative end state).
298
299*(Perpetual invoices do not expire and may have multiple paid/forwarded cycles).*
300
301## Real-time Payment Monitoring
302
303### WebSocket Connection
304
305Receive real-time invoice updates.
306
307**URL:** `wss://api.splitroute.com/api/v1/ws/invoices/{invoice_id}`
308
309**Message Format (Example: `invoice.paid` event):**
310\```json
311{
312 "timestamp": "2025-03-31T09:18:11.897603+00:00", // Message send time
313 "message_type": "invoice", // "invoice" or "payment"
314 "category": "updated", // "updated", "completed", "failed", "payment"
315 "payload": { // Event-specific data
316 "id": "INV_2025_03_...", // Invoice ID
317 "timestamp": "2025-03-31T09:18:11.890364+00:00", // Event occurrence time
318 "event_type": "invoice.paid", // Specific event identifier
319 "unit_amount": "2000000000000000000000000", // Amount relevant to event (raw units)
320 "formatted_amount": "0.000002", // Amount relevant to event (formatted XNO)
321 "nominal_amount": "0.000002", // Approx. nominal value of event amount
322 "is_paid": true,
323 "is_expired": false,
324 "currency": "EUR", // Invoice nominal currency (if applicable)
325 "exchange_rate": "0.8221560000" // Invoice exchange rate (if applicable)
326 // Other fields like is_overpaid, transaction_id may appear depending on event_type
327 }
328}
329\```
330
331**Event Categories & Types:**
332
333| Category | Description | Example Event Types |
334| :---------- | :----------------------------- | :---------------------------------------------- |
335| `updated` | Invoice status changed | `invoice.paid`, `invoice.forwarded` |
336| `completed` | Invoice processing finished | `invoice.done` |
337| `failed` | Invoice expired or failed | `invoice.expired` |
338| `payment` | Payment block confirmed | `payment.confirmed` |
339
340*(See full docs for detailed payload structure per event type)*
341
342**JS Example:**
343\```javascript
344const invoiceId = 'INV_...'; // Your Invoice ID
345const socket = new WebSocket(`wss://api.splitroute.com/api/v1/ws/invoices/${invoiceId}`);
346
347socket.onopen = (e) => console.log('WebSocket Connected');
348socket.onerror = (error) => console.error(`WebSocket Error: ${error.message}`);
349socket.onclose = (event) => console.log(`WebSocket Closed: Code=${event.code}, Reason=${event.reason}`);
350
351socket.onmessage = (event) => {
352 const message = JSON.parse(event.data);
353 const payload = message.payload;
354 console.log(`Received WS [${message.category}] ${payload.event_type} for ${payload.id}`);
355
356 if (payload.event_type === 'invoice.paid') {
357 console.log(`Invoice Paid! Amount: ${payload.formatted_amount} XNO`);
358 // Your logic here (e.g., update UI, fulfill order)
359 } else if (payload.event_type === 'payment.confirmed') {
360 console.log(`Payment Confirmed: ${payload.formatted_amount} XNO (Tx: ${payload.transaction_id.substring(0,8)}...)`);
361 // Useful for showing payment progress, especially for perpetual invoices
362 }
363
364 if (message.category === 'completed' || message.category === 'failed') {
365 console.log(`Invoice ${payload.id} finished processing (Category: ${message.category}).`);
366 socket.close();
367 }
368};
369\```
370
371**Connection Limits:** 5 per invoice, 15 min inactivity timeout, closes 5 min after done/expired.
372
373### Webhook Integration
374
375Receive server-side notifications for invoice events.
376
377**Setup:** Provide `webhook_url` and optional `webhook_secret` during invoice creation.
378
379**Events:** `invoice.created`, `invoice.paid`, `invoice.expired`, `invoice.forwarded`, `invoice.done`
380
381**Verification:** Check `X-Webhook-Signature` header using HMAC-SHA256 (`timestamp` + `request_body`) with your `webhook_secret`.
382
383\```python
384# Example Python Signature Verification
385import hmac, json, hashlib
386
387def verify_signature(secret: str, timestamp: str, body: bytes, received_signature: str) -> bool:
388 payload_str = body.decode('utf-8') # Assuming UTF-8 encoded JSON body
389 signed_content = f"{timestamp}{payload_str}".encode('utf-8')
390 expected_signature = hmac.new(secret.encode('utf-8'), signed_content, hashlib.sha256).hexdigest()
391 return hmac.compare_digest(expected_signature, received_signature)
392
393# In your webhook handler:
394# timestamp = request.headers.get('X-Webhook-Timestamp')
395# signature = request.headers.get('X-Webhook-Signature')
396# body_bytes = await request.body()
397# is_valid = verify_signature(YOUR_WEBHOOK_SECRET, timestamp, body_bytes, signature)
398\```
399
400## Error Handling
401
402API errors return a JSON body:
403\```json
404{
405 "error": "error_code_string", # e.g., "validation_error", "invalid_api_key"
406 "message": "Human-readable message",
407 "details": { # Optional: Field-specific validation errors
408 "field_name": "Error description for this field"
409 }
410}
411\```
412Common Status Codes: `200` (OK), `201` (Created), `400` (Bad Request), `401` (Unauthorized), `403` (Forbidden), `404` (Not Found), `422` (Unprocessable Entity - Validation), `429` (Too Many Requests), `5xx` (Server Error).
413
414Rate limit info in headers: `X-RateLimit-Limit`, `X-RateLimit-Remaining`, `X-RateLimit-Reset`.
415
416## Base URL
417`https://api.splitroute.com`
418
419## Core Implementation Flow (JS Example)
420\```javascript
421async function processOrder(orderId, amount, currency) {
422 const apiKey = 'your_api_key'; // Use environment variables
423 const destinations = [ /* your destination rules */ ];
424
425 try {
426 // 1. Create Invoice
427 const createResponse = await fetch('https://api.splitroute.com/api/v1/invoices', {
428 method: 'POST',
429 headers: { 'Content-Type': 'application/json', 'X-API-Key': apiKey },
430 body: JSON.stringify({
431 nominal_amount: amount,
432 nominal_currency: currency,
433 destinations: destinations,
434 reference: orderId,
435 webhook_url: 'your_webhook_url' // Optional: if using webhooks
436 })
437 });
438
439 if (!createResponse.ok) {
440 const errorData = await createResponse.json();
441 throw new Error(`Invoice creation failed: ${errorData.message}`);
442 }
443 const invoice = await createResponse.json();
444 console.log(`Invoice ${invoice.invoice_id} created. Payer address: ${invoice.account_address}`);
445 // Display payment info (address, amount, QR code) to the user
446
447 // 2. Monitor via WebSocket (or rely on Webhooks)
448 const socket = new WebSocket(`wss://api.splitroute.com/api/v1/ws/invoices/${invoice.invoice_id}`);
449 socket.onmessage = (event) => {
450 const message = JSON.parse(event.data);
451 if (message.payload.event_type === 'invoice.paid') {
452 console.log(`Order ${orderId} Payment Confirmed!`);
453 // Fulfill order, update database, etc.
454 socket.close();
455 } else if (message.category === 'failed') {
456 console.log(`Order ${orderId} Invoice Expired.`);
457 // Handle expiry (e.g., cancel order)
458 socket.close();
459 }
460 };
461 // Handle socket errors and closures appropriately
462
463 } catch (error) {
464 console.error(`Error processing order ${orderId}:`, error);
465 // Handle error (e.g., notify admin, show error to user)
466 }
467}
468\```
469