Quickstart

Run the whole stack on your machine in a few minutes, sign in to the demo workspace, and read an account's access from the API.

Before you start

  • Node 22 or newer. The Workflows service in workflows/ needs Node 24, but you don't need it to run the rest locally.
  • pnpm 9. Running corepack enable picks up the version pinned in package.json.
  • Docker, for Postgres and the API. You don't need Bun or Postgres installed.

Run it locally

  1. Clone and install

    git clone https://github.com/impactvelocity/offer-sdk
    cd offer-sdk
    pnpm install
  2. Start Postgres and the API

    This runs Postgres 17 and the API in watch mode on http://localhost:6767. Migrations run on boot. The API's interactive reference is at http://localhost:6767/docs.

    pnpm dev:api
  3. Start the dashboard

    In a second terminal. Each service has its own script, listed in the table below. Use these rather than the root pnpm dev, which doesn't start the API and also starts the Workflows service, which needs the Render CLI and Node 24.

    pnpm dev:app

    Open http://localhost:6768. In development the dashboard already points at the local API with its development admin key, so neither side needs a .env file. On a fresh database it opens on /setup: the first account you create is the admin, and sign-up closes after that.

  4. Open the demo workspace

    Press Explore the demo workspace on the setup or sign-in page. The first time, the dashboard builds two sample apps with six months of usage, which takes a few seconds. After that you can also sign in with demo@offersdk.dev and demo-password. The demo login is read-only and the Agent is off for it, so sign in as your admin to make changes or use the Agent. Run pnpm seed:demo --reset to start the demo over.

ServiceURLStarted by
APIhttp://localhost:6767pnpm dev:api
Dashboardhttp://localhost:6768pnpm dev:app

Check an account's access

Create your own app in the dashboard, add an entitlement and a plan, then copy the app id and its secret key from Developers → API keys. Every account your app has is a record in the API, keyed by an id you choose (usually your user or team id).

Create an account on a plan
curl -X POST http://localhost:6767/apps/$APP_ID/namespaces \
  -H "Authorization: Bearer $OFFER_SECRET_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "id": "user_42", "name": "Ada", "plan": "pro" }'

Then ask what that account can do. full-plan returns the plan with every entitlement already resolved, including current usage and whether the account can use it right now.

curl http://localhost:6767/apps/$APP_ID/namespaces/user_42/full-plan \
  -H "Authorization: Bearer $OFFER_SECRET_KEY"
Response
{
  "plan": { "id": "pro", "name": "Pro", "description": null, "isFree": false, "meta": {}, "privateMetaKeys": [] },
  "incentive": null,
  "offer": null,
  "addons": [],
  "entitlements": [
    { "id": "posts", "feature": "posts", "name": "Posts", "type": "usage", "usage": 2, "max": 100, "left": 98, "can": true },
    { "id": "pin_posts", "feature": "pin_posts", "name": "Pin posts", "type": "boolean", "usage": 0, "max": null, "left": null, "can": true }
  ]
}

When the account uses a metered feature, record it. The API answers with the new count, or with 402 and an upgrade offer if the entitlement blocks usage past its limit.

curl -X POST http://localhost:6767/apps/$APP_ID/namespaces/user_42/usage/posts/add \
  -H "Authorization: Bearer $OFFER_SECRET_KEY"
Keep the secret key on your server. It can change any record in the app. The browser gets the publishable key (pub_…), which can only read plans and pricing, add usage and run checkouts. Authentication lists what each key can do.

Optional settings

Everything above works with no configuration. Add these when you want the AI features, or to work without Docker.

VariableWhereWhat it turns on
ANTHROPIC_API_KEYoffer-app/.env.localThe Agent page in the dashboard.
ANTHROPIC_API_KEYapi/.envDynamic save offers in cancel flows. Without it, the flow shows its static offer.
OFFER_API_URL=mockoffer-app/.env.localRuns the dashboard on an in-memory mock API with a demo workspace, no Docker needed. Offers and cancel flows are hidden in this mode.

Next steps