You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Every variable this service reads, generated from the Settings model that
reads them. The environment is the only configuration source; a .env file
next to the process is read too, and a real environment variable wins over it.
.env.example
is the annotated copy to start from — same variables, more operator prose. This
page is the one that cannot go out of date.
A required variable has no default and the process will not start without
it. Values marked secret are never logged and never printed by any
endpoint. Amounts are always integers in the asset's smallest unit — satoshis
for BTC and BTC_LN, micro-USDT for USDT_TRC20.
Process
Variable
Type
Default
Notes
ENVIRONMENT
development | test | staging | production
production
Also selects the API key prefix: development and test mint cpk_test_ keys, everything else mints cpk_live_ ones.
DEBUG
bool
false
Verbose errors and internals in responses. Refused unless ENVIRONMENT=development.
LOG_LEVEL
str
INFO
Python logging level for the structlog pipeline.
LOG_JSON
bool
true
One JSON object per line for a log collector; false gives human-readable console output.
Database
Variable
Type
Default
Notes
DATABASE_URL
str
—
required — The ledger. Never the same instance as BTCPay's own database.
DB_POOL_SIZE
int
5
≥ 1 — Postgres connections are the scarce resource on a 4GB box shared with BTCPay and bitcoind, so both pool numbers stay small.
DB_MAX_OVERFLOW
int
5
≥ 0 — Connections opened beyond the pool when it is exhausted.
DB_ECHO
bool
false
Echo every SQL statement. Debugging only, and extremely noisy.
HTTP server
Variable
Type
Default
Notes
API_HOST
str
0.0.0.0
Bind address inside the container. The API publishes no ports in production; BTCPay and the platform reach it over the Docker network.
API_PORT
int
8000
Port the API listens on inside the container.
Idempotency-Key handling
Variable
Type
Default
Notes
IDEMPOTENCY_STALE_SECONDS
int
60
≥ 1 — An in_progress idempotency row older than this may be reclaimed by a retry, so a crash between "row inserted" and "work done" cannot turn into a permanent 409 on a money endpoint.
IDEMPOTENCY_TTL_HOURS
int
72
≥ 1 — How long completed idempotency records are kept before the purge job removes them.
Asset seed values — BTC (satoshis)
Variable
Type
Default
Notes
SEED_BTC_PAYMENT_METHOD
str
BTC-CHAIN
BTCPay's payment method id for on-chain BTC. Version-dependent; the real value is discovered from the store at startup.
SEED_BTC_WITHDRAWAL_AUTO_LIMIT
int
500000
≥ 0 — Withdrawals at or below this gross amount approve themselves. Above it they queue for an admin.
SEED_BTC_WITHDRAWAL_DAILY_CAP
int
5000000
≥ 0 — Rolling 24-hour per-asset cap. Once the window sum reaches it every withdrawal goes to manual approval regardless of size — the control that actually bounds a stolen-API-key attack.
SEED_BTC_WITHDRAWAL_USER_DAILY_CAP
int
(unset)
Optional rolling 24-hour cap per user. Unset disables it.
SEED_BTC_WITHDRAWAL_MIN
int
1
≥ 1 — Smallest accepted gross withdrawal. The dust rule applies on top.
SEED_BTC_WITHDRAWAL_FLAT_FEE
int
0
≥ 0 — Flat service fee charged on top of the estimated miner fee.
Asset seed values — Lightning, BTC_LN (satoshis)
Variable
Type
Default
Notes
LIGHTNING_ENABLED
bool
false
Lightning is off unless a deployment asks for it, and the default is the security decision rather than a convenience. Enabling it means the bootstrap requests btcpay.server.canuseinternallightningnode, which is a server-level BTCPay permission — the one kind this project otherwise refuses to hold. See the security model, threat 5.
SEED_LN_PAYMENT_METHOD
str
BTC-LN
BTCPay's Lightning payment method id.
SEED_LN_WITHDRAWAL_AUTO_LIMIT
int
200000
≥ 0 — The same meaning as the BTC caps, over a different pot of money: channel balance, which the BTC caps say nothing about.
SEED_LN_WITHDRAWAL_DAILY_CAP
int
2000000
≥ 0 — Rolling 24-hour cap on the Lightning float, in satoshis.
SEED_LN_WITHDRAWAL_USER_DAILY_CAP
int
(unset)
Optional rolling 24-hour cap per user. Unset disables it.
SEED_LN_WITHDRAWAL_MIN
int
1000
≥ 1 — Above the flat fee on purpose: a minimum below it would accept requests that deliver nothing and be refused as dust a moment later.
SEED_LN_WITHDRAWAL_FLAT_FEE
int
100
≥ 0 — A flat service charge, because a routing fee cannot be estimated before the route is found. What the route actually cost is journalled at settle time from the node, so this number is revenue and not a cost estimate.
DEPOSIT_INVOICE_EXPIRY_MIN_LN
int
30
≥ 1 — Shorter than the on-chain window on purpose: an open BOLT11 invoice ties up inbound channel liquidity for as long as it lives.
LN_PAYOUT_TIMEOUT_SECONDS
int
900
≥ 60 — How long a Lightning payout may sit in BTCPay's AwaitingPayment before this service cancels it. BTCPay's Lightning processor retries a payout it cannot route for as long as the invoice lives and never moves it to a failed state, so without a timeout the withdrawal stays submitted forever with the user's balance held.
Asset seed values — USDT-TRC20 (micro-USDT)
Variable
Type
Default
Notes
SEED_USDT_PAYMENT_METHOD
str
USDT_TRC20
BTCPay's payment method id from the USDt plugin. Version-dependent; discovered from the store at startup like the BTC one.
SEED_USDT_WITHDRAWAL_AUTO_LIMIT
int
200000000
≥ 0 — USDT withdrawals are manual-only today, so this limit only starts to matter once an automated TRON signer exists.
SEED_USDT_WITHDRAWAL_DAILY_CAP
int
2000000000
≥ 0 — Rolling 24-hour per-asset cap, in micro-USDT.
SEED_USDT_WITHDRAWAL_USER_DAILY_CAP
int
(unset)
Optional rolling 24-hour cap per user. Unset disables it.
SEED_USDT_WITHDRAWAL_MIN
int
1
≥ 1 — Smallest accepted gross withdrawal, in micro-USDT.
SEED_USDT_WITHDRAWAL_FLAT_FEE
int
1000000
≥ 0 — Flat fee covering the operator's TRX gas for a TRC-20 transfer.
Withdrawal policy
Variable
Type
Default
Notes
WITHDRAWAL_FEE_MODE
deduct | absorb
deduct
deduct — the user receives gross minus the fee. absorb — the user receives gross and the operator pays the fee.
USDT_AUTO_WITHDRAW
bool
false
Refused at startup while it is true: the BTCPay USDt plugin has no payout handler, so nothing could send an auto-approved USDT withdrawal.
Bitcoin network, fee estimation and deposit windows
Variable
Type
Default
Notes
BITCOIN_NETWORK
mainnet | testnet | signet | regtest
mainnet
Which network destination addresses are validated against. A production environment refuses to start on anything but mainnet.
BTC_FEE_TARGET_BLOCKS
int
3
≥ 1 — Confirmation target for the fee estimate, in blocks.
BTC_PAYOUT_VSIZE_VB
int
300
≥ 1 — Assumed payout size in virtual bytes. Raise it if the hot wallet holds many small deposit UTXOs; each extra P2WPKH input is roughly 68 vB.
BTC_FALLBACK_FEE_SAT_PER_VB
int
20
≥ 1 — Used only when BTCPay and mempool.space both fail to answer. Reaching it logs an error, because every withdrawal is then priced off a guess.
BTC_DUST_THRESHOLD_SAT
int
546
≥ 0 — A withdrawal whose net amount lands at or below this is refused before any hold is placed; the output would be unspendable.
MEMPOOL_SPACE_URL
str
https://mempool.space/api/v1/fees/recommended
Second fee source, tried when BTCPay's wallet estimate is unavailable. Unset disables it, which is right for a deployment that must make no outbound calls to third parties.
WITHDRAWAL_SUBMIT_INTERVAL_SECONDS
float
10.0
> 0 — How often approved withdrawals are turned into BTCPay payouts.
RECONCILE_WITHDRAWAL_INTERVAL_SECONDS
float
60.0
> 0 — Job B: how often submitted and broadcast payouts are re-checked.
STUCK_SUBMITTING_SECONDS
int
300
≥ 30 — How long a withdrawal may sit in submitting with no payout reference before reconciliation works out what happened to it.
DEPOSIT_INVOICE_EXPIRY_MIN_BTC
int
60
≥ 1 — Invoice checkout window. USDT is deliberately not shortened to recycle the address pool: a short window makes a late payment land on another user's reserved address. Size the pool up instead.
DEPOSIT_INVOICE_EXPIRY_MIN_USDT
int
60
≥ 1 — The USDT checkout window. See the note on the BTC one above.
DEPOSIT_MONITORING_MINUTES
int
1440
≥ 1 — How long after expiry BTCPay keeps attributing payments to an invoice, and therefore how long the deposit sweep keeps asking. Must be at least the invoice expiry.
Worker schedule (the reconciliation loop)
Variable
Type
Default
Notes
WEBHOOK_POLL_INTERVAL_SECONDS
float
5.0
> 0 — How often the worker drains the webhook_events queue.
WEBHOOK_MAX_ATTEMPTS
int
10
≥ 1 — Attempts before an inbound webhook event is parked for an operator.
RECONCILE_DEPOSIT_INTERVAL_SECONDS
float
120.0
> 0 — Job A: re-ask BTCPay about every deposit still worth asking about.
RECONCILE_SETTLED_WINDOW_DAYS
int
7
≥ 1 — Fallback window for polling settled and expired deposits when BTCPay reported no monitoring expiry.
RECONCILE_ORPHAN_SCAN_INTERVAL_SECONDS
float
86400.0
> 0 — Nightly page-through of the store's invoices looking for ours that have no local row — the case after restoring an older database backup.
WALLET_SCAN_INTERVAL_SECONDS
float
600.0
> 0 — Wallet scan: flags coins that reached the hot wallet matching no deposit payment. The only detector for a payment to an address BTCPay stopped watching.
BTCPay Server
Variable
Type
Default
Notes
BTCPAY_URL
str
(unset)
Greenfield base URL, container-to-container inside the Docker network.
BTCPAY_API_KEY
str
(unset)
secret — Restricted Greenfield API key scoped to the single store. Never server-admin.
BTCPAY_STORE_ID
str
(unset)
The store this service owns.
BTCPAY_WEBHOOK_SECRET
str
(unset)
secret — Webhook signing secret. Must differ from BTCPAY_API_KEY — the ingress path verifies an HMAC over raw bytes with this value.
TRON / USDT-TRC20
Variable
Type
Default
Notes
TRON_NETWORK
mainnet | nile
mainnet
Selects the default TronGrid endpoint and the default USDT contract.
TRONGRID_BASE_URL
str
(unset)
Override the endpoint for your own node or a paid provider. Unset uses the network's TronGrid default.
TRONGRID_API_KEY
str
(unset)
secret — Required in production once a hot wallet address is set: keyless TronGrid access is throttled unpredictably, and what gets throttled is the check that a withdrawal really happened.
USDT_CONTRACT_ADDRESS
str
(unset)
Must match what the USDt plugin watches. The withdrawal verifier refuses a transfer of any other token. Unset uses the network default; both defaults were read off their chains on 2026-08-11 and answered USDT / 6, so setting this is about agreeing with your plugin rather than about distrusting the default.
TRON_HOT_WALLET_ADDRESS
str
(unset)
The address USDT is sent from. Verification compares every transfer's sender against it, so a wrong value rejects every genuine withdrawal. Unset disables the TRON jobs entirely, which is right for a BTC-only deployment.
TRON_CONFIRMATIONS
int
19
≥ 1 — Blocks before a manual USDT withdrawal is treated as final. 19 is roughly TRON's solidified-block distance.
GAS_MONITOR_INTERVAL_SECONDS
float
900.0
> 0 — How often the TRX gas balance of the hot wallet is checked.
TRX_ALERT_THRESHOLD
int
200
≥ 0 — TRX balance below which an alert is raised. A wallet full of USDT and empty of TRX cannot send anything, and the symptom reads as "withdrawals are broken".
USDT_AMOUNT_TOLERANCE_PCT
float
1.0
≥ 0 — How far a settled USDT deposit may deviate from the expected amount before it goes to review instead of crediting. Applies only when the platform supplied an expected amount; 0 disables it.
Alerts
Variable
Type
Default
Notes
NTFY_TOPIC_URL
str
(unset)
Optional ntfy.sh (or compatible) topic URL. Alerts are always logged; this adds a push.
TELEGRAM_BOT_TOKEN
str
(unset)
secret — Optional Telegram bot. Both the token and the chat id must be set or the transport stays off.
TELEGRAM_CHAT_ID
str
(unset)
The chat alerts are sent to. Useless without the bot token.
Outbound webhooks to the platform
Variable
Type
Default
Notes
PLATFORM_WEBHOOK_URL
str
(unset)
Where deposit and withdrawal events are POSTed. Unset disables delivery, and events queue rather than being lost, so turning it on later ships the backlog.
PLATFORM_WEBHOOK_SECRET
str
(unset)
secret — HMAC-SHA256 key for the X-CPA-Signature header. The service refuses to start with a URL set and this unset: an unsigned webhook is one anybody can forge.
OUTBOUND_DELIVERY_INTERVAL_SECONDS
float
15.0
> 0 — How often the delivery worker drains the queue. The per-event retry schedule is fixed in code and is not configurable.
OUTBOUND_HTTP_TIMEOUT_SECONDS
float
10.0
> 0 — Per-request timeout for one delivery attempt.
Reconciliation and health
Variable
Type
Default
Notes
RECONCILE_INVARIANT_INTERVAL_SECONDS
float
3600.0
> 0 — Job C: how often the books are re-derived from the postings and custody is compared against the chain. This is the job that would notice an insolvency, so lengthening it lengthens how long one can go unnoticed.
WORKER_HEARTBEAT_STALE_SECONDS
int
300
≥ 30 — A worker job whose last run is older than this reads as stale in /readyz. /healthz is process-and-database only, so this is how a dead worker is noticed.
WEBHOOK_SIGNATURE_FAILURE_THRESHOLD
int
10
≥ 1 — Failed inbound signature verifications in the last hour before an alert. A spike means a misconfigured secret or somebody probing the endpoint.
CUSTODY_TOLERANCE_UNITS
int
0
≥ 0 — How far custody may sit below user obligations before Job C calls it an insolvency signal, in the asset's smallest unit. 0 is correct and is the default: the expected shortfall is derived from in-flight postings, not from a tuned epsilon.
Rules checked at startup
These are cross-field, so they are not visible in the table above. Each one is
a refusal to start, not a warning.
DEBUG=true is allowed only with ENVIRONMENT=development. Debug mode leaks internals into responses and logs.
DATABASE_URL must be a psycopg 3 PostgreSQL URL — postgresql:// or postgresql+psycopg://.
SEED_BTC_WITHDRAWAL_AUTO_LIMIT, SEED_LN_WITHDRAWAL_AUTO_LIMIT and SEED_USDT_WITHDRAWAL_AUTO_LIMIT may not exceed their matching *_DAILY_CAP. Otherwise one withdrawal could clear the 24-hour cap on its own.
BITCOIN_NETWORK must be mainnet when ENVIRONMENT=production. Anything else validates withdrawal addresses against the wrong chain.
TRONGRID_API_KEY is required in production once TRON_HOT_WALLET_ADDRESS is set. Keyless TronGrid access is throttled unpredictably, and what gets throttled is the check that a withdrawal really happened.
USDT_AUTO_WITHDRAW=true is refused outright. The BTCPay USDt plugin has no payout handler, so an auto-approved USDT withdrawal would stall with the user's balance held.
PLATFORM_WEBHOOK_SECRET is required once PLATFORM_WEBHOOK_URL is set. An unsigned webhook is one anybody can forge.
DEPOSIT_MONITORING_MINUTES may not be below DEPOSIT_INVOICE_EXPIRY_MIN_BTC. BTCPay would stop attributing payments before the invoice had even expired.