# Account, API keys and signup

Agents and accounts

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

Manage your account, API keys, usage and balance alerts with free API routes, or in the [dashboard](https://socialtap.dev/dashboard/).

## Sign up

**People:** create your account on the [signup page](https://socialtap.dev/signup/) with Google, GitHub, or an email address and a password (we email you a link to verify the address first). Your first sign-in gives you **100 free credits** (once per account, no card) and your first API key, shown once.

**AI agents:** an agent without a key follows [auth.md](https://socialtap.dev/auth.md). It registers, with no email address and nothing else it has to ask for:

Shell:

```shell
curl -X POST https://socialtap.dev/agent/identity   -H 'content-type: application/json'   -d '{"type": "service_auth", "client_name": "Claude Code", "api_key_name": "my-project"}'
```

It shows you the link and the code from the answer, you sign in or create an account and approve it, and it collects its key. Nobody is emailed and nothing is created until you do, and it works the same for an account you already have. Its key can spend your credits and read your account, never manage it; you see it in the [dashboard](https://socialtap.dev/dashboard/) under the name it gave, and you can revoke it there at any time. The steps it follows after that are in the [agent quickstart](https://socialtap.dev/docs/agent-quickstart/index.md).

## 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 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 |
| `DELETE /v1/account`<br>Delete the account (after a grace period) | Locks the account at once (every key revoked, the owner's sessions ended, jobs and webhooks stopped) and deletes it for good after \`grace_s\`, unless its owner signs in again and chooses it; unspent credits are forfeited. Its people on other accounts stay on those. The owner only, with a fresh sign-in. Free. | the owner's dashboard session |
| `POST /v1/account/leave`<br>Leave the account | Takes the signed-in person off the account they act on. The owner can't leave: hand the account over, or delete it. Free. | dashboard session |
| `POST /v1/account/transfer`<br>Hand the account to another member | Makes a member or admin the owner; the owner becomes an admin, and account email goes to the new owner. The owner only, with a fresh sign-in. Free. | the owner's dashboard session |
| `PATCH /v1/account`<br>Update account settings | 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 (and the owner is emailed, at most once a day, which no setting turns off). keep_response_bodies: keep each request's response (GET /v1/requests/{id}/response). Absent fields keep their value. Free. | key with admin, or an admin's session |
| `POST /v1/agent_claims/approve`<br>Approve an agent's request for a key | The approval page calls it. The agent can then collect a key on the account this session acts on, once: its secret exists only in the agent's answer, and the key can never manage the account. Free. | an admin's dashboard session |
| `POST /v1/agent_claims/deny`<br>Refuse an agent's request for a key | The approval page calls it. The request ends for good, and the agent is told. Free. | dashboard session |
| `POST /v1/agent_claims/lookup`<br>See an agent's request for a key | The approval page calls it with the code an agent shows its person: what is asking, from where, and what its key could do. Free. | dashboard session |
| `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 an admin's 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), every secret it has (during a rotation, the previous one too), and with it every key with the admin scope that this key created, gave admin or rotated, and those keys' in turn, all at once: a leaked admin key's copies don't outlive it. The answer lists them (\`also_revoked\`) and the keys below it without admin, which keep working (\`left_standing\`), as do webhook endpoints: review them after a leak. A key can't revoke a key above it, which would revoke itself: forbidden, reason revoking_own_ancestor (use the dashboard, or rotate_secret). They stop working within a minute everywhere, each with its own \`api_key.revoked\` event; revoking it again changes nothing. Free. | key with admin, or an admin's session |
| `POST /v1/api_keys/{id}/rotate_secret`<br>Rotate an API key's secret | Issues a new secret for the same key (same ID, settings and spend), shown once: store it now. The current secret keeps working for \`previous_secret_expires_in\` seconds, so you can deploy the new one without downtime, but only for calls that need no admin scope (others: forbidden, reason previous_secret). 0 stops it, and rotating again ends the oldest secret, within a minute everywhere: at most two secrets work. A rotation keeps the key, its jobs and every key it made: after a leak, revoke the key instead. A key may rotate itself, or a key it could have created; rotating another key puts that key below it. In the dashboard, a rotation frees the key from any key above it. Free. | key with admin, or an admin's 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 an admin's session |
| `GET /v1/billing/auto_topup`<br>Get auto top-up | The account's auto top-up: the pack it buys when the balance falls to its threshold, its monthly cap and what it charged this month, the saved card it charges, its latest top-up, and why it turned itself off, if it did. Free. | key with account:read, or an admin's session |
| `PATCH /v1/billing/auto_topup`<br>Turn auto top-up on or off, or change it | Auto top-up buys \`pack\` with the account's saved card (off-session; sales tax added where SocialTap collects it, as at checkout) whenever a balance change leaves the balance at or below \`threshold\`, one top-up at a time, never past \`monthly_cap_cents\` in a calendar month (UTC), tax included (by default \`default_cap_packs\` × the pack's price). A charge that fails is never retried: auto top-up turns off, the owner is emailed why, and a person turns it back on once the card is fixed. A top-up that would pass \`rolling_limit_cents\` in any {trust_window_days} days is skipped, the owner is told how to lift it, it runs by itself once there is room, and \`notice\` says so when the chosen pack is already past it. A dispute of any payment on the account turns it off, and it turns on again only with a different card; a bank's inquiry only turns it off. The owner is emailed when anyone else turns it on or raises its cap. The dashboard session only, with a fresh sign-in: it authorizes charges to the card. Absent fields keep their value. Free. | an admin's dashboard session |
| `POST /v1/billing/portal`<br>Open the payment settings (Stripe) | A Stripe customer portal link for the account's payment settings: add, replace or remove the saved card that monthly plans and auto top-up charge, change the billing address and tax ID that sales tax and receipts use, and read past invoices. The dashboard session only, with a fresh sign-in: it shows the billing details and changes the card. Free. | an admin's dashboard session |
| `GET /v1/billing/checkout/{id}`<br>Get a credit pack purchase | A purchase POST /v1/billing/checkout or POST /v1/billing/subscription/checkout started for this account, as Stripe has it, and whether its credits were added (a paid one's are, once, by the time it answers). Poll while open. Other accounts' sessions are not_found. Free. | key with account:read, or an admin's 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/docs_feedback`<br>Send feedback on a docs page | Records whether a docs page helped, with an optional comment: what every docs page's "Was this page helpful?" sends. Agents are welcome to report a page that was wrong, unclear or missing something (set \`agent\`). Free; limited per IP address. | none |
| `POST /v1/funnel_events`<br>Record a step of the site's sign-up funnel | The site's own count of how visitors get from the home page to a first result: what its pages send, never anything about a person. Not for API clients. Free; limited per IP address. | none |
| `POST /v1/invitations/accept`<br>Accept an invitation | Joins the invitation's account with its role, as the signed-in person, whose verified address must be the one invited. Free. | dashboard session |
| `POST /v1/invitations`<br>Invite someone by email | Emails a link to join with a role: it works once, for \`invitation_ttl_s\`, for a person signed in with that verified address (inviting it again re-sends it). Per account: \`max_members\` people, \`max_open_invitations\` waiting, \`invitations_per_day\` emails a day. Free. | an admin's dashboard session |
| `GET /v1/invitations`<br>List invitations waiting to be accepted | The account's pending invitations, newest first. Free. | an admin's dashboard session |
| `DELETE /v1/invitations/{id}`<br>Revoke an invitation | Its link stops working at once. Free. | an admin's dashboard session |
| `GET /v1/members`<br>List the account's people | Everyone on the account with their roles, the owner first. Free. | dashboard session |
| `DELETE /v1/members/{user_id}`<br>Remove someone from the account | Takes the person off the account at once. Keys stay: they are the account's. The answer lists the API keys and webhook endpoints the person created that still work, to review and revoke. The owner can't be removed. Free. | an admin's dashboard session |
| `PATCH /v1/members/{user_id}`<br>Change someone's role | Makes someone an admin or a member (yourself included). The owner's role changes only by a handover. Free. | an admin's dashboard session |
| `GET /v1/notification_preferences`<br>Get notification preferences | Per category: email and dashboard on or off (default on). Security mail is always sent. Free. | key with account:read, or the session |
| `PATCH /v1/notification_preferences`<br>Update notification preferences | Turns a category's email or dashboard notifications on or off; the rest keep theirs. Only the dashboard turns off a category marked \`session_mute\` (API keys): an API key that tries gets forbidden, reason session_only. Free. | key with admin, or an admin's session |
| `GET /v1/notifications`<br>List the dashboard's notifications, newest first | The account's events in the categories the dashboard shows, newest first, each with whether it is read. Free. | dashboard session |
| `POST /v1/notifications/read`<br>Mark notifications read | Marks an event (default: the newest) and every older one read; the marker only moves forward. Free. | dashboard session |
| `GET /v1/notifications/summary`<br>Count unread notifications | How many notifications are unread, and the read marker. Free. | dashboard session |
| `POST /v1/billing/promo_codes/redeem`<br>Redeem a promo code | Adds a promo code's credits to the account's balance, as a promo transaction in its credit history. They never expire. Each code works once per account, and once per email address of the account's owner, whatever account it is on. A code that can't be redeemed is answered invalid_input with param code and details.reason: unknown (no such code), expired (past its end, or withdrawn), used_up (redeemed as many times as it allows), already_redeemed (by this account, or by its owner's address on another) or not_eligible (an account whose owner's address is a throwaway one). The dashboard session only. Free. | an admin's dashboard session |
| `GET /v1/billing/purchases/{id}/receipt`<br>Get a purchase's receipt | A fresh link to a purchase's receipt, from Stripe: a monthly plan's invoice page (with its PDF), or a pack's or auto top-up's receipt (tax and refunds included). A pending purchase has none until its payment clears, and other accounts' purchases are not_found. Free. | key with account:read, or an admin's session |
| `GET /v1/billing/purchases`<br>List purchases | Every payment the account made, newest first, cursor-paged: credit packs bought at checkout, auto top-ups, and monthly plan invoices (first months, renewals, upgrades), each with what it cost, its credits, and whether it was refunded or disputed. Its receipt is GET /v1/billing/purchases/{id}/receipt. Free. | key with account:read, or an admin's session |
| `GET /v1/requests/{id}`<br>Get a request from the request log | One request of the request log by its ID (meta.request_id of its answer). Free. | key with account:read, or the session |
| `GET /v1/requests/{id}/response`<br>Get the response a request returned | The body and status a request returned, without running it again: kept only while keep_response_bodies is on, for \`response_body_retention_s\`, at most \`response_body_max_bytes\` each; otherwise not_found (details.reason). Free. | key with account:read, or the session |
| `GET /v1/requests`<br>List requests, newest first (the request log) | Every endpoint request the account made, newest first, cursor-paged: its endpoint, params, status, outcome, credits, latency and whether the cache answered, filtered by endpoint, key, outcome, surface and time, and searched by the params' values (q: a handle, a link or an ID). Requests are kept for \`log_retention_s\`, at most \`log_max_rows\` per account. Responses are kept only with keep_response_bodies on (GET /v1/requests/{id}/response). A request appears within about 10 seconds. Requests refused before any work are not listed (the runaway-protection ceiling's: GET /v1/usage/rate counts them; over the failure budget or while the API was paused: GET /v1/usage does). Free. | key with account:read, or the session |
| `GET /v1/security_events`<br>List sign-in and security events | The signed-in person's sign-ins, failed sign-ins, password, email, two-factor and session changes, newest first, with the network and browser each came from. Kept \`security_events_retention_s\`. Free. | dashboard session |
| `POST /v1/session`<br>Start the dashboard: set up the account | The dashboard calls it once it is signed in (people sign in with Google, GitHub or an email address and password on the sign-in page). The first call of a new account returns its first API key (its secret shown only here); with agent_token, it approves that agent signup, activating the agent's pending live key and granting the free credits. An account an agent opened that its owner signs in to without approving the agent gets fresh keys, and the agent's are revoked. Free. | dashboard session |
| `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 person and account of the session cookie. | dashboard session |
| `POST /v1/billing/subscription/cancel`<br>Cancel the monthly plan, or undo it | Ends the account's monthly plan at the end of the month it is paid for, with no more charges and no refund for that month; monthly credits last until then, and pack and top-up credits stay. Until then, it can be undone. The dashboard session only, with no fresh sign-in: canceling only stops charges. Free. | an admin's dashboard session |
| `POST /v1/billing/subscription/checkout`<br>Start a monthly plan (Stripe Checkout) | Creates a Stripe Checkout session that starts a monthly plan; open the returned url to pay. The plan's credits are added when the first payment completes, and again at each renewal, when what was left of them ends. GET /v1/billing/checkout/{id} follows it. An account already on a plan changes it with PATCH /v1/billing/subscription instead. The dashboard session only: it starts recurring charges. Free. | an admin's dashboard session |
| `GET /v1/billing/subscription`<br>Get the monthly plan | The account's monthly plan: the plan paid for now, when it renews, what it changes to at renewal, and how much of the balance is monthly credits (spent first, ended at renewal). Without a plan, status is none and the account is pay as you go. Free. | key with account:read, or an admin's session |
| `PATCH /v1/billing/subscription`<br>Change the monthly plan | Upgrades or downgrades the account's monthly plan. An upgrade starts now: the new plan's full price is charged to the saved card, a new month starts today, the plan's credits are added and the monthly credits left stay until the next renewal. A downgrade starts at the next renewal, with nothing charged now. It works in every state of the plan, and answers what the person should know in \`notice\`, or where to pay in \`payment_url\`; only an account with no card is refused, until it adds one. The dashboard session only, with no fresh sign-in: changing plan has to be as easy as subscribing was. Free. | an admin's dashboard session |
| `POST /v1/tool_runs`<br>Run a free tool | What the site's free tools send: one lookup on an endpoint that has a free tool, with no account. Not for API clients: limited per IP address and behind a bot check. The same data with an API key has no such limits. | none |
| `GET /v1/usage`<br>Usage report: requests and credits over time | Requests, successes, failures, cache hits, credits charged and the latency of successful requests (p50, p95, and a histogram on request) per time bucket, optionally grouped by endpoint, key, outcome and surface (REST, MCP or a job). Buckets are listed oldest first, cursor-paged. Data is complete to within about 5 minutes; credits reconcile with the ledger. Requests refused before any work (a 402 or 429 with no charge, or while the API was paused) count per account, outcome and surface only: grouped by endpoint or key they have null there, and filtering by endpoint or key leaves them out. spend_limit_reached alone also stays with its key. Free. | key with account:read, or the session |
| `GET /v1/usage/rate`<br>Request rate against the runaway-protection ceiling | How close the account runs to its runaway-protection ceiling (max_rps in GET /v1/account): per time bucket, the requests the ceiling counted, the busiest second (peak_rps), the average rate and the requests refused with rate_limited. The ceiling is a safety limit that stops a runaway client, not a plan limit: plans have no rate limit. Account-wide (every key and the dashboard). Buckets are listed oldest first, cursor-paged. Data is complete to within about 5 minutes. Free. | key with account:read, or the session |

## API keys

Send the key in the `x-api-key` header. Keys start with `st_live_`. A key has:

- **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; `jobs` includes `jobs:read`, `admin` includes `account:read`. Without the scope a route needs, the answer is [`forbidden`](https://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://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://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.

Revoking a key also revokes every key with the `admin` scope it created, gave `admin` or rotated, and those keys' in turn, so a leaked admin key's copies don't outlive it; the answer lists them (`also_revoked`). Keys without `admin` keep working (`left_standing`; the dashboard offers to revoke them too), and webhook endpoints stay: review them after a leak. A key can't revoke a key above it: revoke that one in the dashboard, or rotate it. Rotating a key in the dashboard frees it from the keys above it. The account's owner is emailed when an API key makes or rotates a live key (keys without `admin` at most once an hour) or changes a webhook endpoint, and only the dashboard can turn off notifications about keys.

| Scope | What it allows | Routes that need it |
| --- | --- | --- |
| `endpoints`<br>default | Call data endpoints. | the routes of its own feature (see above) |
| `jobs`<br>default | Create, read and cancel background jobs. Includes jobs:read. | `POST /v1/jobs/{id}/cancel`, `POST /v1/jobs` |
| `jobs:read` | Read background jobs and their results; never create or cancel them. | `GET /v1/jobs/{id}`, `GET /v1/jobs`, `GET /v1/jobs/{id}/results` |
| `account:read`<br>default | Read the account, usage, the credit ledger, the request log, keys, webhook endpoints and events. | `GET /v1/account`, `POST /v1/account/leave`, `POST /v1/agent_claims/deny`, `POST /v1/agent_claims/lookup`, `GET /v1/api_keys/{id}`, `GET /v1/api_keys`, `GET /v1/billing/auto_topup`, `GET /v1/billing/checkout/{id}`, `GET /v1/credit_transactions`, `GET /v1/events/{id}`, `GET /v1/events`, `POST /v1/invitations/accept`, `GET /v1/invitations`, `GET /v1/members`, `GET /v1/notification_preferences`, `GET /v1/notifications`, `POST /v1/notifications/read`, `GET /v1/notifications/summary`, `GET /v1/billing/purchases/{id}/receipt`, `GET /v1/billing/purchases`, `GET /v1/requests/{id}`, `GET /v1/requests/{id}/response`, `GET /v1/requests`, `GET /v1/security_events`, `POST /v1/session`, `DELETE /v1/session`, `GET /v1/session`, `GET /v1/billing/subscription`, `GET /v1/usage`, `GET /v1/usage/rate`, `GET /v1/webhook_endpoints/{id}/deliveries`, `GET /v1/webhook_endpoints/{id}`, `GET /v1/webhook_endpoints` |
| `admin` | Change account settings, manage keys and webhook endpoints. Includes account:read. | `DELETE /v1/account`, `POST /v1/account/transfer`, `PATCH /v1/account`, `POST /v1/agent_claims/approve`, `POST /v1/api_keys`, `DELETE /v1/api_keys/{id}`, `POST /v1/api_keys/{id}/rotate_secret`, `PATCH /v1/api_keys/{id}`, `PATCH /v1/billing/auto_topup`, `POST /v1/billing/portal`, `POST /v1/events/{id}/redeliver`, `POST /v1/invitations`, `DELETE /v1/invitations/{id}`, `DELETE /v1/members/{user_id}`, `PATCH /v1/members/{user_id}`, `PATCH /v1/notification_preferences`, `POST /v1/billing/promo_codes/redeem`, `POST /v1/billing/subscription/cancel`, `POST /v1/billing/subscription/checkout`, `PATCH /v1/billing/subscription`, `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, outcome or surface. `GET /v1/usage/rate` reports the account's request rate against its runaway-protection ceiling (`max_rps`, see [Rate limits](https://socialtap.dev/docs/authentication/index.md#rate-limits)). `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 owner can turn that email off with `PATCH /v1/notification_preferences` (or in Settings). Security email (sign-in, password, two-factor) is always sent.

## The dashboard session

Signing in starts a dashboard session: a signed cookie with the `__Host-` prefix on this origin (`Secure`, `HttpOnly`, `SameSite=Lax`), checked against the server on every request, so signing out, a password reset or "sign out everywhere else" ends it at once. 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. Creating or changing API keys needs a recent sign-in: after a while the dashboard asks for your password or a two-factor code again.

Turn on two-factor authentication (an authenticator app, with one-time backup codes) in the security settings: every sign-in then asks for a code, with a password, Google or GitHub alike. The same page lists your sessions and a log of your sign-ins and security changes.

## Teams

Several people can work on one account, each with one role. Keys, credits and settings stay the account's: removing someone never revokes a key. The answer and the Team page list the keys and webhook endpoints they made, to revoke what shouldn't stay, and the owner is emailed when an admin invites someone, changes a role or removes someone.

| Role | What they may do |
| --- | --- |
| **Owner** `owner` | Everything, including deleting the account and handing it over. One per account. |
| **Admin** `admin` | Billing, API keys, webhooks, settings and people, never the owner. |
| **Member** `member` | Reads the account, usage, credits, activity log, keys (never their secrets) and jobs, and uses the playground only with a key it is given. Changes nothing about the account. |

Admins invite people from the dashboard's Team page. Everyone keeps their own account and switches in the top bar; dashboard requests name the account with `x-account-id`.

## Deleting the account

The account's owner deletes it in the [security settings](https://socialtap.dev/security/) (`DELETE /v1/account`). For 7 days, signing in again (and choosing it, if you are on several) cancels it. Then it is deleted for good and **its unspent credits are forfeited, with no refund**; signing up again with the address makes a new account, without free credits. The Privacy Policy has the details.

---

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