Environment variables
Every variable each service reads. Locally none of them are required. On Render, the Blueprint sets or asks for the ones that matter.
In development (next dev for the dashboard, any NODE_ENV other than production or test for the API), each service fills in local defaults so they find each other with no .env files. Production and tests get no defaults. To override a value locally, copy api/.env.example to api/.env or offer-app/.env.example to offer-app/.env.local.
API
Service offersdk-api, code in api/.
| Variable | Default | Description |
|---|---|---|
| DATABASE_URL | Dev: local Postgres | Postgres connection string. The Blueprint uses the database's internal URL. |
| ADMIN_API_KEY | Dev: dev-admin-key | Full-access key. The dashboard and the Workflows service use the same value. You set it. |
| BETTER_AUTH_SECRET | Dev fallback | Signs dashboard sessions. Required in production. Generated by the Blueprint. |
| APP_URL | http://localhost:6768 | The dashboard's public URL. Sessions are issued for it and only its origin is trusted. MCP clients are sent to its /oauth/consent page to approve a connection. |
| PUBLIC_API_URL | On Render: RENDER_EXTERNAL_URL | This API's public HTTPS URL. PayPal webhooks are registered under it when an app connects PayPal, and the MCP server's OAuth metadata uses it as the origin. Set it for a custom domain, or when the API runs outside Render or behind a proxy. |
| SECRETS_KEY | BETTER_AUTH_SECRET, then ADMIN_API_KEY | Encrypts stored PayPal client secrets. |
| ANTHROPIC_API_KEY | None | Turns on dynamic save offers in cancel flows. Without it, flows use their static offer. |
| DECIDE_MODEL | claude-opus-5-5 | Model that picks dynamic save offers. |
| DECIDE_TIMEOUT_MS | 20000 | How long to wait for the model before falling back to the static offer. |
| RENDER_API_KEY | None | With RENDER_WORKFLOW_SLUG, runs pauses as Render Workflow tasks. |
| RENDER_WORKFLOW_SLUG | None | The Workflows service to start tasks on. The Blueprint sets offersdk-workflows. |
| PORT | 3000, dev: 6767 | Port to listen on. |
| MIGRATE_ON_BOOT | true | Set to false to run bun run migrate yourself. |
| DATABASE_POOL_MAX | 10 | Postgres pool size for the API. |
| AUTH_DATABASE_POOL_MAX | 5 | Postgres pool size for dashboard sign-in. |
| WEBHOOKS_WORKER | true | Set to false to not deliver webhooks from this instance. |
| WEBHOOKS_ALLOW_PRIVATE_URLS | false, dev: true | Allows webhook endpoints on localhost and private networks. Development only. |
| PAUSE_WORKER | true | Set to false to stop this instance checking for pauses that are due. |
| PAUSE_WORKER_INTERVAL_MS | 10 min, or 60 min with Workflows | How often the API checks for pauses that are due. |
| PAYPAL_API_URL | PayPal's sandbox or live URL | Sends every PayPal call to another base URL. The tests use it for a fake PayPal. |
SECRETS_KEY, or with BETTER_AUTH_SECRET when it isn't set. If that value changes, the API can no longer read them and every app has to connect PayPal again. Set SECRETS_KEY on its own if you expect to rotate the auth secret.Dashboard
Service offer-app, code in offer-app/.
| Variable | Default | Description |
|---|---|---|
| OFFER_API_URL | Dev: http://localhost:6767 | The API's public URL, shown in snippets. mock uses the built-in mock API. Unset outside development also means the mock. |
| OFFER_API_ADMIN_KEY | Dev: dev-admin-key | The API's ADMIN_API_KEY. Required whenever OFFER_API_URL is set. |
| OFFER_API_INTERNAL_URL | None | A private address for server-to-server calls, such as offersdk-api:10000. Wired by the Blueprint. |
| ANTHROPIC_API_KEY | None | Turns on the Agent page. |
| AGENT_APPROVAL_SECRET | Derived from another secret | Signs the agent's change approvals. Set the same value on every instance if you run more than one. |
| DEMO_ENABLED | Dev: true | Shows the read-only demo login on the sign-in page (pre-filled at /demo-account). The first sign-in builds the demo workspace. Off unless you set it. |
| CORS_ORIGINS | * | Origins allowed to call the dashboard's public routes from the browser. A comma-separated list, or * for any. |
| DEMO_APP_ID | None | App for the public PayPal checkout demo at /demo/checkout. |
| DEMO_PUBLISHABLE_KEY | None | That app's publishable key. |
| BETTER_AUTH_SECRET | A fixed dev value | Mock mode only, where sign-in runs inside the dashboard. Unset, sessions are signed with a value that is public in the source, so set your own on any mock dashboard other people can reach. |
| BETTER_AUTH_URL | None | Mock mode only. The dashboard's own URL. |
Workflows
Service offersdk-workflows, code in workflows/.
| Variable | Description |
|---|---|
| OFFER_API_URL | The API's public URL. The tasks call its admin-only /pauses routes. You set it. |
| ADMIN_API_KEY | Copied from the API by the Blueprint. |
Scripts
pnpm seed:demo reads APP_URL, the dashboard whose demo workspace it builds (or rebuilds with --reset), and defaults to http://localhost:6768. With --reset it also reads ADMIN_API_KEY, the API's admin key. That defaults to dev-admin-key for a local dashboard and is required for a deployed one. See Deploy on Render for resetting a deployed dashboard's demo.