REST API Reference
Base URL: https://api.toggletown.com/api/v1
Authentication
The API uses two authentication methods:
| Method | Header | Use case |
|---|---|---|
| JWT Token | Authorization: Bearer <token> | Dashboard and management endpoints |
| SDK API Key | X-API-Key: <api-key> | SDK flag fetching |
JWT tokens are obtained via the login/signup endpoints and expire after 7 days.
Error Responses
All errors follow a consistent format:
{
"error": {
"message": "Description of what went wrong",
"details": [...] // Optional validation details
}
}
| Status | Meaning |
|---|---|
400 | Bad request — validation error |
401 | Unauthorized — missing or invalid auth |
403 | Forbidden — insufficient permissions |
404 | Not found |
429 | Rate limited |
500 | Internal server error |
Auth
Sign Up
POST /auth/signup
Body:
| Field | Type | Required | Description |
|---|---|---|---|
email | string | Yes | Valid email address |
password | string | Yes | Min 8 chars, must include uppercase, lowercase, and number |
Response 201:
{
"user": {
"id": "uuid",
"email": "user@example.com",
"isAdmin": false,
"plan": "FREE",
"createdAt": "2026-01-01T00:00:00.000Z"
},
"token": "eyJhbG..."
}
Login
POST /auth/login
Body:
| Field | Type | Required |
|---|---|---|
email | string | Yes |
password | string | Yes |
Response 200: Same shape as signup.
Projects
All project endpoints require JWT authentication.
List Projects
GET /projects
Returns all projects where the authenticated user is an owner or member.
Response 200:
{
"projects": [
{
"id": "uuid",
"name": "My App",
"environments": [...],
"_count": { "flags": 5 }
}
]
}
Create Project
POST /projects
Body:
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | 1–100 characters |
Creates the project with default environments (Production, Development). Subject to plan limits.
Response 201:
{
"project": {
"id": "uuid",
"name": "My App",
"environments": [
{ "id": "uuid", "name": "Production", "apiKey": "tt_live_..." },
{ "id": "uuid", "name": "Development", "apiKey": "tt_live_..." }
]
}
}
Get Project
GET /projects/:projectId
Response 200: Project with all environments and flags.
Update Project
PUT /projects/:projectId
Body: { "name": "New Name" }
Requires project owner.
Delete Project
DELETE /projects/:projectId
Requires project owner. Returns 204.
Environments
List Environments
GET /projects/:projectId/environments
Response 200:
{
"environments": [
{ "id": "uuid", "name": "Production", "apiKey": "tt_live_..." }
]
}
Create Environment
POST /projects/:projectId/environments
Body: { "name": "Staging" }
Subject to plan limits.
Rotate API Key
POST /projects/:projectId/environments/:environmentId/rotate-key
Generates a new API key for the environment. The old key is immediately invalidated.
Delete Environment
DELETE /projects/:projectId/environments/:environmentId
Returns 204.
Flags
List Flags
GET /projects/:projectId/flags
Response 200:
{
"flags": [
{
"id": "uuid",
"key": "new-checkout",
"name": "New Checkout",
"type": "BOOLEAN",
"flagEnvironments": [
{
"environmentId": "uuid",
"enabled": true,
"defaultValue": "true",
"rolloutPercentage": 100,
"rules": []
}
]
}
]
}
Create Flag
POST /projects/:projectId/flags
Body:
| Field | Type | Required | Description |
|---|---|---|---|
key | string | Yes | Kebab-case identifier (e.g., new-checkout) |
name | string | Yes | Display name, 1–200 characters |
description | string | No | Optional description |
type | string | Yes | BOOLEAN, STRING, NUMBER, or JSON |
defaultValue | any | No | Initial value |
environmentId | string | No | Environment to create config in |
Get Flag
GET /projects/:projectId/flags/:flagKey
Update Flag
PUT /projects/:projectId/flags/:flagKey
Body: { "name": "Updated Name", "description": "Updated description" }
Delete Flag
DELETE /projects/:projectId/flags/:flagKey
Triggers webhooks. Returns 204.
Toggle Flag
POST /projects/:projectId/flags/:flagKey/toggle/:environmentId
Flips the flag's enabled state for the given environment.
Update Flag Environment Config
PUT /projects/:projectId/flags/:flagKey/environments/:environmentId
Body:
| Field | Type | Description |
|---|---|---|
enabled | boolean | Enable/disable the flag |
defaultValue | any | Default value when no rules match |
rolloutPercentage | number | 0–100, percentage of users who get the flag |
rules | array | Targeting rules |
Rules format:
{
"rules": [
{
"attribute": "plan",
"operator": "equals",
"value": "pro"
}
]
}
Propagate Flag Config
POST /projects/:projectId/flags/:flagKey/propagate/:sourceEnvId/:targetEnvId
Copies flag configuration from one environment to another.
Evaluate Flag (Preview)
POST /projects/:projectId/flags/:flagKey/evaluate/:environmentId
Body:
{
"userId": "user-123",
"attributes": { "plan": "pro", "country": "US" }
}
Returns the evaluated value and debug information showing the evaluation logic.
Flag History
GET /projects/:projectId/flags/:flagKey/history?limit=50&offset=0
Returns audit log entries for the flag.
Bulk Toggle
POST /projects/:projectId/flags/bulk/toggle
Body:
{
"flagKeys": ["flag-1", "flag-2"],
"environmentId": "uuid",
"enabled": true
}
Bulk Delete
POST /projects/:projectId/flags/bulk/delete
Body: { "flagKeys": ["flag-1", "flag-2"] }
Segments
Segments are reusable groups of targeting rules. Requires Pro plan or above.
List Segments
GET /projects/:projectId/segments
Create Segment
POST /projects/:projectId/segments
Body:
{
"key": "enterprise-users",
"name": "Enterprise Users",
"description": "Users on enterprise plan",
"rules": [
{ "attribute": "plan", "operator": "equals", "value": "enterprise" }
]
}
Get Segment
GET /projects/:projectId/segments/:segmentId
Update Segment
PUT /projects/:projectId/segments/:segmentId
Delete Segment
DELETE /projects/:projectId/segments/:segmentId
Webhooks
Webhooks notify external services when flag changes occur. Requires Pro plan or above.
Events
| Event | Description |
|---|---|
FLAG_CREATED | A new flag was created |
FLAG_UPDATED | Flag metadata was updated |
FLAG_DELETED | A flag was deleted |
FLAG_TOGGLED | A flag was toggled on/off |
FLAG_RULES_UPDATED | Targeting rules were changed |
SCHEDULE_EXECUTED | A scheduled change was applied |
List Webhooks
GET /projects/:projectId/webhooks
Create Webhook
POST /projects/:projectId/webhooks
Body:
{
"name": "Slack Notifier",
"url": "https://example.com/webhook",
"events": ["FLAG_TOGGLED", "FLAG_CREATED"],
"secret": "optional-signing-secret"
}
URL must use HTTPS. The webhook secret is only returned at creation time.
Update Webhook
PATCH /projects/:projectId/webhooks/:webhookId
Delete Webhook
DELETE /projects/:projectId/webhooks/:webhookId
Test Webhook
POST /projects/:projectId/webhooks/:webhookId/test
Sends a test payload to the webhook URL.
Regenerate Secret
POST /projects/:projectId/webhooks/:webhookId/regenerate-secret
List Deliveries
GET /projects/:projectId/webhooks/:webhookId/deliveries?limit=50
Retry Delivery
POST /projects/:projectId/webhooks/:webhookId/deliveries/:deliveryId/retry
Schedules
Schedule flag changes to apply automatically at a future time.
List Schedules
GET /projects/:projectId/flags/:flagKey/schedules
Create Schedule
POST /projects/:projectId/flags/:flagKey/schedules
Body:
| Field | Type | Required | Description |
|---|---|---|---|
environmentId | string | Yes | Target environment |
changeType | string | Yes | ENABLE, DISABLE, UPDATE_ROLLOUT, or UPDATE_VALUE |
newValue | any | Conditional | Required for UPDATE_ROLLOUT and UPDATE_VALUE |
scheduledAt | string | Yes | ISO 8601 datetime (must be in the future) |
Update Schedule
PATCH /projects/:projectId/flags/:flagKey/schedules/:scheduleId
Only pending schedules can be updated.
Cancel Schedule
DELETE /projects/:projectId/flags/:flagKey/schedules/:scheduleId
Experiments
Run A/B tests linked to feature flags.
Create Experiment
POST /projects/:projectId/experiments
Body:
{
"flagId": "uuid",
"environmentId": "uuid",
"name": "Checkout Flow Test",
"hypothesis": "New checkout will increase conversions",
"variants": [
{ "name": "control", "value": "old", "trafficAllocation": 50 },
{ "name": "treatment", "value": "new", "trafficAllocation": 50 }
],
"primaryMetric": "checkout_completed"
}
Traffic allocation must sum to 100%.
List Experiments
GET /projects/:projectId/experiments?status=RUNNING
Get Experiment
GET /projects/:projectId/experiments/:id
Update Experiment
PATCH /projects/:projectId/experiments/:id
Only experiments in DRAFT status can be updated.
Start / Pause / Resume / Complete
POST /projects/:projectId/experiments/:id/start
POST /projects/:projectId/experiments/:id/pause
POST /projects/:projectId/experiments/:id/resume
POST /projects/:projectId/experiments/:id/complete
Complete accepts optional body: { "winnerVariant": "treatment", "applyWinner": true }
Get Results
GET /projects/:projectId/experiments/:id/results?days=30
Track Event (SDK)
POST /sdk/experiments/track
Auth: SDK API Key (X-API-Key header)
Body:
{
"userId": "user-123",
"eventName": "checkout_completed",
"flagKey": "checkout-flow"
}
SDK
Get Flags
GET /sdk/flags
Auth: SDK API Key (X-API-Key header)
This is the endpoint all SDKs poll. Returns all flag configurations for the environment associated with the API key.
Response 200:
{
"flags": {
"new-checkout": {
"enabled": true,
"type": "BOOLEAN",
"defaultValue": "true",
"rolloutPercentage": 50,
"rules": [
{
"attribute": "plan",
"operator": "equals",
"value": "pro"
}
]
}
},
"timestamp": "2026-01-01T00:00:00.000Z"
}
Includes cache headers (X-Cache: HIT or X-Cache: MISS) when Redis caching is enabled.
Audit Logs
GET /projects/:projectId/audit-logs?limit=50&offset=0&environmentId=uuid
Returns flag change history for the project. Retention depends on plan:
| Plan | Retention |
|---|---|
| Free | 7 days |
| Pro | 90 days |
| Team | Unlimited |
| Enterprise | Unlimited |
Statistics
GET /projects/:projectId/statistics?days=7
Returns evaluation statistics for the project over the given time period (1–30 days).
Usage & Billing
Get Usage
GET /usage
Returns current usage against plan limits.
Usage History
GET /usage/history
Returns monthly usage snapshots for the last 12 months.
Create Checkout
POST /billing/checkout
Body: { "plan": "PRO" }
Returns a checkout URL for Lemon Squeezy.
Customer Portal
POST /billing/portal
Returns a URL to the billing management portal.
Rate Limits
The API enforces rate limits to ensure fair usage. When rate limited, you'll receive a 429 response with a Retry-After header.