Pular para o conteúdo principal

Managed Tables API

Endpoints para gerenciar tabelas PostgreSQL proprias da company. Cada company tem um banco managed dedicado, provisionado on-demand. Suporta criacao manual de tabelas, upload de arquivos (CSV/Excel/ZIP) com inferencia de schema, e CRUD de linhas.

Diferenca em relacao a Data API:

  • Data API = consumo de visualizacoes salvas como endpoint publico (read-only via API key).
  • Managed Tables = camada de armazenamento do usuario (CRUD via JWT do console).

Visualizations publicadas via Data API podem ler de tabelas managed-tables, mas os dois modulos sao independentes.

Todos os endpoints exigem autenticacao JWT padrao (Authorization: Bearer <token>) e estao sob o prefixo /v1/managed-tables.

Provisioning

Provisionar banco managed

POST/v1/managed-tables/provision

Cria um banco PostgreSQL dedicado para a company (idempotente — re-chamar retorna a conexao existente).

Required Permission: managed-tables:create

Response: 200 OK

{
"provisioned": true,
"connection_id": "507f1f77bcf86cd799439011"
}

Status do provisionamento

GET/v1/managed-tables/status

Required Permission: managed-tables:list

Response: 200 OK

{
"provisioned": true,
"connection_id": "507f1f77bcf86cd799439011"
}

provisioned: false indica que POST /provision ainda nao foi executado.


Tables CRUD

Listar tabelas

GET/v1/managed-tables

Required Permission: managed-tables:list

Query Parameters:

ParameterTypeDefaultDescription
pageinteger1Numero da pagina
per_pageinteger20Itens por pagina (max 100)
searchstring-Filtra por display_name (case-insensitive)

Response: 200 OK

{
"total": 12,
"quantity": 12,
"records": [
{
"_id": "507f1f77bcf86cd799439011",
"company_id": "507f1f77bcf86cd799439012",
"connection_id": "507f1f77bcf86cd799439013",
"display_name": "vendas_mensais",
"columns": [
{ "name": "produto", "display_name": "Produto", "type": "text", "nullable": false },
{ "name": "valor", "display_name": "Valor", "type": "decimal", "nullable": true }
],
"source": "manual",
"status": "active",
"row_count": 1500,
"created_by_user_id": "507f1f77bcf86cd799439014",
"created_at": "2026-01-15T10:30:00.000Z",
"updated_at": "2026-01-15T10:30:00.000Z"
}
]
}

Obter tabela

GET/v1/managed-tables/:id

Required Permission: managed-tables:list

Response: 200 OK — mesmo shape do item em records da listagem.

Errors: 404 { "error": "Tabela nao encontrada" }


Criar tabela (manual)

POST/v1/managed-tables

Required Permission: managed-tables:create

Pre-requisito: banco provisionado (POST /provision). Caso contrario retorna 400 Base de dados nao provisionada. Ative "Meus Dados" primeiro.

Body:

CampoTipoObrigatorioDescricao
display_namestringSimNome da tabela. Deve casar ^[a-z_][a-z0-9_]*$ (snake_case sem comecar com numero).
columnsarraySimLista de colunas (>=1).
columns[].namestringSimNome PG (snake_case).
columns[].display_namestringSimNome amigavel.
columns[].typeenumSimtext | integer | decimal | boolean | date | datetime.
columns[].nullablebooleanSimSe aceita NULL.
{
"display_name": "vendas_mensais",
"columns": [
{ "name": "produto", "display_name": "Produto", "type": "text", "nullable": false },
{ "name": "valor", "display_name": "Valor", "type": "decimal", "nullable": true }
]
}

Response: 201 Created — mesmo shape de GET /:id.

Errors:

HTTPCodigoDescricao
400MANAGED_TABLE_DUPLICATE_NAMENome ja existe ativo
400-Banco nao provisionado
422-Schema invalido (display_name ou columns)

Atualizar tabela

PUT/v1/managed-tables/:id

Required Permission: managed-tables:update

Body (campos opcionais):

{
"display_name": "novo_nome",
"columns": [...],
"status": "active"
}

Response: 200 OK


Deletar tabela

DELETE/v1/managed-tables/:id

Required Permission: managed-tables:delete

Soft-delete: a tabela some das listagens. Restauracao nao e exposta pela API.

Response: 204 No Content


Upload de arquivos

Fluxo de 3 etapas:

  1. Upload → cria job em status queued. Worker processa o arquivo e infere schema (status analisandowaiting_confirm).
  2. Confirm → usuario revisa schema inferido e confirma; job vai para insertingdone.
  3. Polling → frontend acompanha estado via GET /upload-jobs/:jobId.

Aceita .csv, .xlsx, .xls, .zip. Ate 10 arquivos por upload, 100MB total.

Upload (criar tabela nova)

POST/v1/managed-tables/upload

Required Permission: managed-tables:upload

Content-Type: multipart/form-data

Form fields:

