Multi-Tenancy
The Voki platform uses a multi-tenant architecture with complete data isolation per tenant. This guide explains how the tenant system works and how to interact with the API correctly.
Concept
Each company (Voki customer) is an independent tenant. Isolation is guaranteed by:
- Separate PostgreSQL schema per tenant
- JWT with tenant claim for authentication
X-Tenantheader on all authenticated requests
Tenant Identification
Each tenant has a unique slug that identifies it. The slug is defined when the tenant is created and cannot be changed.
Examples of slugs: avanter, clinica-abc, empresa-xyz
How It Works
1. Login
During login, the tenant is specified in the request body:
curl -X POST https://voki.avanter.com.br/api/auth/login \
-H "Content-Type: application/json" \
-d '{
"email": "user@company.com",
"password": "password123",
"tenant": "avanter"
}'The returned JWT token contains the tenant in its claims:
{
"sub": "user_uuid",
"tenant": "avanter",
"role": "manager",
"exp": 1708387200
}2. Authenticated Requests
After login, all requests must include:
Authorization: Bearer {token}- JWT tokenX-Tenant: {slug}- Tenant slug
curl -X GET https://voki.avanter.com.br/api/v1/users \
-H "Authorization: Bearer eyJhbGci..." \
-H "X-Tenant: avanter"Important
The X-Tenant header must match the tenant in the JWT token. If there is a mismatch, the request will be rejected with 401 Unauthorized.
3. Data Isolation
Each request operates exclusively on the authenticated tenant's data:
- A user from the
avantertenant never accesses data from theclinica-abctenant - All SQL queries are executed within the tenant's schema
- IDs are unique within each tenant but may repeat across tenants
Technical Architecture
┌─────────────────────────────────────────────────┐
│ PostgreSQL 16 │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌──────────┐ │
│ │ public │ │ avanter │ │ clinica │ │
│ │ (shared) │ │ (tenant) │ │ (tenant) │ │
│ │ │ │ │ │ │ │
│ │ - companies │ │ - users │ │ - users │ │
│ │ - plans │ │ - calls │ │ - calls │ │
│ │ │ │ - customers │ │ - ... │ │
│ └─────────────┘ └─────────────┘ └──────────┘ │
└─────────────────────────────────────────────────┘publicschema: Shared data (companies, plans, global settings)- Per-tenant schema: Isolated data (users, calls, customers, recordings, etc.)
Creating a New Tenant
New tenants are created via the signup flow:
# 1. Validate document
curl -X POST https://voki.avanter.com.br/api/signup/validate-document \
-H "Content-Type: application/json" \
-d '{"document": "12.345.678/0001-90", "document_type": "cnpj"}'
# 2. Register
curl -X POST https://voki.avanter.com.br/api/signup/register \
-H "Content-Type: application/json" \
-d '{
"company_name": "New Company",
"document": "12.345.678/0001-90",
"document_type": "cnpj",
"admin_name": "Administrator",
"admin_email": "admin@newcompany.com",
"admin_password": "securePassword123",
"plan": "professional"
}'
# 3. Checkout (Asaas)
curl -X POST https://voki.avanter.com.br/api/signup/checkout \
-H "Content-Type: application/json" \
-d '{
"tenant": "new-company",
"plan": "professional",
"success_url": "https://...",
"cancel_url": "https://..."
}'Best Practices
- Store the tenant slug along with the token in your client
- Always include the
X-Tenantheader - even though the token already contains the tenant - Don't hardcode slugs - obtain them from login or configuration
- Handle 401 errors - they may indicate an expired token OR an invalid tenant
- Renew tokens before expiration using the refresh token
Plan Limits
Each tenant has limits based on their subscribed plan:
| Resource | Starter | Professional | Enterprise |
|---|---|---|---|
| Concurrent calls | 2 | 10 | Unlimited |
| Users | 5 | 50 | Unlimited |
| Departments | 3 | 20 | Unlimited |
| Recording | Yes | Yes | Yes |
| AI Transcription | No | Yes | Yes |
| Advanced Analytics | No | Yes | Yes |
Check the current plan via GET /api/v1/billing/plan.
