Docs
PrivCart · Integration

Accept crypto in one API call.

Create an invoice from your server, hand the customer a hosted checkout, and receive a signed webhook the moment it settles. Twelve assets across five chains — behind one Bearer key.

01 Get started

Two ways to get a key

Every request to the API is authenticated with an API key. Pick whichever fits you — both mint a live pck_ key you use the same way.

Option A — Email account (recommended)

  1. Register at /signup with an email address.
  2. Verify your email from the link we send — an unverified account cannot create invoices or mint a key.
  3. Generate an API key from your dashboard. It is issued after verification and shown once.

Option B — Instant, no email

Register a pseudonymous merchant in a single call. No email, a flat 1% fee, and the key is returned inline — shown once.

POST/v1/merchants
bash
curl -X POST https://privcart.xyz/v1/merchants \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Your store",
    "allowed_origins": ["https://yourstore.example"]
  }'
201 · json
{
  "api_key": "pck_…",
  "merchant_id": "…",
  "name": "Your store",
  "fee_bp": 100
}

allowed_origins lists the site origins allowed to embed your hosted checkout in an <iframe> (see Checkout & embed). Supply at least one bare origin — scheme + host, no path or wildcards. fee_bp: 100 is the 1% fee in basis points.

Save the key now — it is shown once. PrivCart stores only a hash and can never show it again. Put api_key straight into your server's secret manager before you close the response. Lost it? Mint a new one and the old one stops working.
02 Authentication

One bearer key, server-side only

