EKSP v1.0 — Especificação

Encantra Knowledge & Signals Protocol · Versão 1.0 · Status: estável · Licença: CC BY 4.0

As palavras DEVE (MUST), NÃO DEVE (MUST NOT), DEVERIA (SHOULD), PODE (MAY) seguem a RFC 2119.

  • Provedor: o sistema que expõe dados (seu produto, BaaS, ERP, loja…).
  • Consumidor: a Encantra (ou outro cliente do protocolo).
  • URL base: a raiz HTTPS do provedor, sob a qual existe /.well-known/encantra.json. Ex.: https://api.seuproduto.com/eksp.

1. Princípios

  1. Pull primeiro, push como acelerador. Toda informação DEVE estar disponível por GET; push apenas antecipa mudanças.
  2. Incremental e idempotente. Listas aceitam updated_since/since e cursor; reprocessar a mesma página não causa efeitos colaterais.
  3. IDs estáveis. Todo objeto tem um identificador que nunca muda (id ou external_id).
  4. Markdown como formato de conteúdo. Textos longos em body_markdown (CommonMark).
  5. Mínimo de dados pessoais. Envie só o necessário para atender (LGPD art. 6º, III).

2. Transporte e convenções

  • O provedor DEVE usar HTTPS (TLS 1.2+) em produção.
  • Requisições e respostas DEVEM ser application/json; charset=utf-8.
  • Datas DEVEM estar em ISO 8601 com fuso (2026-10-28T10:20:00-03:00 ou ...Z). Datas sem hora: YYYY-MM-DD.
  • Valores monetários DEVEM ser inteiros em centavos (mrr_cents, price_cents, amount_cents) com currency ISO 4217 quando aplicável (padrão BRL).
  • O consumidor envia X-EKSP-Version: 1.0 e User-Agent: Encantra-Connect/1.0. O provedor DEVERIA ecoar X-EKSP-Version na resposta.
  • Campos desconhecidos DEVEM ser ignorados por ambos os lados (compatibilidade futura).
  • Respostas DEVEM ser servidas em até 10 s (listas) e 1,5 s (contexto ao vivo). O consumidor aplica timeouts de 20 s e 5 s respectivamente.

3. Autenticação

  • O manifesto (/.well-known/encantra.json) DEVERIA ser público (sem credencial).
  • Todos os demais endpoints DEVEM exigir Authorization: Bearer <token>, exceto quando auth.type = "none" (conteúdo totalmente público).
  • Requisição sem token ou com token inválido DEVE retornar 401 com o corpo de erro padrão (§11).
  • O token DEVERIA ser específico para a Encantra, somente-leitura (exceto ações), revogável e rotacionável sem downtime (aceitar dois tokens durante a rotação).
  • O provedor PODE restringir por IP (allowlist) — consulte a equipe Encantra para os IPs de saída.

4. Descoberta — GET /.well-known/encantra.json

{
  "eksp_version": "1.0",
  "provider": { "name": "Aurora Cloud", "description": "…", "url": "https://auroracloud.demo", "support_email": "suporte@auroracloud.demo" },
  "capabilities": ["content", "catalog", "customers", "context", "usage", "status", "actions", "push"],
  "endpoints": { "content": "/content", "context": "/customers/{external_id}/context" },
  "auth": { "type": "bearer" },
  "locales": ["pt-BR", "en"],
  "limits": { "max_page_size": 100, "rate_limit_per_minute": 120 },
  "push": { "events": ["content.upserted", "content.deleted"], "signature": "hmac-sha256" },
  "updated_at": "2026-10-01T12:00:00-03:00"
}
  • eksp_version, provider.name e capabilities são obrigatórios.
  • endpoints é opcional. Os caminhos padrão são:
Capacidade Caminho padrão
content /content
catalog /catalog
customers /customers
context /customers/{external_id}/context
usage /usage
status /status
actions /actions e /actions/{name}

Caminhos relativos são resolvidos contra a URL base; URLs absolutas também são aceitas. Schema: schemas/manifest.schema.json.

