Authentication

Get your tenant API key from Settings and send it with every request.

The public API authenticates with a single per-tenant API key. The key is tenant-scoped and acts with Admin-equivalent, tenant-wide access — it only ever sees and touches its own tenant's data.

Get your key

There are two ways to get a key:

  • Fastest — sign up over the API. A single unauthenticated POST /signup returns a working key immediately, no dashboard needed. See Getting started → Sign up.

  • From the dashboard — if you already have a FOREMAN account:

    1. Sign in to FOREMAN as an Admin.
    2. Open Settings → API & integrations.
    3. Your tenant's API key is shown there, always visible with a Copy button. Copy it whenever you need it (it isn't a one-time reveal).

    Only Admins can see this page. The key is stored encrypted at rest.

Regenerating

Regenerate rotates the key. After you confirm, the old key stops working on the very next request and the new key takes its place. Regenerate is also the recovery path if a key is ever exposed.

Send the key

Send the key in the x-api-key request header:

curl https://app.foremanapp.com.au/api/v1/projects \
  -H "x-api-key: YOUR_API_KEY"

An Authorization: Bearer YOUR_API_KEY header is also accepted for clients that default to it.

Verify a key resolves to your tenant with the diagnostic endpoint:

curl https://app.foremanapp.com.au/api/v1/whoami \
  -H "x-api-key: YOUR_API_KEY"
# { "tenantId": "…", "role": "admin" }

Response shape

List endpoints wrap results in { "data": [...], "nextCursor": ... }. A single resource is returned as the bare object (no envelope). Errors always use { "error": {...} } (see below). All requests and responses are JSON; send Content-Type: application/json on writes.

Pagination

List endpoints page with ?limit= and an opaque ?cursor=:

  • limit — page size, 1–200 (default 50).
  • cursor — pass back the nextCursor a previous page returned. Omit it for the first page. Don't construct cursors yourself; they're opaque.
{
  "data": [
    /* … */
  ],
  "nextCursor": "…or null on the last page"
}

Keep fetching, passing the previous nextCursor as ?cursor=, until nextCursor comes back null — that's the last page.

Limits & retries

  • Rate limits — only POST /signup is rate-limited in v1 (per IP → 429). Authenticated endpoints have no per-key rate limit today, but don't assume unlimited throughput; back off on any 429.
  • Idempotency — there are no idempotency keys in v1, so a retried POST can create a duplicate. Guard retries on the client side.

Errors

Every failed request returns a JSON envelope:

{
  "error": {
    "code": "validation",
    "message": "One or more inputs are invalid.",
    "fields": { "name": "Enter a project name." }
  }
}
Statuserror.codeWhen
401unauthorizedMissing or invalid API key.
403read_only / project_capSubscription cancelled (writes disabled, reads still work), or a plan limit is hit (e.g. the Free-plan project cap — the message carries an upgrade link).
404not_foundNo such resource in your tenant.
405method_not_allowedWriting a read-only resource (create/edit it in the app instead).
409conflictConflict with the resource's state (e.g. deleting a template that has submissions).
422validationValidation failed — see error.fields for the per-field detail.
429rate_limitedToo many requests — currently only on POST /signup (per-IP cap).

See the API reference for the request and response shape of every endpoint.

On this page