# Account, API keys and signup

Docs

Sign up, manage API keys (scopes, spend limits, expiry, IP allowlists, live and test modes), read usage and the credit ledger, and get balance alerts.

Everything about your account is an API route, free, and also available in the [dashboard](https://staging.socialtap.dev/dashboard/).

## Sign up

**People:** enter your email address on the [signup page](https://staging.socialtap.dev/signup/). We email a single-use link; confirming it creates your account with **100 free credits** (once per account, no card) and two keys: a live key and a test key, shown once. The same link signs in to an existing account.

**AI agents:** an agent without a key signs up with its human's email address:

Shell:

```shell
curl -X POST https://staging.socialtap.dev/v1/agent_signup \
  -H 'content-type: application/json' \
  -d '{"email": "you@example.com", "name": "my-agent"}'
```

The answer has two keys. The **test key** works at once: every endpoint answers from recorded responses, free (see [test mode](https://staging.socialtap.dev/docs/test-mode/index.md)). The **live key** is *pending*: it spends nothing and can only call `GET /v1/account` until the human opens the confirmation email and confirms, which activates it and grants the free credits. Agents never get spending power without a human. Poll `GET /v1/account` with the live key: `api_key.status` turns from `pending` to `active`. An address that already has an account is refused: its owner creates a key for the agent in the dashboard.

Signup is throttled per IP address and per email address, and disposable email domains are refused.

## Routes

| Route | What it does | Auth |
| --- | --- | --- |
| `GET /v1/account`<br>Get the account, balance and calling key | The account's balance and plan, the low-balance alert threshold, and the calling key: its mode, scopes, spend limit (with what it used this interval) and expiry. Free; never cached. A pending agent key may call it (and nothing else) to learn when its owner confirmed. | key with account:read, or the session |
| `PATCH /v1/account`<br>Update account settings | Sets low_balance_threshold: credits.low (an event and an email to the owner) fires once each time a request takes the balance from above it to at or below it; credits.depleted fires when the balance reaches 0. Free. | key with admin, or the session |
| `POST /v1/agent_signup`<br>Sign up as an agent: a test key now, live on confirm | For AI agents: send your human's email address and get a test key that works at once (recorded responses, free) and a live key that stays pending until the human confirms by email, which grants the free credits. Agents never get spending power without a human. An address that already has an account is refused: its owner creates keys in the dashboard. | none |
| `POST /v1/api_keys`<br>Create an API key | Creates a key with the given scopes, spend limit, expiry and IP allowlist. The secret is in this answer only: store it now. A key never gets a scope its creator lacks. Free. | key with admin, or the session |
| `GET /v1/api_keys/{id}`<br>Get an API key | One key and its spend this interval; never its secret. Free. | key with account:read, or the session |
| `GET /v1/api_keys`<br>List API keys | The account's keys, newest first, revoked ones included; never their secrets. Free. | key with account:read, or the session |
| `DELETE /v1/api_keys/{id}`<br>Revoke an API key | Revokes a key for good (a key may revoke itself). It stops working within a minute everywhere; revoking it again changes nothing. Free. | key with admin, or the session |
| `PATCH /v1/api_keys/{id}`<br>Update an API key | Changes a key's name, scopes, spend limit, expiry or IP allowlist; absent fields keep their value. Takes effect within a minute everywhere. Free. | key with admin, or the session |
| `GET /v1/credit_transactions`<br>List credit transactions (the ledger) | Every change to the balance, newest first, cursor-paged: grants (free credits), purchases, hourly usage roll-ups and adjustments. Per-request detail is in GET /v1/usage. Free. | key with account:read, or the session |
| `POST /v1/session`<br>Confirm an emailed link: start a session | Exchanges an emailed link's token (single use) for a dashboard session: an HttpOnly, SameSite=Strict cookie on the API origin. Confirming a sign-up creates the account, grants the free credits and returns its first live and test keys; confirming an agent signup activates the agent's live key and grants the free credits. | none |
| `DELETE /v1/session`<br>Sign out | Ends the session and clears its cookie. | dashboard session |
| `GET /v1/session`<br>Get the dashboard session | The signed-in account of the session cookie (the dashboard calls it first). | dashboard session |
| `POST /v1/signup`<br>Email a sign-up or sign-in link | Sends a single-use link to the address; opening it and confirming creates the account (with free credits and live and test keys) or signs in to an existing one. The answer is the same whether or not the address has an account. Used by the signup page; agents use POST /v1/agent_signup. | none |
| `GET /v1/usage`<br>Usage report: requests and credits over time | Requests, successes, failures, cache hits and credits charged per time bucket, optionally grouped by endpoint, key and outcome. Buckets are listed oldest first, cursor-paged. Data is complete to within about 5 minutes; credits reconcile with the ledger. Free. | key with account:read, or the session |

## API keys

Send the key in the `x-api-key` header. A key has:

- a **mode**: live keys start with `st_live_` and make real, billed requests; test keys start with `st_test_` and answer from recorded responses, free. The mode is stored with the key; the prefix is only a hint for people.
- **scopes**, what it may call. A new key gets every scope but `admin` unless you choose, and a key never gets a scope its creator lacks. Without the scope a route needs, the answer is [`forbidden`](https://staging.socialtap.dev/docs/errors/index.md#forbidden) with `details.required_scope`.
- an optional **spend limit**: at most that many credits per UTC day or month. Past it, requests fail with [`spend_limit_reached`](https://staging.socialtap.dev/docs/errors/index.md#spend_limit_reached) (HTTP 402, never retried automatically) until `details.resets_at`, even when the account still has credits. Concurrent requests can never take a key past its limit.
- an optional **expiry** and **IP allowlist** (addresses or CIDR ranges, IPv4 or IPv6). An expired key, or a call from another address, is refused with [`forbidden`](https://staging.socialtap.dev/docs/errors/index.md#forbidden) (`details.reason` says which).

The secret is shown once, when the key is created; the API only ever shows its prefix afterwards. Changes and revocations reach every server within a minute.

| Scope | Routes that need it |
| --- | --- |
| `endpoints` | the routes of its own feature (see above) |
| `jobs` | `POST /v1/jobs/{id}/cancel`, `POST /v1/jobs`, `GET /v1/jobs/{id}`, `GET /v1/jobs`, `GET /v1/jobs/{id}/results` |
| `account:read` | `GET /v1/account`, `GET /v1/api_keys/{id}`, `GET /v1/api_keys`, `GET /v1/credit_transactions`, `GET /v1/events/{id}`, `GET /v1/events`, `DELETE /v1/session`, `GET /v1/session`, `GET /v1/usage`, `GET /v1/webhook_endpoints/{id}/deliveries`, `GET /v1/webhook_endpoints/{id}`, `GET /v1/webhook_endpoints` |
| `admin` | `PATCH /v1/account`, `POST /v1/api_keys`, `DELETE /v1/api_keys/{id}`, `PATCH /v1/api_keys/{id}`, `POST /v1/events/{id}/redeliver`, `POST /v1/webhook_endpoints`, `DELETE /v1/webhook_endpoints/{id}`, `POST /v1/webhook_endpoints/{id}/rotate_secret`, `POST /v1/webhook_endpoints/{id}/test`, `PATCH /v1/webhook_endpoints/{id}` |

## Usage and the ledger

`GET /v1/usage` reports requests, successes, failures, cache hits and credits per hour or day, grouped by endpoint, key or outcome. `GET /v1/credit_transactions` lists every change to the balance: free credits, purchases, hourly usage and adjustments, with the balance after each.

## Balance alerts

Set `low_balance_threshold` with `PATCH /v1/account`. When a request takes the balance from above it to at or below it, the account gets a `credits.low` event and an email to its owner, once per crossing. `credits.depleted` fires when the balance reaches 0, and `api_key.spend_limit_reached` the first time in an interval that a key is refused for its spend limit.

## The dashboard session

Confirming an emailed link starts a dashboard session: a random secret in a `Secure`, `HttpOnly`, `SameSite=Strict` cookie with the `__Host-` prefix on this origin; sign in again when it ends. Only its hash is stored. The session can read and manage the account and keys, never call data endpoints. Every change it makes must come from this origin (the API checks the request's `Origin`), so another site can neither read nor drive it. Links are single use and expire after minutes; the confirm page asks you to press a button, so mail scanners that open links can't use them up.

---

[HTML version](https://staging.socialtap.dev/docs/account/) · [Docs index for AI agents](https://staging.socialtap.dev/llms.txt) · [All docs in one file](https://staging.socialtap.dev/llms-full.txt)
