The Accept Crypto HERE API lets you accept crypto, track payments, and manage payouts. Base URL:
https://your-host/v1 # self-hosted; locally: http://localhost:3000/v1
All requests and responses are JSON. Amounts are decimal strings (e.g. "49.99"). The only Accept Crypto HERE fee is a flat 0.25% (0.5% when a payment is converted to another coin); blockchain network fees on payouts are paid by you.
Every /v1 request is authenticated with a key as a Bearer token (or the x-api-key header). There are two key types:
| Key | Prefix | Use |
|---|---|---|
| Secret key | pic_live_ | Server-side only. Full access — payments, balances, payouts, withdrawals. Stored as a hash, shown once. |
| Publishable key | pic_pub_ | Client-safe. Can only create payments, read a payment, and quote. Anything sensitive returns 403. |
curl https://your-host/v1/balance?asset=BTC \
-H "Authorization: Bearer pic_live_your_secret_key"
You also get a webhook secret (whsec_…) for verifying IPN callbacks. Find all three in the dashboard under Settings → API keys, or get them programmatically (below). Rotating immediately invalidates the previous keys:
# -> { "secret_key": "pic_live_…", "publishable_key": "pic_pub_…" } (secret shown once)
Create a merchant account + key in one call (no auth required), then start creating payments:
curl -X POST https://your-host/v1/merchants \ -H "Content-Type: application/json" \ -d '{"name":"My store","webhook_url":"https://my-store.com/ipn"}' # -> { "merchant_id": "mer_…", "secret_key": "pic_live_…", # "publishable_key": "pic_pub_…", "webhook_secret": "whsec_…" }
Save the secret_key and webhook_secret — they are shown only once (the publishable key is always retrievable from GET /v1/account).
| Field | Type | Notes |
|---|---|---|
| asset | string | Coin to receive, e.g. BTC, USDT.ETH, USDT.TRON. See GET /v1/assets. |
| amount | string | Amount to charge in asset. |
| order_id | string? | Your reference (optional). |
| settle_as | string? | Settle (convert) into this coin instead — uses the 0.5% tier. |
curl -X POST https://your-host/v1/invoices \ -H "Authorization: Bearer pic_live_…" -H "Content-Type: application/json" \ -d '{"asset":"USDT.ETH","amount":"49.99","order_id":"order-123"}'
{
"id": "inv_8f3c…",
"asset": "USDT.ETH",
"status": "new",
"pay_address": "0x…",
"pay_amount": "49.990001", // exact amount the customer must send
"amount": "49.99",
"fee": "0.124975",
"settle_asset": null,
"expires_at": 1782579331203
}
Show the customer pay_address and the exact pay_amount — or just redirect them to the hosted checkout.
Send the customer to the drop-in payment page (QR, exact amount, live status). No frontend to build:
https://your-host/pay/{invoice_id}
The page polls a public, read-only status endpoint and shows a success screen when confirmed:
curl https://your-host/v1/invoices/inv_8f3c… -H "Authorization: Bearer pic_live_…"
| Status | Meaning |
|---|---|
| new | Created; awaiting payment. |
| partially_paid | Some funds received, below the amount. |
| confirming | Full amount seen; waiting for confirmations. |
| paid | Confirmed and credited. Terminal. |
| expired | Window closed without sufficient payment. Terminal. |
{ "from":"BTC", "to":"USDT.ETH", "to_amount":"602.81", "fee_bps":50, "source":"price-oracle" }
Returns your payout settings, per-coin balances (with live usd_value), total_usd, webhook URL, and the supported assets list.
{ webhook_url?, consolidate_to? }| Field | Notes |
|---|---|
| asset | Coin these settings apply to. |
| payout_address | Where this coin is sent. |
| mode | instant (auto-pay each payment) or manual (accrue a balance). |
| instant_min_amount | Skip instant payouts below this (so the network fee never exceeds the payout). |
{ asset, amount?, to_address?, consolidate_to? }{ to } (consolidate all balances into one coin)Payout amounts are before network fees — the blockchain fee is deducted from what is actually sent (net_of_network_fee: false).
When an invoice changes status we POST JSON to your webhook_url with these headers:
| Header | Value |
|---|---|
| x-payincrypto-event | invoice.paid, invoice.confirming, invoice.partially_paid, invoice.expired |
| x-payincrypto-signature | sha256=<hmac> of the raw body, keyed with your webhook_secret |
Body: { "type": "...", "created_at": 0, "data": { …the invoice… } }. Always verify the signature before trusting it:
import crypto from "node:crypto"; function verify(rawBody, signature, secret) { const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex"); const a = Buffer.from(expected), b = Buffer.from(signature || ""); return a.length === b.length && crypto.timingSafeEqual(a, b); } // in your handler (use the RAW request body, not re-serialized JSON): if (!verify(rawBody, req.headers["x-payincrypto-signature"], WEBHOOK_SECRET)) { return res.status(400).end(); }
Delivery retries with exponential backoff until your endpoint returns 2xx.
Errors return a JSON body { "error": "code", "message": "…" } with a matching HTTP status:
| Status | When |
|---|---|
| 400 | Validation, unsupported asset, invalid amount. |
| 401 | Missing or invalid API key / session. |
| 404 | Resource not found. |
| 503 | A dependency (price oracle, address generation) is temporarily unavailable. |
Need something here that's missing? Everything the dashboard does is a public API call — open your browser dev tools on the dashboard to see live examples.