Admin API
Administrative endpoints for managing tenants, users, invitations, and API keys. These routes live under /admin/*.
Overview
Admin endpoints require an admin session token in Authorization: Bearer <token> (preferred) or a legacy X-Admin-Token. Most write operations require a super_admin role.
Security Warning
Admin credentials grant full system access. Store and transmit them securely.

Create tenants, manage keys, users, and invitations
Authentication
curl -X GET "https://gobananasai.com/admin/tenants" \
-H "Authorization: Bearer <admin_session_token>"Legacy header:
curl -X GET "https://gobananasai.com/admin/tenants" \
-H "X-Admin-Token: <legacy_admin_token>"Tenants
List Tenants
GET /admin/tenants{
"data": [
{
"tenantId": "acme-corp",
"tenantName": "ACME Corporation",
"isActive": true,
"contactEmail": "ops@acme.com",
"monthlyQuotaMb": 10240,
"rateLimitPerMinute": 60,
"modelId": "gemini-flash-image",
"allowedModels": ["gemini-flash-image", "gemini-pro-image"],
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-10T00:00:00.000Z"
}
]
}Get Tenant Details
GET /admin/tenants/:tenantId{
"data": {
"tenant": {
"tenantId": "acme-corp",
"tenantName": "ACME Corporation",
"isActive": true,
"contactEmail": "ops@acme.com",
"monthlyQuotaMb": 10240,
"rateLimitPerMinute": 60,
"notes": null,
"modelId": "gemini-flash-image",
"allowedModels": ["gemini-flash-image", "gemini-pro-image"]
},
"apiKeys": [
{
"apiKey": "sk_live_a1b2...",
"keyName": "Production API Key",
"isActive": true,
"createdAt": "2024-01-01T00:00:00.000Z",
"lastUsedAt": "2024-01-15T10:30:00.000Z",
"usageCount": 120
}
]
}
}Create Tenant
POST /admin/tenantsRequest body (snake_case):
| Field | Type | Required | Description |
|---|---|---|---|
tenant_id | string | Yes | Lowercase id, e.g. new-company |
tenant_name | string | Yes | Display name |
gemini_api_key | string | * | Gemini API key (legacy field; prefer provider_credentials) |
provider_credentials | object | * | {"gemini": "AIza...", "openai": "sk-..."}. At least one provider key is required |
contact_email | string | Yes | Ops/billing contact |
monthly_quota_mb | integer | No | Default 10240 |
rate_limit_per_minute | integer | No | Default 60 |
model_id | string | No | Default model id (defaults to gemini-flash-lite-image) |
allowed_models | string[] | No | Allowed model ids — any of gemini-flash-lite-image, gemini-flash-image, gemini-pro-image, openai-gpt-image-2, openai-gpt-image-2.5-flare, openai-gpt-image-2.5-sunburst |
api_keys | object[] | No | Seed keys; defaults to one live and one test |
curl -X POST "https://gobananasai.com/admin/tenants" \
-H "Authorization: Bearer <admin_session_token>" \
-H "Content-Type: application/json" \
-d '{
"tenant_id": "new-company",
"tenant_name": "New Company LLC",
"provider_credentials": {
"gemini": "AIza...",
"openai": "sk-..."
},
"allowed_models": ["gemini-flash-lite-image", "gemini-flash-image", "gemini-pro-image", "openai-gpt-image-2", "openai-gpt-image-2.5-flare", "openai-gpt-image-2.5-sunburst"],
"model_id": "gemini-flash-lite-image",
"contact_email": "ops@newco.com",
"monthly_quota_mb": 5120,
"rate_limit_per_minute": 30
}'Response:
{
"data": {
"tenant": {
"tenantId": "new-company",
"tenantName": "New Company LLC",
"isActive": true,
"contactEmail": "ops@newco.com",
"monthlyQuotaMb": 5120,
"rateLimitPerMinute": 30,
"createdAt": "2024-01-15T11:00:00.000Z",
"updatedAt": "2024-01-15T11:00:00.000Z"
},
"apiKeys": [
{ "apiKey": "sk_live_x1y2...", "keyName": "Production API Key", "type": "live" },
{ "apiKey": "sk_test_a1b2...", "keyName": "Test API Key", "type": "test" }
]
}
}Update Tenant
PATCH /admin/tenants/:tenantIdBody fields (snake_case) are optional: tenant_name, contact_email, is_active, monthly_quota_mb, rate_limit_per_minute, notes, model_id, allowed_models.
Response matches Get Tenant Details.
Create API Key
POST /admin/tenants/:tenantId/api-keyscurl -X POST "https://gobananasai.com/admin/tenants/acme-corp/api-keys" \
-H "Authorization: Bearer <admin_session_token>" \
-H "Content-Type: application/json" \
-d '{ "key_name": "Mobile App", "type": "live" }'{
"data": {
"apiKey": "sk_live_m1n2...",
"keyName": "Mobile App",
"type": "live"
}
}Users
List Users
GET /admin/users{
"data": [
{
"userId": "user_01H...",
"email": "admin@gobananasai.com",
"displayName": "Admin",
"role": "super_admin",
"isActive": true,
"lastLoginAt": "2024-01-10T12:00:00.000Z"
}
]
}Update User
PATCH /admin/users/:userIdBody fields (snake_case): display_name, role (admin or super_admin), is_active.
{
"data": {
"user": {
"userId": "user_01H...",
"role": "admin",
"isActive": true,
"updatedAt": "2024-01-15T12:00:00.000Z"
}
}
}Unlock User Account
POST /admin/users/:userId/unlock{
"data": {
"success": true,
"userId": "user_01H...",
"message": "Account unlocked successfully. Failed login attempts reset to 0."
}
}Invitations
List Invitations
GET /admin/invitations{
"data": [
{
"token": "inv_abcd...",
"email": "new.admin@acme.com",
"tenantId": "acme-corp",
"role": "admin",
"expiresAt": "2024-01-22T00:00:00.000Z",
"createdAt": "2024-01-15T11:00:00.000Z"
}
]
}Create Invitation
POST /admin/invitationsBody fields (snake_case): email (required), role (admin/super_admin), tenant_id (optional), expires_in_days (default 7).
{
"data": {
"token": "inv_abcd...",
"email": "new.admin@acme.com",
"role": "admin",
"tenantId": "acme-corp",
"expiresAt": "2024-01-22T00:00:00.000Z"
}
}Revoke Invitation
DELETE /admin/invitations/:token{ "data": { "success": true } }Error Responses
Errors follow the standard format:
{ "error": "Human-readable message" }Common cases:
401admin authentication required403super_admin privileges required404tenant/user/invitation not found