Pular para o conteúdo

Auth, Accounts & API Keys

Rotas de gerenciamento de contas, autenticação, API keys e audit logs.


Descrição: Lista todas as contas do usuário autenticado, com dados de cada conta (nome, plano, status), o ID da conta ativa, status de admin da plataforma, e modo suporte.

Auth: Required (Supabase session, sem getAuthContext() — usa supabase.auth.getUser() diretamente) Rate Limit: Nenhum Audit: Nenhum

Request: N/A

Response:

  • 200:
{
"data": {
"accounts": [
{
"account_id": "uuid",
"role": "owner",
"account": {
"id": "uuid",
"name": "Empresa X",
"slug": "empresa-x",
"logo_url": "https://...",
"plan": "starter",
"status": "active",
"created_at": "2026-01-01T..."
}
}
],
"active_account_id": "uuid | null",
"is_admin": false,
"support_mode": null
}
}
  • 401: Auth error

Notas:

  • is_admin é calculado verificando se o email do usuário está em PLATFORM_ADMIN_EMAILS
  • support_mode lido do cookie x17-support-mode (set durante impersonation)
  • active_account_id lido do cookie x17-active-account-id

Descrição: Cria uma nova conta e vincula o usuário como owner. Seta cookie de conta ativa.

Auth: Required (Supabase session, sem getAuthContext()) Rate Limit: Nenhum Audit: Nenhum

Request:

  • Body (Zod createAccountSchema):
{
"name": "string (obrigatório)"
}

Response:

  • 201:
{
"data": {
"account": { "id": "uuid", "name": "Empresa X", "slug": "empresa-x", "..." },
"role": "owner"
}
}
  • 401: Auth error
  • 422: Validation error

Notas:

  • Slug gerado automaticamente: lowercase, caracteres alfanuméricos + hifens, max 50 chars
  • Usa createAdminClient() para bypass RLS na inserção (accounts + account_users)
  • Seta cookie x17-active-account-id para a nova conta (httpOnly, 1 ano TTL)

Descrição: Troca a conta ativa do usuário. Valida que o usuário pertence a conta alvo.

Auth: Required (Supabase session, sem getAuthContext()) Rate Limit: Nenhum Audit: Nenhum

Request:

  • Body (Zod switchAccountSchema):
{
"account_id": "uuid (obrigatório)"
}

Response:

  • 200:
{
"data": {
"account": { "id": "uuid", "name": "Empresa X", "..." },
"role": "owner"
}
}
  • 401: “Você não pertence a esta conta”
  • 422: Validation error

Notas:

  • Seta cookie x17-active-account-id para a nova conta
  • Atualiza account_users.last_active_at para o timestamp atual

Descrição: Aceita um convite de equipe. Valida token, expiração, email do usuário, e cria vínculo account_users.

Auth: Required (getAuthContext()) Rate Limit: Custom: 5 req/min por IP Audit: create em account_user

Request:

  • Body:
{
"token": "string (obrigatório)"
}

Response:

  • 200:
{
"data": {
"message": "Convite aceito",
"account_id": "uuid",
"role": "member"
}
}
  • 403: Convite enviado para outro email
  • 404: Convite não encontrado
  • 422: Token ausente, convite expirado, ou usuário já e membro
  • 429: Rate limit excedido

Notas:

  • Busca convite na tabela team_invites com status “pending”
  • Se expirado, atualiza status para “expired” antes de retornar erro
  • Compara email do convite com email do usuário autenticado (case-insensitive)
  • Usa claim optimista: UPDATE com WHERE status = "pending" para evitar race condition
  • Usa createAdminClient() para bypass RLS

Descrição: Lista todas as API keys do account. Retorna apenas metadados (não a key completa).

Auth: Required (getAuthContext()) Rate Limit: Nenhum Audit: Nenhum

Request: N/A

Response:

  • 200:
{
"data": [
{
"id": "uuid",
"name": "Integração Shopify",
"key_prefix": "x17_live_sk_",
"last_used_at": "2026-02-20T... | null",
"created_at": "2026-01-15T..."
}
]
}

Notas: Ordenadas por created_at desc. NAO retorna key_hash (campo sensivel).


Descrição: Cria uma nova API key. Gera key aleatoria com prefixo x17_live_sk_ e retorna a key completa (única vez que e visivel).

Auth: Required (getAuthContext()) + role “owner” ou “admin” Rate Limit: api preset (30/min) Audit: create em api_key

Request:

  • Body:
{
"name": "string"
}

Response:

  • 201:
{
"data": {
"id": "uuid",
"name": "Integração Shopify",
"key_prefix": "x17_live_sk_",
"full_key": "x17_live_sk_aBcDeFgHiJkLmNoPqRsTuVwXyZ012345",
"created_at": "2026-02-20T..."
}
}
  • 401: Auth error (sem permissão)
  • 429: Rate limit excedido

Notas:

  • Key gerada com crypto.randomBytes(24).toString("base64url")
  • full_key só é retornado nesta resposta (não é armazenado em plaintext no futuro)
  • key_prefix armazenado como os primeiros 12 caracteres para identificação visual
  • Nota de segurança: o código atual armazena a key como key_hash mas sem hash real (comentario “Em producao: hash com bcrypt”)

Descrição: Remove permanentemente uma API key (hard delete).

Auth: Required (getAuthContext()) + role “owner” ou “admin” Rate Limit: Nenhum Audit: delete em api_key

Request:

  • Path params: id (uuid) - ID da API key

Response:

  • 204: No content
  • 401: Auth error (sem permissão)

Notas: Delete scoped por account_id para prevenir acesso cross-tenant.


Descrição: Lista audit logs do account com paginação offset-based. Suporta filtro por entity type e action.

Auth: Required (getAuthContext()) Rate Limit: api preset (30/min) Audit: Nenhum

Request:

  • Query params:
    • page (number, default: 1) - página
    • limit (number, default: 50, max: 100) - itens por página
    • entityType (string, opcional) - filtro por tipo de entidade
    • action (string, opcional) - filtro por ação (“create” | “update” | “delete” | etc.)

Response:

  • 200:
{
"data": {
"logs": [
{
"id": "uuid",
"account_id": "uuid",
"user_id": "uuid",
"action": "create",
"entity_type": "automation",
"entity_id": "uuid",
"changes": { "name": "Nova Automação" },
"ip_address": "192.168.1.1",
"created_at": "2026-02-20T..."
}
],
"total": 150,
"page": 1,
"limit": 50
}
}
  • 429: Rate limit excedido

Notas:

  • Usa createAdminClient() para bypass RLS (tabela audit_logs pode não ter RLS policy para SELECT por conta)
  • Resposta usa NextResponse.json direto (não success() helper) — formato ligeiramente diferente: { data: { logs, total, page, limit } } em vez de { data: [...] }
  • Páginação via range(offset, offset + limit - 1) do Supabase
  • Contagem total via select("*", { count: "exact" })