Skip to main content

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.
Running 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

CauseDATABASE_URL points at a database that isn’t reachable or hasn’t been created yet.Fix
1

Confirm Postgres is up and the URL is right

If this fails, fix the host/port/credentials in DATABASE_URL (postgres://user:pass@host:5432/recurso) before doing anything else.
2

Create the database if it is missing

Cause — The schema migrations haven’t been applied to this database. This is the single most common first-run error.Fix
Then restart the API. make demo runs migrations for you; only standalone or production deployments need this step explicitly.
If you later see missing columns after pulling new code, a migration was added upstream. Restart the API — migrations run automatically at boot, are additive, and are safe to re-apply.

Authentication (401)

Cause — The key is missing, malformed, or from a different environment than the endpoint you are calling.Fix
  • Send the key as a Bearer token in the Authorization header — 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 demo stack, the seeded key is sk_test_12345. If you ran make seed again, the data was wiped and re-seeded — the key still works, but any keys you created were deleted.
Cause — The Authorization header is absent entirely, or the value is not in Bearer <key> form.Fix — Add the header. See Authentication for the exact format.

Webhooks

Cause — The secret your handler uses doesn’t match the secret registered on the webhook endpoint, or the signed payload has been altered before verifying (body parsers re-serialize JSON and change the bytes).Fix
1

Verify against the raw request body

HMAC is computed over the exact bytes Recurso sent. If your framework parses JSON first and you re-JSON.stringify it, key order and whitespace change and the signature won’t match. Read the raw body:
2

Confirm the secret matches

The 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.
The signature is delivered in the X-Recurso-Signature header. See the Webhooks guide for the full verification snippet in Node and Python.
Cause — Your endpoint didn’t return 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

Cause — No gateway credentials are configured for the invoice currency. Recurso routes each payment from the invoice currency (INR → Razorpay, others → Stripe by default); with no matching connection, the hosted checkout page and autopay have nothing to charge through.Fix — Set the keys for the gateway you use, then restart the API:
.env
Use test keys (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%

CauseTAXJAR_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:
.env
If tax is still 0 with 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.
Cause — No GST configuration has been saved for the tenant, so there is no 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

Cause — You reused an 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.
See Idempotency & Rate Limiting for the full rules.

TigerBeetle (Optional Ledger)

Cause — TigerBeetle is an optional high-performance ledger backend. When it isn’t running, Recurso falls back to the Postgres ledger — billing still works end to end.Fix
  • If you don’t need it: ignore the warning. TigerBeetle degrades to a warning (not critical) in the health watcher precisely because the system keeps functioning without it. make demo starts it for you.
  • If you want it: make sure the TigerBeetle container/process is running and reachable at its configured address, then restart the API. Verify with:
    A healthy response reports each component (PostgreSQL, Redis if configured, TigerBeetle) individually.
PostgreSQL going down is critical; Redis and TigerBeetle are warning because the API can serve billing without them. See the health-alerting section of Going to Production for the alert semantics.

Still Stuck?

Error Reference

The full error-code taxonomy and what each one means

FAQ

Honest answers on production-readiness, licensing, and tax coverage

GitHub Issues

Search existing issues or open a new one

Support

Email the team