5. Listas paginadas

Todos os endpoints de lista (content, customers, usage) DEVEM responder no envelope:

{ "data": [ … ], "next_cursor": "b3BhY28", "has_more": true }
  • limit (query): o provedor DEVE aceitar de 1 até limits.max_page_size (mínimo 50); padrão 50.
  • cursor (query): opaco para o consumidor. O provedor DEVE retornar next_cursor não nulo quando has_more = true.
  • Cursores DEVERIAM ser estáveis por pelo menos 1 hora. Cursor inválido DEVE retornar 400 invalid_cursor.
  • A ordenação DEVE ser determinística; DEVERIA ser crescente por updated_at (ou occurred_at) e depois por id.
  • Uma página NÃO DEVE repetir itens da página anterior.

Schema: schemas/list.schema.json.

6. Conteúdo — GET /content

Parâmetros: updated_since (ISO 8601, opcional), cursor, limit, locale (opcional).

  • Sem updated_since, o provedor DEVE retornar todo o conteúdo ativo (carga completa).
  • Com updated_since, DEVE retornar apenas documentos criados, alterados ou excluídos depois do instante informado. Exclusões DEVEM vir como {"id": "...", "deleted": true, "updated_at": "..."} por pelo menos 30 dias.

Documento (schemas/document.schema.json):

Campo Tipo Obrig. Descrição
id string ✔ Estável e único no provedor (≤160)
type enum ✔* article faq tutorial changelog api_reference product plan policy status video glossary
title string ✔* ≤255 caracteres
body_markdown string ✔* Conteúdo completo. *type, title e body_markdown são dispensados quando deleted=true
updated_at date-time ✔ Última alteração
url string|null Link canônico (citado nas respostas da IA)
summary string|null Resumo de 1–2 frases
locale string BCP 47, padrão pt-BR
tags string[] Palavras-chave
product_area string|null Ex.: "Faturamento", "Autenticação"
audience enum public (padrão) · customer (clientes logados) · internal (só atendentes)
deleted boolean true para remover do índice

Boas práticas para a IA: um assunto por documento, títulos em forma de pergunta/tarefa, passos numerados, limites e números explícitos, sem conteúdo dentro de imagens.

7. Catálogo — GET /catalog

Resposta {"data": {"products": [Produto]}} — sem paginação. Produto: id, name, description, url, plans[]. Plano: id, name, description, price_cents (null = sob consulta), currency, interval (mês, ano, único…), features[] (strings), limits{} (chave → valor). Schema: schemas/catalog.schema.json.

8. Clientes — GET /customers

Parâmetros: updated_since, cursor, limit. Cliente (schemas/customer.schema.json):

Campo Tipo Obrig. Descrição
external_id string ✔ ID da conta no provedor — chave de vínculo com a Encantra
name string ✔ Razão social ou nome
updated_at date-time ✔
domain string|null Domínio de e-mail corporativo
plan string|null Nome do plano atual
mrr_cents integer|null Receita recorrente mensal em centavos
status enum active trialing past_due paused canceled churned
customer_since, renewal_date date
tags, custom array, object Metadados livres
contacts[] Contato external_id ✔, name, email, phone, title, role (champion decision_maker user billing other), last_seen_at

9. Contexto ao vivo — GET /customers/{external_id}/context

Chamado enquanto o atendente/CSM abre um ticket ou conta; DEVE responder em até 1,5 s. Parâmetro opcional user_external_id. Cliente inexistente DEVE retornar 404.