CampoTipoDescricao
filesfile[]1-10 arquivos (campo repetido).

Response: 201 Created

{
"job_id": "507f1f77bcf86cd799439011",
"status": "queued"
}

Upload (anexar a tabela existente)

POST/v1/managed-tables/:id/upload

Required Permission: managed-tables:upload

Content-Type: multipart/form-data

Form fields:

CampoTipoDefaultDescricao
filesfile[]-1-10 arquivos.
modestringappendappend (adiciona linhas) ou replace (truncate + insert).

Response: mesmo shape do upload de tabela nova.


Listar jobs ativos

GET/v1/managed-tables/upload-jobs/active

Required Permission: managed-tables:list

Retorna jobs ainda nao terminais (queued, analisando, waiting_confirm, inserting).

Response: 200 OK

{
"records": [
{
"_id": "...",
"company_id": "...",
"user_table_id": null,
"files": [
{ "original_name": "vendas.csv", "storage_path": "...", "size_bytes": 12345, "mime_type": "text/csv" }
],
"mode": "create",
"status": "waiting_confirm",
"inferred_schema": {
"suggested_table_name": "vendas",
"columns": [{ "name": "produto", "display_name": "Produto", "type": "text", "nullable": false }],
"sample_rows": [{ "produto": "X" }],
"total_row_count_estimate": 1500
},
"confirmed_schema": null,
"progress": null,
"error": null,
"created_at": "2026-01-15T10:30:00.000Z",
"updated_at": "2026-01-15T10:30:00.000Z"
}
]
}

Obter job

GET/v1/managed-tables/upload-jobs/:jobId

Required Permission: managed-tables:list

Response: 200 OK — mesmo shape do item em active.

Status possiveis: queued, analisando, waiting_confirm, inserting, done, failed.

Errors: 404 { "error": "Job nao encontrado" }


Confirmar schema do job

POST/v1/managed-tables/upload-jobs/:jobId/confirm

Required Permission: managed-tables:upload

Aplica o schema confirmado pelo usuario e enfileira a fase inserting.

Body:

{
"display_name": "vendas",
"columns": [
{ "name": "produto", "display_name": "Produto", "type": "text", "nullable": false }
]
}

Para upload em tabela existente (user_table_id setado no job), o display_name reusa o nome legado e nao e validado contra o regex.

Response: 200 OK

{ "status": "inserting" }

Errors:

HTTPCodigoDescricao
400-Job nao esta em waiting_confirm
400MANAGED_TABLE_DUPLICATE_NAMENome ja existe (somente em criacao)
422-display_name invalido (regex), coluna invalida (name/type)

Data CRUD

Listar linhas

GET/v1/managed-tables/:id/data

Required Permission: managed-tables:data-read

Query Parameters:

ParameterTypeDefaultDescription
pageinteger1Numero da pagina
per_pageinteger50Itens por pagina
sort_bystring-field ASC ou -field DESC

Response: 200 OK

{
"total": 1500,
"quantity": 50,
"records": [
{ "id": "uuid", "produto": "X", "valor": 12.5 }
]
}

Inserir linhas

POST/v1/managed-tables/:id/data

Required Permission: managed-tables:data-write

Body:

{
"rows": [
{ "produto": "X", "valor": 12.5 },
{ "produto": "Y", "valor": 30 }
]
}

Response: 201 Created

{ "inserted": 2 }

Errors: 422 se rows ausente ou vazio.


Atualizar linha

PUT/v1/managed-tables/:id/data/:rowId

Required Permission: managed-tables:data-write

Body: objeto com os campos a atualizar.

Response: 200 OK — linha atualizada.

Errors: 404 { "error": "Linha nao encontrada" }


Deletar linha

DELETE/v1/managed-tables/:id/data/:rowId

Required Permission: managed-tables:data-write

Response: 204 No Content

Errors: 404 { "error": "Linha nao encontrada" }


Tipos de coluna

TipoPostgreSQL DDLDescricao
textTEXTString livre
integerBIGINTInteiro 64-bit
decimalNUMERICNumerico de precisao arbitraria
booleanBOOLEANtrue/false
dateDATEData (sem hora)
datetimeTIMESTAMPTZTimestamp com timezone

Limites e restricoes

  • Upload: max 100MB total, max 10 arquivos por chamada.
  • Nome de tabela / coluna: regex ^[a-z_][a-z0-9_]*$.
  • display_name da tabela e unico por company entre tabelas ativas.
  • DELETE e soft-delete: a tabela some das listagens. Restauracao nao e exposta pela API.

Codigos de erro

HTTPCodigoDescricao
400MANAGED_TABLE_DUPLICATE_NAMEConflito de display_name na company
400-Banco nao provisionado / job nao esta em waiting_confirm
401-JWT ausente ou invalido
403-Sem permissao IAM (managed-tables:*)
404-Tabela, linha ou job inexistente
422-Schema invalido (regex de identificador / tipo de coluna)
500-Erro interno