Skip to content

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 — ULID
  • name — display
  • slug — 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 auth
  • metadata — JSON livre

E tenant_limits aninhado:

  • max_chips, max_apps, max_messages_per_day, max_storage_bytes
  • features[] — array de flags pagas (audit_csv, transcription, multi_seat, etc.)

Tenants de sistema

Dois tenants são fixos e não devem ser desativados:

SlugPropósito
wpphub-internalOperador da plataforma. push_devices, admin_users, super-admin holding.
meuzapPrimeiro cliente real (interno do operador).

Endpoints

POST /v1/tenants

Cria tenant novo + linha em tenant_limits (transação atômica).

Terminal window
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).

Terminal window
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 ou null)
  • 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.create
  • tenant.update
  • tenant.deactivate

actor_type='bootstrap' enquanto super_admin_keys não existir.

Como o tenant é resolvido nas requests

BearerTenant 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).