{ "data": {
  "customer_external_id": "cust_1003",
  "generated_at": "2026-10-28T10:20:00-03:00",
  "summary": "Cliente Pro desde 05/2025 — fatura em atraso.",
  "subscription": { "plan": "Pro", "status": "past_due", "mrr_cents": 420000, "currency": "BRL", "renews_at": "2026-11-17" },
  "fields": [ { "key": "billing", "label": "Financeiro", "value": "Fatura vencida há 12 dias", "severity": "critical" } ],
  "invoices": [ { "id": "inv_309", "amount_cents": 420000, "status": "overdue", "due_date": "2026-10-16", "url": "https://…" } ],
  "feature_flags": { "realtime_v2": false },
  "usage_summary": { "period": "last_30_days", "metrics": { "api_requests": 480000 } },
  "incidents": [],
  "links": [ { "label": "Abrir no console", "url": "https://…" } ]
} }
  • fields[] é o coração do painel: pares rotulados com severity (ok info warning critical). DEVERIA ter de 3 a 12 itens, priorizando o que ajuda a resolver casos.
  • O consumidor guarda o contexto em cache por até 5 minutos.
  • NÃO inclua segredos, senhas, dados de cartão ou dados sensíveis (LGPD art. 5º, II). Schema: schemas/context.schema.json.

10. Uso — GET /usage

Parâmetros: since (ISO 8601, obrigatório na prática; o consumidor envia os últimos 30 dias na primeira carga), cursor, limit (até limits.max_page_size; o consumidor pede 500 e o provedor limita).

Evento (schemas/usage-event.schema.json): id ✔ (único, usado para deduplicação), customer_external_id ✔, event ✔ (snake_case, ≤120), occurred_at ✔, user_external_id, quantity (padrão 1), properties{}.

Envie eventos de valor (login, recurso-chave usado, convite enviado, relatório exportado, deploy), não cliques. Agregações diárias são aceitas (quantity = total do dia).

11. Status — GET /status

{"data": {"status": "operational|degraded|partial_outage|major_outage|maintenance", "updated_at": "…", "components": [{name, status}], "incidents": [{id, title, status, severity, started_at, resolved_at, url, body}]}}. Inclua incidentes resolvidos nas últimas 72 h. Schema: schemas/status.schema.json.

12. Ações — GET /actions e POST /actions/{name}

Ações permitem que um humano autorizado na Encantra execute operações no produto com confirmação explícita. A IA nunca executa ações sem confirmação humana.

GET /actions → {"data": [{ "name": "extend_trial", "title": "Estender avaliação", "description": "…", "danger": "low|medium|high", "requires_confirmation": true, "input_schema": { JSON Schema do objeto de entrada } }]}

POST /actions/{name} com headers Idempotency-Key: <uuid> (obrigatório) e corpo:

{ "customer_external_id": "cust_1002", "input": { "days": 7 }, "requested_by": { "name": "Rafael Lima", "email": "rafael@empresa.com" } }

Respostas: 200 {"data": {"ok": true, "message": "Avaliação estendida em 7 dia(s).", "reference": "act_9f2c"}} · 404 cliente/ação inexistente · 422 validation_failed · 409 conflito de estado.

  • O provedor DEVE validar input contra o input_schema e DEVE tratar a mesma Idempotency-Key como a mesma operação por 24 h.
  • O provedor DEVE registrar em auditoria quem pediu (requested_by).
  • Ações destrutivas (danger: high) DEVERIAM exigir um token com escopo distinto.

13. Push — provedor → Encantra

Cada conector tem uma URL e um segredo exibidos na Encantra:

POST https://suporte.inspire4u.com.br/eksp/v1/push/{connector_uuid}
Content-Type: application/json
X-EKSP-Signature: t=1761657600,v1=5f2b…c9e1
  • Assinatura: v1 = hex(HMAC_SHA256(segredo, t + "." + corpo_bruto)), com t = Unix timestamp em segundos. O consumidor rejeita assinaturas com mais de 5 minutos de diferença (proteção contra replay).
  • Corpo: um evento {id, type, created_at, data} ou lote {"events": [ … ]} (máx. 500 eventos, 5 MB).
  • Tipos: content.upserted (data = Documento), content.deleted (data = {id}), customer.upserted (data = Cliente), usage.recorded (data = Evento de uso ou {events: [...]}), incident.opened / incident.resolved (data = Incidente), ping.
  • Respostas: 202 com {"data": {"accepted": n, "rejected": [{index, reason}]}}; 401 assinatura inválida; 404 conector inexistente/pausado; 413 excesso.
  • O provedor DEVERIA reenviar em caso de falha (backoff exponencial até 24 h). Eventos repetidos são idempotentes pelo id.

