Base 3:16
Getting started

Quickstart

A key, a first call, and a gated feature.

By the end of this page your application will refuse work an account has not paid for, and record the work it does allow.

1. Get a secret key

From your application's directory:

npx @base316/cli login
npx @base316/cli init

login opens the dashboard and asks you to approve a code; init creates a project named after the directory (or uses the one you name with --project) and writes its Sandbox keys to .env, adding .env to .gitignore. Building with an AI agent? See Build with an agent.

Or by hand: in the dashboard, open your project and go to Developers → API keys, copy the secret key for the Sandbox environment — it starts b316_sk_sandbox_ — and export it:

export BASE316_SECRET_KEY=b316_sk_sandbox_...

Secret keys are server-side only. The publishable key (b316_pk_) is the one safe to ship to a browser, and it cannot read customers, subscriptions or invoices.

2. Check the key works

npx @base316/cli whoami
Environment  sandbox (env_…)
Project      proj_…
Workspace    org_…
API          https://api.base316.com

If that fails, nothing below will work — fix it first.

3. Install the SDK

pnpm add @base316/sdk
import { Base316 } from "@base316/sdk";

// Reads BASE316_SECRET_KEY from the environment.
const base316 = new Base316();

4. Define what you sell

In the dashboard's Catalog tab, create:

  1. A meter — the raw thing you count, for example api_call.
  2. A feature — what a customer buys, for example api_calls, backed by that meter.
  3. A plan — a price and what it entitles. Give it a limit on api_calls.

Then create a customer and subscribe them to the plan.

Use the CLI to check your work without leaving the terminal: npx @base316/cli plans and npx @base316/cli features.

5. Gate the feature

This is the call you will make most often. Ask before doing the work:

const decision = await base316.access.check({
	externalId: "acct_1234", // your own id for the account
	feature: "api_calls",
	requested: 1, // how much you are about to use
});

if (!decision.allowed) {
	throw new Error(`Refused: ${decision.reason}`);
}

// ... do the work ...

decision.usage carries used, limit, credits and remaining, so you can show somebody how close they are before they hit the wall.

6. Record what you used

Metering is a separate call, made after the work succeeds. If the work fails, nothing should be billed.

await base316.events.record({
	externalId: "acct_1234",
	event: "api_call",
	idempotencyKey: requestId, // required
	payload: { tokens: 1_420 },
});

idempotencyKey is required rather than optional. A retry after a network timeout must not bill the same work twice, and you are the only one who knows what "the same work" means — a request id is usually the right answer.

7. Watch it add up

npx @base316/cli check acct_1234 api_calls
ALLOWED
Limit      1000
Used       1
Remaining  999

Your project's setup checklist

The project's Overview lists the first steps — a meter, a published plan, the first usage event, Stripe connected and able to charge, the first invoice, and a Production secret key that has made a call. Each step is read from what the environment actually has, links to the screen that does it, and the list disappears once everything is done. Hide it with the cross; that is remembered for you, not for your teammates.

Rather explore before building? On an empty Sandbox, the checklist offers Load example data: three plans, four customers with subscriptions in different states, and some usage — the same world POST /v1/seed builds. It only ever adds to an empty catalog and is never offered in Production.

Where to go next

On this page