How to Use This Page
This page collects the problems people actually run into when running Recurso themselves — most of them are configuration, not bugs. Each entry follows the same shape: symptom → cause → fix. If you are stuck, work through the matching section top to bottom.make demo? It builds and starts the full stack (API, dashboard,
PostgreSQL, TigerBeetle, Mailhog), waits for the API to become healthy, and
loads demo data — so most of the setup issues below are already handled for
you. The demo API key is sk_test_12345.Database & Migrations
API won't start, `dial tcp ... connection refused` or `pq: database 'recurso' does not exist`
API won't start, `dial tcp ... connection refused` or `pq: database 'recurso' does not exist`
DATABASE_URL points at a database that isn’t reachable or hasn’t
been created yet.FixConfirm Postgres is up and the URL is right
DATABASE_URL
(postgres://user:pass@host:5432/recurso) before doing anything else.Create the database if it is missing
`relation 'customers' does not exist` (or any `relation ... does not exist`)
`relation 'customers' does not exist` (or any `relation ... does not exist`)
make demo runs migrations for you; only standalone or
production deployments need this step explicitly.Authentication (401)
Every request returns `401` with `{'error':{'code':'invalid_api_key'}}`
Every request returns `401` with `{'error':{'code':'invalid_api_key'}}`
-
Send the key as a Bearer token in the
Authorizationheader — not as a query param or a custom header: -
Check the prefix matches the mode you intend:
rsk_test_for development,rsk_live_for production. A live key against test data (or vice versa) authenticates against the wrong tenant scope. -
On a local
make demostack, the seeded key issk_test_12345. If you ranmake seedagain, the data was wiped and re-seeded — the key still works, but any keys you created were deleted.
Webhooks
Your endpoint rejects every delivery — 'signature verification failed'
Your endpoint rejects every delivery — 'signature verification failed'
Verify against the raw request body
JSON.stringify it, key order and whitespace change and
the signature won’t match. Read the raw body:Confirm the secret matches
WEBHOOK_SECRET in your app must equal the secret set when you created
the endpoint (POST /v1/webhooks). If you didn’t set one, Recurso
auto-generated it — read it back from the endpoint and copy it into your
environment.X-Recurso-Signature header. See the
Webhooks guide for the full verification snippet in
Node and Python.Deliveries show as `failed` in the dashboard
Deliveries show as `failed` in the dashboard
2xx within the timeout, or isn’t
reachable over HTTPS.Fix — Your handler must respond 200 within 5 seconds and process the
event asynchronously if the work is slow. Inspect recent attempts and re-queue
them once the endpoint is healthy:Payment Gateways
Invoices are created but payments are never collected
Invoices are created but payments are never collected
rzp_test_ / sk_test_) until you go live. Switching to
rzp_live_ / sk_live_ is a step in the
Going to Production checklist.Tax Computes as 0%
US invoices come out with `tax_amount: 0` and `tax_type: sales_tax_stub`
US invoices come out with `tax_amount: 0` and `tax_type: sales_tax_stub`
TAXJAR_API_KEY isn’t set. Recurso deliberately does not guess
US rates; without TaxJar it ships US invoices at 0%, honestly labeled
sales_tax_stub.Fix — Configure TaxJar and give US customers a billing state + zip:tax_type: sales_tax and the note says “no nexus in
buyer state per provider”, that is correct — TaxJar reports you have no nexus
there. Nexus lives in your TaxJar account, not Recurso. See
US Sales Tax for the full behavior matrix.Indian invoices compute 0% GST
Indian invoices compute 0% GST
gst_rate to apply.Fix — Save your seller GST config (this also drives CGST/SGST vs IGST):GET /v1/settings/gst returns 404 until this is set. See the
India GST tutorial for the end-to-end walkthrough.Idempotency Key Reuse
A retried POST returns `409 conflict` / `422` instead of the original result
A retried POST returns `409 conflict` / `422` instead of the original result
Idempotency-Key with a different request body.
Recurso rejects this so two different operations can’t quietly share a key.Fix- Generate one key per distinct business operation (a UUID v4 prefixed with
idem_is a good default) and reuse that same key only for retries of that same request. - Keys are scoped to your API key and expire after 24 hours.
- Error responses are not cached: if the first attempt returned a 5xx, retry with the same key — it re-executes and can still succeed.