Tenant Management
Create and manage tenants for a self‑hosted Go Bananas! deployment.
Overview
Go Bananas! is multi‑tenant by design. Each tenant:
- Has isolated data (images, characters, products)
- Uses up to five provider API keys for Gemini and up to five for OpenAI — encrypted independently in
tenant_provider_credentials - Picks which models it can route to via an
allowed_modelsallowlist - Has configurable quotas and rate limits
- Can have multiple API keys
Creating Tenants
Interactive Setup
npm run setup-tenantPrompts for tenant id, display name, provider keys (Gemini and/or OpenAI), allowed models, quota, and rate limit, then writes the tenant config + initial API keys.
Via Admin API
POST /admin/tenantscurl -X POST "https://gobananasai.com/admin/tenants" \
-H "Authorization: Bearer <admin_session_token>" \
-H "Content-Type: application/json" \
-d '{
"tenant_id": "acme-corp",
"tenant_name": "ACME Corporation",
"providerCredentials": {
"gemini": "AIza...",
"openai": "sk-..."
},
"allowedModels": ["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"],
"default_model_id": "gemini-flash-lite-image",
"contact_email": "ops@acme.com",
"monthly_quota_mb": 10240,
"rate_limit_per_minute": 60
}'Response (keys shown only once):
{
"data": {
"tenant": {
"tenantId": "acme-corp",
"tenantName": "ACME Corporation",
"isActive": true,
"monthlyQuotaMb": 10240,
"rateLimitPerMinute": 60
},
"apiKeys": [
{ "apiKey": "sk_live_...", "keyName": "Production API Key", "type": "live" },
{ "apiKey": "sk_test_...", "keyName": "Test API Key", "type": "test" }
]
}
}Legacy admin deployments may use X-Admin-Token instead of a session token.
Provider Key Pools
After creating a tenant, open its admin detail view and use Manage Gemini Key Pool or Manage OpenAI Key Pool. Each pool accepts up to five keys and supports two update modes:
- Append Keys preserves the encrypted pool and adds new, deduplicated keys.
- Replace Pool removes existing keys that are not included in the submitted set.
Consecutive generation requests rotate their starting key. Authentication, quota, billing-limit, and provider rate-limit responses fail over to the next configured key.
Direct Database / Scripting
For bulk setup:
npx tsx scripts/create-tenant.ts \
--tenant-id new-tenant \
--tenant-name "New Tenant" \
--gemini-key "AIza..." \
--email ops@example.comThe script prints the SQL (and wrangler d1 execute commands) to create the tenant with an encrypted Gemini key; it does not run them. It only takes a Gemini key. Add an OpenAI key and choose allowed models afterwards in the Admin console or with PATCH /admin/tenants/:id. The six model IDs are gemini-flash-lite-image, gemini-flash-image, gemini-pro-image, openai-gpt-image-2, openai-gpt-image-2.5-flare and openai-gpt-image-2.5-sunburst.
Managing API Keys
Create Additional Keys
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" }'List API Keys
GET /admin/tenants/:tenantId{
"data": {
"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
}
]
}
}Revoke / Disable Keys
There is no REST endpoint to revoke a key. To disable one:
- Set
is_active = 0for the key in theapi_keystable. - Remove the key from the
API_KEYSKV namespace if present.
Quotas and Rate Limits
Update tenant limits via:
PATCH /admin/tenants/:tenantIdcurl -X PATCH "https://gobananasai.com/admin/tenants/acme-corp" \
-H "Authorization: Bearer <admin_session_token>" \
-H "Content-Type: application/json" \
-d '{
"monthly_quota_mb": 20480,
"rate_limit_per_minute": 120
}'Quotas reset at the start of each month. To manually reset usage in D1:
DELETE FROM usage_logs
WHERE tenant_id = 'acme-corp'
AND timestamp >= date('now', 'start of month');Tenant Lifecycle
Activate / Deactivate
curl -X PATCH "https://gobananasai.com/admin/tenants/acme-corp" \
-H "Authorization: Bearer <admin_session_token>" \
-H "Content-Type: application/json" \
-d '{ "is_active": false }'Deleting Tenants
Tenant deletion is not exposed via REST. For self‑hosting, delete rows from:
tenantsapi_keys- tenant‑scoped tables such as
images,sessions,characters,product_references
Prefer scripting migrations for safe bulk cleanup.