{"openapi":"3.1.0","info":{"title":"PurpleRail API","version":"0.1.0","description":"Accept crypto, settle in fiat. Create a payment, send the customer to the hosted checkout (or give them a deposit address), and receive webhooks as the payment is confirmed, screened, converted and paid out."},"servers":[{"url":"https://purplerail.ecomstat.io","description":"Sandbox (test mode, simulated providers)"}],"security":[{"bearer":[]}],"tags":[{"name":"Payments","description":"Fixed-amount payments with a hosted checkout."},{"name":"Deposit wallets","description":"Open-amount deposit addresses; whatever arrives is settled at the achieved rate."},{"name":"Payment links","description":"Shareable links / invoices that create a payment when opened."},{"name":"Payouts","description":"Per-payment payouts and daily settlement batches."},{"name":"PSP decisions","description":"For PSPs that make the acceptance decision (decision model B)."},{"name":"Reference","description":"Supported assets and currencies."},{"name":"Test mode","description":"Sandbox-only helpers."}],"paths":{"/v1/payments":{"post":{"tags":["Payments"],"summary":"Create a payment","operationId":"createPayment","description":"Idempotent on `reference`: repeating the same request returns the same payment; reusing a reference with a different amount is refused (409).","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reference","amount","currency"],"properties":{"reference":{"type":"string","maxLength":100,"example":"order-1042"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Decimal string in major units, e.g. \"100.00\". Never a float.","example":"100.00"},"currency":{"type":"string","example":"EUR","description":"One of /v1/currencies. Minor units follow ISO 4217 (JPY has none)."},"description":{"type":"string","maxLength":500,"example":"Order #1042 – 2 items"},"return_url":{"type":"string","format":"uri","description":"Where the checkout sends the customer when done.","example":"https://shop.example/thanks"},"metadata":{"type":"object","additionalProperties":true,"example":{"cart_id":"c_88"}}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Payment"}}}},"400":{"$ref":"#/components/responses/Error"},"401":{"$ref":"#/components/responses/Error"},"409":{"$ref":"#/components/responses/Error"}}},"get":{"tags":["Payments"],"summary":"List payments","operationId":"listPayments","parameters":[{"name":"status","in":"query","schema":{"$ref":"#/components/schemas/Status"}},{"name":"limit","in":"query","schema":{"type":"integer","maximum":100,"default":25}}],"responses":{"200":{"description":"OK","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#/components/schemas/Payment"}}}}}}}}}},"/v1/payments/{id}":{"get":{"tags":["Payments"],"summary":"Retrieve a payment","operationId":"getPayment","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}},{"name":"expand","in":"query","schema":{"type":"string","enum":["timeline"]},"description":"Include the status history and payouts."}],"responses":{"200":{"description":"OK (PSP keys also receive an `evidence` object with the AML screening results)","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Payment"}}}},"404":{"$ref":"#/components/responses/Error"}}}},"/v1/deposit-wallets":{"post":{"tags":["Deposit wallets"],"summary":"Create a deposit wallet","operationId":"createDepositWallet","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["reference","currency"],"properties":{"reference":{"type":"string"},"currency":{"type":"string","example":"USDT@tron","description":"Asset key from /v1/assets."},"description":{"type":"string"},"metadata":{"type":"object"}}}}}},"responses":{"201":{"description":"Created; `quote.deposit_address` is where funds go.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Payment"}}}},"400":{"$ref":"#/components/responses/Error"}}}},"/v1/payment-links":{"post":{"tags":["Payment links"],"summary":"Create a payment link","operationId":"createLink","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["title","currency"],"properties":{"title":{"type":"string","example":"Invoice #1042"},"description":{"type":"string"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Omit to let the payer enter the amount."},"currency":{"type":"string","example":"GBP"},"single_use":{"type":"boolean","description":"Invoice: can be paid once."},"expires_at":{"type":"string","format":"date-time"}}}}}},"responses":{"201":{"description":"Created","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PaymentLink"}}}}}},"get":{"tags":["Payment links"],"summary":"List payment links","operationId":"listLinks","responses":{"200":{"description":"OK"}}}},"/v1/payouts":{"get":{"tags":["Payouts"],"summary":"List per-payment payouts","operationId":"listPayouts","responses":{"200":{"description":"OK"}}}},"/v1/settlements":{"get":{"tags":["Payouts"],"summary":"List daily settlements","operationId":"listSettlements","responses":{"200":{"description":"OK"}}}},"/v1/settlements/{id}/report.csv":{"get":{"tags":["Payouts"],"summary":"Settlement report (CSV)","operationId":"settlementReport","description":"One line per payment: order amount, token received, rate, gross, PurpleRail fee and net. Derived from the ledger.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"responses":{"200":{"description":"CSV","content":{"text/csv":{}}}}}},"/v1/decisions/{id}":{"post":{"tags":["PSP decisions"],"summary":"Accept, review or reject a payment","operationId":"decide","description":"Sent in response to the `payment.decision_required` webhook, before the deadline in `respond_by`. PurpleRail always blocks sanctioned funds, whatever the decision. Without a response the PSP's timeout action applies.","parameters":[{"name":"id","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["decision"],"properties":{"decision":{"type":"string","enum":["accept","review","reject"]},"reason":{"type":"string","maxLength":500}}}}}},"responses":{"200":{"description":"Applied","content":{"application/json":{"schema":{"$ref":"#/components/schemas/Payment"}}}},"409":{"$ref":"#/components/responses/Error"}}}},"/v1/assets":{"get":{"tags":["Reference"],"summary":"Supported tokens and networks","security":[],"operationId":"assets","responses":{"200":{"description":"OK"}}}},"/v1/currencies":{"get":{"tags":["Reference"],"summary":"Supported fiat currencies","security":[],"operationId":"currencies","responses":{"200":{"description":"OK"}}}},"/sim/deposits":{"post":{"tags":["Test mode"],"summary":"Simulate an on-chain deposit","operationId":"simDeposit","description":"Sandbox only. Pretends a customer sent funds to a deposit address; confirmations then arrive every `tick_seconds`. Use `from_address` containing \"mix\", \"bad\" or \"dead\" to trigger medium, high or sanctioned AML results.","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["address"],"properties":{"address":{"type":"string"},"amount":{"type":"string","pattern":"^\\d+(\\.\\d+)?$","description":"Defaults to the quoted total."},"currency":{"type":"string"},"from_address":{"type":"string"},"tick_seconds":{"type":"number","default":2}}}}}},"responses":{"202":{"description":"Accepted"}}}}},"webhooks":{"payment.status_changed":{"post":{"summary":"A payment changed status","description":"Body: `{ id, type, created_at, data: { payment, previous_status } }`.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Return any 2xx to acknowledge"}}}},"payment.decision_required":{"post":{"summary":"PSPs only: a payment needs your decision","description":"Body data: `{ payment, evidence, respond_by, respond_to }`.","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/WebhookEvent"}}}},"responses":{"200":{"description":"Acknowledged"}}}}},"components":{"securitySchemes":{"bearer":{"type":"http","scheme":"bearer","description":"API key: `pr_test_…` in the sandbox, `pr_live_…` in production. Keep it server-side."}},"responses":{"Error":{"description":"Error","content":{"application/json":{"schema":{"type":"object","properties":{"error":{"type":"object","properties":{"code":{"type":"string","example":"invalid_request"},"message":{"type":"string"}}}}}}}}},"schemas":{"Status":{"type":"string","enum":["created","awaiting_payment","payment_detected","confirming","underpaid","expired","screening","decision_pending","under_review","accepted","converting","settling","settled","rejected","refund_pending","refunded","failed"]},"Quote":{"type":"object","properties":{"id":{"type":"string"},"currency":{"type":"string","example":"USDC@ethereum"},"symbol":{"type":"string"},"network":{"type":"string"},"rate":{"type":"string","description":"Fiat per token, after spread"},"amount":{"type":"string"},"network_fee":{"type":"string"},"total":{"type":"string","description":"Exactly what the customer must send"},"deposit_address":{"type":"string"},"fee_policy":{"type":"string","enum":["customer","split","merchant"]},"expires_at":{"type":"string","format":"date-time"}}},"Payment":{"type":"object","properties":{"id":{"type":"string","example":"pay_7q2k9x4mf1d8c3a"},"kind":{"type":"string","enum":["checkout","deposit_wallet"]},"reference":{"type":"string","example":"order-1042"},"description":{"type":["string","null"]},"status":{"$ref":"#/components/schemas/Status"},"amount":{"type":"object","properties":{"currency":{"type":"string","example":"EUR"},"value":{"type":["string","null"],"example":"100.00"}}},"checkout_url":{"type":["string","null"],"example":"https://purplerail.ecomstat.io/checkout/pay_7q2k9x4mf1d8c3a"},"quote":{"oneOf":[{"$ref":"#/components/schemas/Quote"},{"type":"null"}]},"received":{"type":"array","items":{"type":"object","properties":{"currency":{"type":"string"},"amount":{"type":"string"},"confirmed":{"type":"boolean"}}}},"late":{"type":"boolean","description":"Funds arrived after the quote expired."},"decision":{"type":["object","null"],"properties":{"decision":{"type":"string","enum":["accept","review","reject"]},"source":{"type":"string"}}},"metadata":{"type":"object","additionalProperties":true},"expires_at":{"type":"string","format":"date-time"},"created_at":{"type":"string","format":"date-time"},"updated_at":{"type":"string","format":"date-time"},"mode":{"type":"string","enum":["test","live"]}}},"PaymentLink":{"type":"object","properties":{"id":{"type":"string"},"title":{"type":"string"},"amount":{"type":["string","null"]},"currency":{"type":"string"},"single_use":{"type":"boolean"},"uses":{"type":"integer"},"url":{"type":"string"}}},"WebhookEvent":{"type":"object","properties":{"id":{"type":"string","example":"evt_…"},"type":{"type":"string"},"created_at":{"type":"string"},"data":{"type":"object"}}}}}}