Tenants (multi-tenancy)
import { Aside } from ‘@astrojs/starlight/components’;
O WPP Hub é multi-tenant: cada empresa cliente tem dados isolados (chips, apps, mensagens, audit, etc). Endpoints de tenant ficam restritos a super-admin — não confundir com admin regular de tenant.
Modelo
Cada tenant tem:
id— ULIDname— displayslug— kebab-case lowercase, único, imutável, regex^[a-z][a-z0-9-]*$plan—'trial' | 'starter' | 'pro' | 'business' | 'enterprise'trial_ends_at— quando trial expira (null se não-trial)is_active— soft delete bloqueia authmetadata— JSON livre
E tenant_limits aninhado:
max_chips,max_apps,max_messages_per_day,max_storage_bytesfeatures[]— array de flags pagas (audit_csv,transcription,multi_seat, etc.)
Tenants de sistema
Dois tenants são fixos e não devem ser desativados:
| Slug | Propósito |
|---|---|
wpphub-internal | Operador da plataforma. push_devices, admin_users, super-admin holding. |
meuzap | Primeiro cliente real (interno do operador). |
Endpoints
POST /v1/tenants
Cria tenant novo + linha em tenant_limits (transação atômica).
curl -X POST https://hub.gustavomaritan.com/v1/tenants \ -H "Authorization: Bearer $BOOTSTRAP_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Clínica Sol Nascente", "slug": "clinica-sol-nascente", "plan": "trial", "trial_days": 14, "limits": { "max_chips": 2, "max_apps": 1, "max_messages_per_day": 500, "features": ["audit_csv"] } }'Response 201: tenant completo com limits aninhado.
Conflitos:
409 tenant-slug-conflict— slug duplicado.
GET /v1/tenants
Lista todos os tenants com limits embutido. Sem filtro/paginação por enquanto (universos de dezenas de tenants).
curl https://hub.gustavomaritan.com/v1/tenants \ -H "Authorization: Bearer $BOOTSTRAP_KEY"GET /v1/tenants/:id
Detalhe.
PATCH /v1/tenants/:id
Atualiza qualquer combinação de:
name(string)plan(enum)is_active(boolean — false = bloqueia auth, preserva audit)trial_ends_at(ISO datetime ounull)limits.{max_chips, max_apps, max_messages_per_day, max_storage_bytes, features}
Slug não pode ser alterado.
DELETE /v1/tenants/:id
Soft delete: is_active = false. Audit + dados (chips, mensagens, etc) ficam intactos pra compliance. Reverter via PATCH {is_active: true}.
Auditoria
Todas as mutações geram audit:
tenant.createtenant.updatetenant.deactivate
actor_type='bootstrap' enquanto super_admin_keys não existir.
Como o tenant é resolvido nas requests
| Bearer | Tenant resolvido |
|---|---|
API key de app (ak_*) | apps.tenant_id da app dona da key |
Admin key (ak_adm_*) | admin_users.tenant_id do dono da key |
| Bootstrap (env) | meuzap por compat (vai virar cross-tenant no PR 16.4) |
Rotas /v1/chips, /v1/apps, /v1/messages, etc. filtram automaticamente por req.tenantId. Tentar acessar recurso de outro tenant retorna 404 (não 403 — não vazamos existência).