Pulse

API keys

Call the REST API from a script, a server or another app with a personal API key.

Every user can create API keys and call the REST API under /api/v1. A key acts as the user who created it: it sees the same account and the same organisations.

The examples use the demo at https://demo.launch-now.dev. On your own deployment, replace it with your NEXT_PUBLIC_APP_URL.

1. Create a key

  1. Sign in and open Account → API keys (/account/keys).
  2. Click Create key, give it a name (for example ci-script) and an optional expiry, from 1 to 365 days.
  3. Copy the key right away. It starts with pulse_ and is shown only once. Afterwards the list only shows its first characters.

Store it like a password: in an environment variable or a secret manager, never in the repository.

export PULSE_API_KEY="pulse_…"

2. Call the API

Send the key in the x-api-key header.

curl https://demo.launch-now.dev/api/v1/me \
  -H "x-api-key: $PULSE_API_KEY"
{
  "data": {
    "id": "u_8f2c…",
    "name": "Ada Lovelace",
    "email": "ada@example.com",
    "createdAt": "2026-09-01T10:12:00.000Z"
  }
}

The same call from TypeScript:

const response = await fetch("https://demo.launch-now.dev/api/v1/me", {
  headers: { "x-api-key": process.env.PULSE_API_KEY! },
})
if (!response.ok) throw new Error((await response.json()).error.message)
const { data } = await response.json()

Endpoints

MethodPathReturns
GET/api/v1/meThe account that owns the key
GET/api/v1/organizationsThe organisations the owner belongs to, with role
curl https://demo.launch-now.dev/api/v1/organizations \
  -H "x-api-key: $PULSE_API_KEY"
{
  "data": [
    {
      "id": "org_41d…",
      "name": "Acme",
      "slug": "acme",
      "role": "owner",
      "createdAt": "2026-09-02T08:00:00.000Z"
    }
  ]
}

Every response is JSON. Successful responses wrap the result in data.

Errors

Errors always come back as JSON with a stable code:

{
  "error": {
    "code": "invalid_api_key",
    "message": "This API key is invalid, disabled or expired.",
    "docs": "https://demo.launch-now.dev/docs/api"
  }
}
StatusCodeWhat to do
401missing_api_keyAdd the x-api-key header
401invalid_api_keyThe key is wrong, disabled or expired: create a new one
429rate_limitedWait a minute, then retry
404not_foundCheck the path against the endpoint table

Limits

Each key allows 60 requests per minute. The limit is set in lib/auth.ts, in the apiKey() plugin (rateLimit), and applies to keys created after a change.

Rotate or revoke a key

  • Rotate: create a new key, deploy it where the old one was used, then delete the old one. Nothing breaks in between.
  • Pause: Disable a key to reject its requests without deleting it, and Enable it again later.
  • Revoke: Delete it. Requests with that key fail with invalid_api_key immediately.

Add your own endpoint

Wrap the route handler in withApiKey, which checks the key and gives you the caller:

app/api/v1/projects/route.ts
import { withApiKey } from "@/lib/api/with-api-key"

export const GET = withApiKey(async ({ userId }) => {
  const projects = await listProjectsFor(userId)
  return Response.json({ data: projects })
})

Unknown paths under /api/v1 already answer with a JSON 404, so clients never receive an HTML page.