Send your key as a Bearer token on every /v1/* request. A missing or invalid key returns 401.

http
GET /v1/invoices
Host: privcart.xyz
Authorization: Bearer pck_…
The key is a server-side secret. It authenticates your backend — anyone holding it can create invoices and move your funds. Never ship it to a browser, a mobile app, or the checkout iframe. Keep it in an environment variable or secret manager and make API calls from your server.

Public exceptions

Two surfaces need no key — they're what your customer touches:

  • The hosted checkout pageGET /checkout/{invoiceId} renders the QR, address, and live status.
  • Its status streamGET /v1/invoices/{id}/stream is an SSE feed with Access-Control-Allow-Origin: *.

Both are gated by the unguessable invoice id (122-bit), so a customer can pay without ever seeing your key.

03 Create an invoice

Create an invoice

Ask for an asset and an amount; PrivCart returns a unique receive address and a checkout you can hand to the customer.

POST/v1/invoices
  • asset requiredOne of the twelve supported symbols, e.g. BTC.
  • amount requiredA string in the asset's main unit (string avoids float rounding), e.g. "0.0025".
  • order_id optionalYour own reference, echoed back and in webhooks.
  • callback_url optionalHTTPS endpoint for signed webhooks.
  • ttl_ms optionalTime-to-pay in ms (1000 – 86 400 000). Defaults apply if omitted.
bash
curl -X POST https://privcart.xyz/v1/invoices \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "asset": "BTC",
    "amount": "0.0025",
    "order_id": "order-1042",
    "callback_url": "https://yourstore.example/webhooks/privcart"
  }'
201 · json
{
  "id": "0da553a9-e7f0-4b72-b050-0fdd0db1ccca",
  "asset": "BTC",
  "amount": "0.0025",
  "receive_address": "bc1q…",
  "status": "pending",
  "confirmations": 0,
  "min_confirmations": 2,
  "expires_at": 1754064000000
}
Create invoices server-side. A direct browser POST is blocked by CORS and would leak your key. The receive_address is unique to this invoice — show it yourself, or just redirect to the hosted checkout below.
04 Checkout & embed

Hosted checkout & embed

PrivCart hosts the payment page — a QR, the exact pay-to address, and a status that updates live as the payment is detected and confirmed. Point the customer at it, or embed it.

Redirect to the hosted page

url
https://privcart.xyz/checkout/0da553a9-e7f0-4b72-b050-0fdd0db1ccca

Embed in your page

html
<iframe
  src="https://privcart.xyz/checkout/INVOICE_ID"
  title="Pay with PrivCart"
  width="100%" height="640"
  style="max-width:460px;border:0;border-radius:16px"
  allow="clipboard-write">
</iframe>
The iframe URL carries only the random invoice id — never your API key. Add the embedding site to your allowed_origins at registration so the checkout accepts being framed there.

No-code pay links (no server required)

Create a pay link for a product in your dashboard — a label, an asset, and a fixed price (or leave the price open for donations). You get a permanent link you can embed anywhere. When a customer opens it, PrivCart mints the invoice server-side and shows the checkout — you never touch an API key and need no backend.

html
<iframe src="https://privcart.xyz/l/LINK_ID"
  title="Pay with crypto" width="100%" height="540"
  style="max-width:420px;border:0;border-radius:12px"></iframe>

Prefer a button, or a script that mounts it for you? The dashboard's embed builder gives you an <a> button and a PrivCart.payLink() script for the same link, each with a live preview and a one-click copy.

Copy prompt for AI

Not a developer? Every pay link ships with a ready-made AI prompt — copy it from the dashboard and paste it into ChatGPT, Claude, or your site builder's assistant. It tells the AI exactly where to place the button on the product page for your platform (WordPress / WooCommerce, Shopify, or custom) and never to alter the fixed price.

A fixed pay link's price is set on PrivCart's server and cannot be changed by the embedding page — there is no amount parameter. Add your store's origin under Allowed embed origins in the dashboard so the pay page will load framed on your site.
05 Status & realtime

Status & live updates

Read the current state on demand, or subscribe to a stream and react the instant it changes.

GET/v1/invoices/:id

Returns the full invoice — status, confirmations, tx_id, and more. Requires your key (server-side).

GET/v1/invoices/:id/stream

A Server-Sent Events stream of status updates. No key required, CORS * — safe to consume straight from the browser.

pending confirming settled /expired
javascript
const es = new EventSource(
  "https://privcart.xyz/v1/invoices/INVOICE_ID/stream"
);

es.onmessage = (ev) => {
  const { status, confirmations, min_confirmations } = JSON.parse(ev.data);
  render(status, confirmations, min_confirmations);
  if (status === "settled" || status === "expired") es.close();
};

es.onerror = () => {
  // transient — the browser reconnects automatically
};
06 Webhooks

Signed webhooks

Set a callback_url when you create an invoice (or in dashboard settings). On every status change, PrivCart POSTs a signed JSON callback to that URL.

invoice.created invoice.confirming invoice.settled invoice.expired
http · delivered to your callback_url
POST /webhooks/privcart
X-PrivCart-Signature: sha256=<hmac-hex>
X-PrivCart-Event-Id: id_…
Content-Type: application/json

{
  "id": "id_…",
  "event": "invoice.settled",
  "created_at": 1754064000000,
  "data": {
    "invoice": {
      "id": "0da553a9-e7f0-4b72-b050-0fdd0db1ccca",
      "asset": "BTC",
      "amount": "0.0025",
      "status": "settled",
      "confirmations": 2,
      "min_confirmations": 2
    }
  }
}

Verify before you trust it

The signature is sha256= + HMAC-SHA256 of the raw request body, keyed by your webhook signing secret. Compute it and compare in constant time before parsing the body.

javascript · node
import { createHmac, timingSafeEqual } from "node:crypto";

// rawBody: the exact bytes PrivCart POSTed — verify BEFORE JSON.parse
export function verify(rawBody, signatureHeader, secret) {
  const expected =
    "sha256=" + createHmac("sha256", secret).update(rawBody).digest("hex");
  const a = Buffer.from(signatureHeader ?? "");
  const b = Buffer.from(expected);
  return a.length === b.length && timingSafeEqual(a, b);
}

Rotate the signing secret

POST/v1/webhooks/rotate-secret
bash
curl -X POST https://privcart.xyz/v1/webhooks/rotate-secret \
  -H "Authorization: Bearer YOUR_API_KEY"
# → { "webhook_secret": "wcs_…" }   (shown once)
Never settle an order on an unverified POST. Delivery is at-least-once with retry/backoff, so make your handler idempotent — dedupe on X-PrivCart-Event-Id. The new secret from a rotation is shown once, just like an API key.
07 Supported assets

Twelve supported assets

Five native chains plus seven ERC-20 tokens. Confirmation depth is chain-appropriate; times are typical, not guaranteed.

AssetNameNetworkConfirmations~ Confirm time
BTCBitcoinBitcoin2~20 min
ETHEthereumEthereum12~3 min
SOLSolanaSolana1~1 sec
XMRMoneroMonero10~20 min
LTCLitecoinLitecoin3~8 min
USDCUSD CoinEthereum · ERC-2012~3 min
USDTTetherEthereum · ERC-2012~3 min
DAIDaiEthereum · ERC-2012~3 min
USDSSky DollarEthereum · ERC-2012~3 min
WBTCWrapped BTCEthereum · ERC-2012~3 min
XAUTTether GoldEthereum · ERC-2012~3 min
ZCHFSwiss FrancEthereum · ERC-2012~3 min
08 Fees & settlement

Fees & settlement

New accounts pay 1% per transaction (fee_bp: 100). The fee is booked to a ledger as each payment settles — never skimmed mid-transfer — so your available balance is always gross receipts minus accrued fees.

Custodial-lite. PrivCart derives a unique HD receive address per invoice. Funds land there, are swept to treasury once confirmed on-chain, and credited to your merchant balance. You withdraw available balances from the merchant dashboard to an address you control.

customer pays confirmed on-chain settled & swept balance (net of 1%) you withdraw
You always withdraw to an address you control. Balances and payouts live in your dashboard — no fee is ever taken from the amount your customer pays.
09 Go-live checklist

Go-live checklist

Four things to confirm before you take real money.

  • Key stored server-side. In a secret manager or environment variable — never in a browser bundle, mobile app, or the checkout iframe.
  • callback_url set & signature verified. Your handler validates the raw-body HMAC in constant time before it fulfils an order.
  • Tested on a small real amount. One end-to-end run: create → pay → settled → webhook → withdraw.
  • Dashboard bookmarked. For monitoring invoices and withdrawing your balances.