Subscription Lifecycle
How subscriptions flow from request to active API key β with approval workflows, state transitions, and gateway provisioning.
Overviewβ
Every API or MCP tool access in STOA goes through a subscription. Subscriptions connect a consumer (who wants access) to an API (what they want to access) via a plan (how much access they get).
Subscription Statesβ
| State | Access | Transition From | Transition To |
|---|---|---|---|
| pending | None β awaiting approval | [*] | active, revoked |
| active | Full API access | pending, suspended | suspended, revoked, expired |
| suspended | Blocked temporarily | active | active, revoked |
| revoked | Permanently cancelled | pending, active, suspended | Terminal |
| expired | Auto-expired | active | Terminal |
API Key Formatβ
When a subscription is approved, STOA generates a unique API key:
stoa_sk_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4
ββββββββ ββββββββββββββββββββββββββββββββββββ
prefix 32 hex characters (128 bits entropy)
| Variant | Prefix | Usage |
|---|---|---|
| REST API subscription | stoa_sk_ | Standard API access via X-API-Key header |
| MCP server subscription | stoa_mcp_ | MCP tool access via gateway |
Security:
- The full API key is returned once at creation β store it securely
- STOA stores only a SHA-256 hash β the key cannot be recovered
- The prefix (
stoa_sk_a1b2) is stored for identification in dashboards
The examples below use environment variables. Set them for your STOA instance:
export STOA_API_URL="https://api.gostoa.dev" # Replace with your domain
export STOA_AUTH_URL="https://auth.gostoa.dev" # Keycloak OIDC provider
export STOA_GATEWAY_URL="https://mcp.gostoa.dev" # MCP Gateway endpoint
Self-hosted? Replace gostoa.dev with your domain.
Create a Subscriptionβ
Via REST APIβ
curl -X POST "${STOA_API_URL}/v1/subscriptions" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"api_id": "billing-api",
"plan_name": "gold",
"application_name": "my-payment-app"
}'
Response:
{
"id": "sub-uuid-123",
"status": "pending",
"api_key": "stoa_sk_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"api_key_prefix": "stoa_sk_a1b2",
"api_name": "billing-api",
"plan_name": "gold",
"created_at": "2026-02-13T10:00:00Z"
}
The api_key field is only returned once. Copy it to a secure location (e.g., Infisical, environment variable). It cannot be retrieved later β only rotated.
Via Developer Portalβ
- Browse the API Catalog at
portal.<YOUR_DOMAIN> - Click Subscribe on any API
- Select a plan (e.g., Gold, Silver, Community)
- Name your application
- Click Request Access
- Copy your API key from the confirmation screen
Approval Workflowsβ
Manual Approval (Default)β
When a plan has requires_approval: true, subscriptions start in pending state:
# Admin: List pending subscriptions for your tenant
curl "${STOA_API_URL}/v1/subscriptions/tenant/${TENANT_ID}/pending" \
-H "Authorization: Bearer ${TOKEN}"
# Admin: Approve a subscription
curl -X POST "${STOA_API_URL}/v1/subscriptions/${SUB_ID}/approve" \
-H "Authorization: Bearer ${TOKEN}"
Auto-Approvalβ
Plans can define roles that bypass manual approval:
{
"slug": "community",
"name": "Community Plan",
"requires_approval": false,
"auto_approve_roles": ["tenant-admin", "devops"]
}
When requires_approval is false, subscriptions go directly to active and the API key is usable immediately.
State Transitionsβ
Suspend a Subscriptionβ
Temporarily blocks access (e.g., billing issues, abuse detection):
curl -X POST "${STOA_API_URL}/v1/subscriptions/${SUB_ID}/suspend" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"reason": "Payment overdue"}'
The consumer receives a 401 Unauthorized error on API calls. The API key is not deleted β it can be reactivated.
Reactivate a Subscriptionβ
curl -X POST "${STOA_API_URL}/v1/subscriptions/${SUB_ID}/reactivate" \
-H "Authorization: Bearer ${TOKEN}"
Revoke a Subscriptionβ
Permanently cancels access β cannot be undone:
curl -X POST "${STOA_API_URL}/v1/subscriptions/${SUB_ID}/revoke" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{"reason": "Terms of service violation"}'
Gateway Provisioningβ
When a subscription is approved, STOA provisions the API route on the gateway. This process is tracked via a separate provisioning status:
| Provisioning Status | Meaning |
|---|---|
none | No gateway provisioning needed |
pending | Queued for provisioning |
provisioning | Route being created on gateway |
ready | Route active β traffic flowing |
failed | Provisioning error (check provisioning_error) |
deprovisioning | Route being removed |
deprovisioned | Route removed from gateway |
Using Your API Keyβ
Once your subscription is active and provisioning is ready, use the API key:
curl "${STOA_GATEWAY_URL}/apis/${TENANT_ID}/${API_NAME}/v1/endpoint" \
-H "X-API-Key: stoa_sk_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4"
The gateway validates the key, checks the subscription status, and applies the plan's rate limits before forwarding to the backend.
Subscription Plansβ
Plans define the quotas and approval rules for subscriptions:
| Field | Type | Description |
|---|---|---|
slug | string | Unique identifier per tenant (e.g., gold) |
rate_limit_per_second | int | Max requests per second |
rate_limit_per_minute | int | Max requests per minute |
daily_request_limit | int | Max requests per day |
monthly_request_limit | int | Max requests per month |
burst_limit | int | Max concurrent requests |
requires_approval | bool | Whether admin must approve |
auto_approve_roles | list | Roles that skip approval |
Example Plansβ
| Plan | Rate/min | Daily | Monthly | Approval |
|---|---|---|---|---|
| Community | 60 | 10,000 | 100,000 | Auto |
| Silver | 300 | 50,000 | 1,000,000 | Auto |
| Gold | 1,000 | 500,000 | 10,000,000 | Manual |
| Enterprise | Custom | Custom | Custom | Manual |
Relatedβ
- API Key Rotation β Rotate keys with zero downtime
- Consumer Onboarding β Register API consumers
- Quota Enforcement β Rate limits and quotas
- Subscriptions Overview β General subscription concepts