Schema: schemas/push-event.schema.json.

14. Erros

Todo erro (4xx/5xx) DEVE usar:

{ "error": { "code": "invalid_parameter", "message": "updated_since deve ser ISO 8601.", "details": {} } }

Códigos: unauthorized (401) · forbidden (403) · not_found (404) · invalid_parameter / invalid_cursor (400) · validation_failed (422) · conflict (409) · rate_limited (429, com header Retry-After) · internal_error (500) · unavailable (503).

15. Limites de taxa

O provedor DEVERIA aceitar ao menos 120 requisições/minuto do consumidor e DEVERIA informar X-RateLimit-Limit, X-RateLimit-Remaining e Retry-After em 429. A Encantra respeita Retry-After e reduz a concorrência automaticamente.

16. Versionamento

  • Mudanças compatíveis (novos campos, novas capacidades) não alteram a versão maior.
  • Mudanças incompatíveis gerarão eksp_version: "2.0"; provedores PODEM servir manifestos de versões diferentes em URLs base distintas.
  • O consumidor ignora capacidades desconhecidas.

17. Segurança e privacidade (LGPD)

  • TLS obrigatório; HSTS recomendado.
  • Tokens com escopo mínimo, rotação periódica (90 dias recomendado) e revogação imediata.
  • Base legal: execução de contrato e legítimo interesse para suporte (LGPD art. 7º, V e IX). O provedor DEVERIA documentar o compartilhamento em sua política de privacidade.
  • Não envie: senhas, tokens, dados completos de cartão, documentos (CPF/RG) desnecessários, dados de saúde ou outros dados sensíveis.
  • Conteúdo internal nunca é exibido a clientes finais pela Encantra.
  • Exclusões de clientes (direito ao esquecimento) DEVERIAM ser comunicadas via customer.upserted com status: "churned" e solicitação formal à Encantra para remoção de dados pessoais.

18. Conformidade

Um provedor é compatível com EKSP v1 quando passa no Testador de conformidade da Encantra sem falhas obrigatórias e com nota ≥ 80. Checklist:

  • [ ] Manifesto público e válido em /.well-known/encantra.json
  • [ ] Endpoints protegidos retornam 401 sem token, no formato de erro padrão
  • [ ] Listas no envelope {data, next_cursor, has_more} com cursor opaco
  • [ ] Segunda página não repete itens; cursor inválido → 400
  • [ ] updated_since filtra corretamente (data futura → lista vazia)
  • [ ] Exclusões de conteúdo informadas com deleted: true
  • [ ] Objetos válidos contra os JSON Schemas
  • [ ] Contexto ao vivo < 1,5 s; cliente inexistente → 404
  • [ ] Ações validam input_schema e respeitam Idempotency-Key
  • [ ] Push assinado com HMAC-SHA256 e timestamp
  • [ ] HTTPS em produção

Apêndice A — Verificando a assinatura (pseudocódigo)

header = "t=1761657600,v1=5f2b…"
t, v1  = parse(header)
if abs(now() - t) > 300: rejeitar
esperado = hex(hmac_sha256(segredo, t + "." + corpo_bruto))
if !constant_time_equals(esperado, v1): rejeitar

Apêndice B — Mapeamento na Encantra

EKSP Encantra
Documento knowledge_documents (source_type connector) → busca da Lumi, central de ajuda, sugestões de resposta
Cliente accounts (por external_id) e contacts
Evento de uso usage_events → health score, gatilhos de jornada
Contexto Painel "Dados do produto" no ticket e na visão 360º
Incidente Documento de status + notificação aos agentes
Ação Botão com confirmação no painel; registro em timeline e auditoria

Encontrou algo para melhorar? Escreva para master@inspire4u.com.br.