Create a payment
Creates a new payment request for your store. The store is implied entirely by your credentials — you never name it in the request body.
Request fields
Section titled “Request fields”| Field | Type | Required | Description |
|---|---|---|---|
asset |
string | Yes | One of BTC, LTC, DOGE, DASH, USDC, USDT. |
chain |
string | Only for multi-chain assets | Required only for assets that span more than one chain (USDC/USDT — base or polygon). Inferred automatically otherwise. |
amount.minor |
integer | Yes | Charge amount in minor units of amount.currency (e.g. 500 = $5.00). |
amount.currency |
string | Yes | ISO 4217 currency code. |
payerIp |
string | No | Your end customer’s IP, forwarded for screening. Falls back to your server’s own request IP if omitted. Can only add scrutiny — never bypasses it. |
Request
Section titled “Request”curl -s https://api.sendchain.example/v1/store-api/payments \ -u "$API_KEY:$SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{ "asset": "LTC", "chain": "litecoin", "amount": { "minor": 5000, "currency": "GBP" }, "payerIp": "203.0.113.7" }'const res = await fetch("https://api.sendchain.example/v1/store-api/payments", { method: "POST", headers: { "Authorization": "Basic " + Buffer.from(`${API_KEY}:${SECRET_KEY}`).toString("base64"), "Content-Type": "application/json", }, body: JSON.stringify({ asset: "LTC", chain: "litecoin", amount: { minor: 5000, currency: "GBP" }, payerIp: "203.0.113.7", }),});
if (!res.ok) { const err = await res.json(); throw new Error(`${res.status}: ${err.error}`);}
const payment = await res.json();import requests
response = requests.post( "https://api.sendchain.example/v1/store-api/payments", auth=(API_KEY, SECRET_KEY), json={ "asset": "LTC", "chain": "litecoin", "amount": {"minor": 5000, "currency": "GBP"}, "payerIp": "203.0.113.7", },)response.raise_for_status()payment = response.json()<?php$ch = curl_init("https://api.sendchain.example/v1/store-api/payments");curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_USERPWD => "$apiKey:$secretKey", CURLOPT_HTTPHEADER => ["Content-Type: application/json"], CURLOPT_POSTFIELDS => json_encode([ "asset" => "LTC", "chain" => "litecoin", "amount" => ["minor" => 5000, "currency" => "GBP"], "payerIp" => "203.0.113.7", ]),]);
$response = curl_exec($ch);$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);curl_close($ch);
$payment = json_decode($response, true);if ($status >= 400) { throw new Exception("{$status}: {$payment['error']}");}Response — 201 Created
Section titled “Response — 201 Created”{ "id": "pay_...", "status": "awaiting_payment", "address": "ltc1q...", "payAmount": { "base": "...", "asset": "LTC", "decimals": 8 }, "displayAmount": { "minor": 5000, "currency": "GBP" }, "rate": { "pricePerWholeUnit": "...", "expiresAt": "..." }, "chain": "litecoin", "uri": "litecoin:ltc1q...?amount=...", "quoteExpiresAt": "..."}Render this exactly like a normal pay page would: address + payAmount +
uri (QR-able) — the payer never needs to visit Send Chain directly.
Response fields
Section titled “Response fields”| Field | Type | Description |
|---|---|---|
id |
string | The payment’s id — use it to poll status or match a webhook. |
status |
string | Starts as awaiting_payment. |
address |
string | The address to display to the payer. |
payAmount.base |
string | The exact amount to send, in the asset’s smallest unit. |
payAmount.asset / payAmount.decimals |
string / integer | Asset code and its decimal precision. |
displayAmount |
{ minor, currency } |
Echoes the requested fiat amount. |
rate.pricePerWholeUnit / rate.expiresAt |
string / timestamp | The locked exchange rate and when the quote lock expires. |
chain |
string | The settlement chain. |
uri |
string | A scannable payment URI (e.g. bitcoin:… / ethereum:…@chainId). |
quoteExpiresAt |
ISO 8601 timestamp | Send before this. |
Errors
Section titled “Errors”| Status | error |
Meaning |
|---|---|---|
| 401 | invalid_credentials |
Missing, malformed, or mismatched Basic auth. |
| 403 | screening_failed |
The payer/caller failed jurisdiction or sanctions screening. |
| 409 | store_disabled |
The store is toggled off for new payments. |
| 409 | currency_unavailable |
No verified wallet for that asset, or the chain’s indexer is down. |
| 429 | rate_limited |
Exceeded your per-minute cap — respect Retry-After. |