Callbacks
What TrezorPay sends to your callback URL, how it retries, and how to verify the RSA-SHA256 signature.
When callbacks are sent
| Moment | pending | Sent when |
|---|---|---|
| Payment seen | 1 | Only if you created the address with pending=1 |
| Payment forwarded | 0 | Always, once the amount has been sent to your wallet |
Each payment to an address gets its own pair of callbacks. Addresses never expire.
Transport
- GET (default): every field is added to the query string of your callback URL.
- POST form (
post=1): fields in anapplication/x-www-form-urlencodedbody. - POST JSON (
json=1): fields in a JSON body.
Your own query parameters always stay on the URL.
Fields
| Field | Description |
|---|---|
uuid | Payment id, identical on its pending and confirmed callback |
address_in | The address that was paid |
address_out | Your wallet, or the split |
txid_in | Customer's transaction |
txid_out | Our forwarding transaction (confirmed only) |
confirmations | Confirmations at the time of the callback |
value_coin | Amount paid |
value_forwarded_coin | Amount sent to your wallet (confirmed only) |
fee_coin | Our fee (confirmed only) |
coin | Ticker in underscore form, such as trc20_usdt |
price | Coin price in USD |
pending | 1 or 0 |
result | pending or sent |
value_coin_convert | Fiat values, with convert=1 |
value, value_forwarded and fee are legacy integer fields: read the _coin strings instead.
Retries
Answer HTTP 200 with the body *ok* within 30 seconds. Anything else is retried after 6, 12, 24 minutes and
so on, doubling, until the payment is 3 days old. Every attempt is visible in /{ticker}/logs/.
Verifying the signature
Every callback carries x-ca-signature: a base64 RSA-SHA256 (PKCS#1 v1.5) signature of the full URL for GET
callbacks, or of the raw body for POST callbacks. Verify it with the key from /pubkey/.
import { createVerify } from 'node:crypto'
export function verify(signedData, signature, pubkey) {
return createVerify('sha256').update(signedData).verify(pubkey, signature, 'base64')
}import base64
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding
def verify(signed_data: bytes, signature: str, pubkey_pem: str) -> bool:
key = serialization.load_pem_public_key(pubkey_pem.encode())
try:
key.verify(base64.b64decode(signature), signed_data, padding.PKCS1v15(), hashes.SHA256())
return True
except Exception:
return False<?php
function verify(string $signedData, string $signature, string $pubkey): bool {
return openssl_verify($signedData, base64_decode($signature), $pubkey, OPENSSL_ALGO_SHA256) === 1;
}