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
- Pull primeiro, push como acelerador. Toda informação DEVE estar disponível por
GET; push apenas antecipa mudanças. - Incremental e idempotente. Listas aceitam
updated_since/sincee cursor; reprocessar a mesma página não causa efeitos colaterais. - IDs estáveis. Todo objeto tem um identificador que nunca muda (
idouexternal_id). - Markdown como formato de conteúdo. Textos longos em
body_markdown(CommonMark). - 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:00ou...Z). Datas sem hora:YYYY-MM-DD. - Valores monetários DEVEM ser inteiros em centavos (
mrr_cents,price_cents,amount_cents) comcurrencyISO 4217 quando aplicável (padrãoBRL). - O consumidor envia
X-EKSP-Version: 1.0eUser-Agent: Encantra-Connect/1.0. O provedor DEVERIA ecoarX-EKSP-Versionna 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 quandoauth.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.nameecapabilitiessã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 retornarnext_cursornão nulo quandohas_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(ouoccurred_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 comseverity(okinfowarningcritical). 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
inputcontra oinput_schemae DEVE tratar a mesmaIdempotency-Keycomo 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)), comt= 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:
202com{"data": {"accepted": n, "rejected": [{index, reason}]}};401assinatura inválida;404conector inexistente/pausado;413excesso. - 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
internalnunca é exibido a clientes finais pela Encantra. - Exclusões de clientes (direito ao esquecimento) DEVERIAM ser comunicadas via
customer.upsertedcomstatus: "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_sincefiltra 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_schemae respeitamIdempotency-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.