POST /v1/qr
Render a base64 PNG QR code for the order pay-in address.
/api/v1/qrAuth requiredReturns a base64 PNG data: URL of the order’s pay-in address, rendered at 256×256 with error-correction level M and a 2-module quiet zone. Drop the value directly into an <img src="…"> to show the deposit QR inline.
Caching. Successful responses are served from a one-hour server-side cache. The cached PNG is immutable per order, and every call is gated by partner ownership before the cache is consulted.
Request
idstring (UUID)requiredtokenstring (43-char base64url)requiredResponse
{
"code": 0,
"msg": "",
"data": {
"qrPng": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAA..."
}
}data.qrPngstring (data: URL)data:image/png;base64,… of the rendered QR. The encoded payload is the raw pay-in address; the address itself never appears in this response and must be fetched from data.from.address on /v1/order.Errors
| code | msg | HTTP | Description |
|---|---|---|---|
| 1 | INVALID_REQUEST | 400 | Body fails validation: unsupported asset, disabled pair, unparseable amount, invalid address, FLOAT without refundAddress, malformed Idempotency-Key. The same code + HTTP 409 fires on Idempotency-Key reuse with a different payload. |
| 2 | AUTH_REQUIRED | 401 | One of X-API-KEY / X-API-SIGN is missing on an endpoint that requires authentication. |
| 3 | AUTH_INVALID | 401 | Unknown key, malformed signature, signature mismatch, expired or replayed nonce, decrypt failure. Generic body — never an oracle for "which" of those it was. |
| 4 | AUTH_DISABLED | 401 | Partner record enabled = false. Reachable only with a valid signature, so the operator can distinguish a kill-switched partner from a stolen-and-revoked key. |
| 5 | RATE_LIMIT | 429 | Per-partner weight budget exhausted within the 60-second sliding window. Response includes Retry-After: <seconds>. |
| 6 | NOT_FOUND | 404 | Order does not exist OR exists but belongs to a different partner OR exists and the token is wrong. The three cases collapse to one envelope. |
| 99 | INTERNAL | 500 | Unexpected server-side condition. Already logged on our side; safe to retry. |
Code examples
# Returns base64 PNG data URI of the order's pay-in address.
APIKEY="rWqZ...Rg"
APISECRET="G1JV...n4"
NONCE=$(openssl rand -hex 16)
BODY='{"id":"1f8c5e0b-9c92-4d6a-b0a8-3c5f8d6e7a4b","token":"v8k3-Po9...43-char-base64url"}'
SIGN=$(printf '%s' "$BODY" | openssl dgst -sha256 -hmac "$APISECRET" | sed 's/^.* //')
curl -sS -X POST "https://0trace.io/api/v1/qr" \
-H "Content-Type: application/json" \
-H "X-API-KEY: $APIKEY" \
-H "X-API-SIGN: $SIGN" \
-H "X-API-NONCE: $NONCE" \
--data "$BODY"Rate limit
Weight 5 per call. 5 weight units per call (heavier than /v1/order's 1 because PNG generation is CPU, lighter than /v1/create's 50).
Notes
Soft-deleted orders. Cached QRs are not served for soft-deleted orders. The route re-checks visibility on every call, even when the cache would otherwise hit. The response collapses to code 6 NOT_FOUND in that case.
No raw address in the body. Use this endpoint when you want a QR. Use /v1/order when you want the text address. They are separate so a UI that only renders a QR does not need to handle the address string format.