# Offline integration examples

These examples do not create watches or send notifications when tested.

- Node: `node receiver.mjs --self-test` (Node 22.13+, built-in SQLite; may emit an experimental-feature warning).
- Python: `python3 receiver.py --self-test` (Python 3.9+, standard library only).
- cURL preview: set `TRON_ADDRESS`, `ASSET`, `DIRECTION`, `TARGET`, and `WEBHOOK_URL` for a webhook; run `bash create-watch.sh --preview`. `TARGET=telegram` needs personal Telegram binding before a real create.
- Only `bash create-watch.sh --create NEW_PRIVATE_FILE` performs a real POST, using `TRONHOOKS_API_KEY` from the local environment. The mode is explicit; writes are never retried automatically. Response files use restrictive permissions and may contain a one-time secret. Treat timeouts as unknown and query watches before retrying.

Each receiver instance uses one configured watch signing secret. For multiple watches sharing a URL, first use the untrusted watch_id only to look up an already registered secret, then verify the raw-body signature before trusting any event fields; never fetch a secret from a payload-supplied URL. These single-watch examples do not implement that multi-tenant lookup.

Both receivers verify the exact raw bytes, reject malformed signatures/amounts, atomically store an inbox row keyed by `event_id`, preserve decimal strings, survive restart, and reject conflicting payloads with the same ID. The examples acknowledge only committed data; storage failures result in retryable errors.

Run a receiver with `TRONHOOKS_WEBHOOK_SECRET` and `INBOX_DB` set locally plus `--serve`. It binds only to 127.0.0.1:8787; put an HTTPS reverse proxy in front. The Python demonstration server is single-threaded and requires Content-Length, with no chunked request support. Both bound payload size to 64 KiB. Use a managed database and production server for high-volume deployments; never place the SQLite file or a signing secret in a public directory.

The durable inbox is the receiver, not a full payment ledger. Build a worker that reads `processed=0` rows and commits accounting updates plus the processed marker in one database transaction. Do not credit funds directly from an HTTP callback. Define your own account/address mapping, confirm asset contract and reconcile `GET /v1/events` before financial decisions. Do not convert `amount` or `amount_raw` through floating point.

API retries: honor Retry-After for bounded read retries. 400 fixes the input; 401/403 fixes credentials/network; 402 is quota (billing is closed); 409 requires comparing the existing watch; 429/5xx/timeout never justify blind write retries. For pagination pass opaque `next_cursor` unchanged until null (do not parse it as a date), and stop on a repeated cursor.

Official contract: https://tronhooks.com/docs/openapi.json
Capabilities and limitations: https://tronhooks.com/docs/ai/
