| 1 | |
| 2 | # SplitRoute API Documentation - AI Code Context |
| 3 | |
| 4 | ## Overview |
| 5 | SplitRoute is a payment processing API for Nano cryptocurrency that enables instant revenue sharing and payment distribution. |
| 6 | Create standard, perpetual, or simulated invoices, split payments among multiple recipients, and automate payouts. |
| 7 | |
| 8 | \``` |
| 9 | Key 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 | |
| 18 | All authenticated endpoints require an API key in the header: |
| 19 | |
| 20 | \```http |
| 21 | X-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 | \``` |
| 37 | POST /api/v1/api-keys/register |
| 38 | |
| 39 | Request: |
| 40 | { |
| 41 | "email": "[email protected]", |
| 42 | "name": "your.key.name" |
| 43 | } |
| 44 | |
| 45 | Response (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 | \``` |
| 62 | POST /api/v1/api-keys/purchase |
| 63 | |
| 64 | Headers: |
| 65 | X-API-Key: your_current_api_key |
| 66 | |
| 67 | Request: |
| 68 | { |
| 69 | "tier": "BASIC", # Required. One of: BASIC, PRO, ENTERPRISE |
| 70 | "billing_period": "monthly" # Optional. One of: monthly, annual (default: monthly) |
| 71 | } |
| 72 | |
| 73 | Response: |
| 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 | |
| 82 | Creates a standard (expiring) or perpetual (non-expiring) invoice. Can also simulate creation. |
| 83 | |
| 84 | \``` |
| 85 | POST /api/v1/invoices |
| 86 | |
| 87 | Headers: |
| 88 | X-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 | |
| 195 | Retrieves the full Invoice object by its public ID. |
| 196 | |
| 197 | \``` |
| 198 | GET /api/v1/invoices/{invoice_id} |
| 199 | |
| 200 | Response: Full Invoice Object (see Create Invoice Response) |
| 201 | \``` |
| 202 | |
| 203 | ### Get Invoice by Secret ID |
| 204 | |
| 205 | Retrieves the full Invoice object by its secret ID. |
| 206 | |
| 207 | \``` |
| 208 | GET /api/v1/invoices/secret/{secret_id} |
| 209 | |
| 210 | Response: Full Invoice Object (see Create Invoice Response) |
| 211 | \``` |
| 212 | |
| 213 | ### Get Supported Currencies |
| 214 | |
| 215 | Lists currencies usable in the `nominal_currency` field. |
| 216 | |
| 217 | \``` |
| 218 | GET /api/v1/currencies |
| 219 | |
| 220 | Response: |
| 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 | |
| 245 | When creating an invoice using `destinations`, you define how funds are split: |
| 246 | |
| 247 | ### 1. Primary Destination (`primary: true`) |
| 248 | Receives 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`) |
| 254 | Receives 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`) |
| 260 | Receives 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 | |
| 267 | Distributions are calculated in this order from the total received: |
| 268 | 1. **Service Fee:** (e.g., 0.5% for FREE tier) |
| 269 | 2. **Fixed Amounts:** Destinations with `nominal_amount`. |
| 270 | 3. **Percentage Amounts:** Destinations with `percentage` (calculated on remaining balance). |
| 271 | 4. **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 | \``` |
| 284 | Distribution: |
| 285 | 1. Service Fee: $0.50 (to SplitRoute) -> Remaining: $99.50 |
| 286 | 2. Fixed (Platform): $10.00 -> Remaining: $89.50 |
| 287 | 3. Percentage (Partner): 20% of $89.50 = $17.90 -> Remaining: $71.60 |
| 288 | 4. Primary (Seller): $71.60 |
| 289 | |
| 290 | ## Invoice Lifecycle (Standard Invoices) |
| 291 | |
| 292 | 1. **Created**: Ready for payment (`is_paid`, `is_expired`, etc. are false). |
| 293 | 2. **Pending**: Partially paid (`is_pending: true`). |
| 294 | 3. **Paid**: Full required amount received (`is_paid: true`). |
| 295 | 4. **Forwarded**: Funds sent to destinations (`is_forwarded: true`). |
| 296 | 5. **Done**: Processing complete (Paid & Forwarded) (`is_done: true`). |
| 297 | 6. **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 | |
| 305 | Receive 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 |
| 344 | const invoiceId = 'INV_...'; // Your Invoice ID |
| 345 | const socket = new WebSocket(`wss://api.splitroute.com/api/v1/ws/invoices/${invoiceId}`); |
| 346 | |
| 347 | socket.onopen = (e) => console.log('WebSocket Connected'); |
| 348 | socket.onerror = (error) => console.error(`WebSocket Error: ${error.message}`); |
| 349 | socket.onclose = (event) => console.log(`WebSocket Closed: Code=${event.code}, Reason=${event.reason}`); |
| 350 | |
| 351 | socket.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 | |
| 375 | Receive 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 |
| 385 | import hmac, json, hashlib |
| 386 | |
| 387 | def 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 | |
| 402 | API 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 | \``` |
| 412 | Common 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 | |
| 414 | Rate 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 |
| 421 | async 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 | |