Tokens
Tokens authenticate every API and MCP call. The Tokens endpoints let you self-manage them programmatically. Most users will use the API tokens dashboard instead, but the endpoints exist for automation.
Base URL: https://formspring.io/api/v1
| Method | Path | Ability |
|---|---|---|
| GET | /tokens |
tokens:read |
| POST | /tokens |
tokens:write |
| DELETE | /tokens/{token} |
tokens:write |
A token can manage its own family — meaning, the tokens minted by the same user in the same team. It cannot see or revoke tokens belonging to other team members.
List tokens
GET /tokens
Returns every token minted by the calling user in the current team. The plaintext value of each token is not included — only the metadata.
Response 200:
{
"data": [
{
"id": "tok_01H...",
"name": "CI deploy bot",
"abilities": ["forms:read", "submissions:write"],
"last_used_at": "2026-05-07T10:14:00Z",
"created_at": "2026-04-01T00:00:00Z",
"last4": "f3a2"
}
]
}
last4 shows the last four characters of the token so you can identify which physical token a teammate has.
Create a token
POST /tokens
Content-Type: application/json
{
"name": "CI deploy bot",
"abilities": ["forms:read", "submissions:write"]
}
abilities is a non-empty array of ability strings. The minted token can only carry abilities the caller's token already has — you cannot mint a token more powerful than the one you're holding.
Response 201:
{
"data": {
"id": "tok_02H...",
"name": "CI deploy bot",
"abilities": ["forms:read", "submissions:write"],
"last4": "9c1d",
"created_at": "2026-05-07T11:00:00Z"
},
"token": "fs_live_a1b2c3d4..."
}
The plaintext token is shown once and never again. Save it. If lost, revoke and mint a new one.
Status codes for create
| Status | Meaning |
|---|---|
201 |
Token created |
400 |
abilities empty or contains an unknown ability string |
401 |
Token missing or invalid |
402 |
API not available on free plan |
403 |
Token would exceed caller's abilities, or caller's role disallows one of the requested abilities (e.g. viewer requesting forms:write) |
A 403 for ability escalation looks like:
{
"error": "ability_exceeds_caller",
"exceeded": ["forms:write"],
"message": "Cannot mint a token with abilities your own token does not have."
}
Revoke a token
DELETE /tokens/{token}
{token} is the token ID (e.g. tok_01H...), not the plaintext value.
Response 200:
{ "ok": true }
The revoked token is unusable on the next API call (no caching, no grace period).
Status codes for revoke
| Status | Meaning |
|---|---|
200 |
Revoked |
401 |
Token missing or invalid |
403 |
The token to revoke belongs to another user, or the caller is trying to revoke the token currently in use |
404 |
Token not found in current team |
Cannot revoke the active token
The token making the request cannot be the token being revoked. Trying to revoke yourself returns 403:
{ "error": "cannot_revoke_active_token" }
This is a guard rail — accidentally revoking your own token mid-script would lock you out of the rest of the script. To rotate, mint the new token first, switch to it, then revoke the old one.
Cannot escalate beyond caller
Even if your role permits broader abilities, your token is the cap. If your token only has forms:read, you can only mint sub-tokens with subsets of forms:read. To mint a token with forms:write, you need to be holding a token that already has forms:write.
This makes token compromise containable: a leaked read-only token cannot mint a write-capable child token.