Guia do Corpus
Corpus: de uma chave à primeira consulta
Este guia leva você de um workspace vazio a uma decisão resolvida, sem ajuda além dele. Ele percorre um único exemplo do início ao fim: um expert chamado publication-review que decide quantos revisores um rascunho precisa antes de ser publicado.
Você precisa de uma organização do Corpus com pelo menos um expert publicado e uma versão de documento publicada. Se for montar isso você mesmo, faça primeiro no workspace; este guia começa a partir de uma chave.
1. Gere uma chave
Tudo o que segue é feito no workspace, em corpus.trysenda.xyz, com sessão iniciada como administrador ou proprietário.
Gere uma chave
Abra Chaves na barra lateral da sua organização e gere uma. Dê a ela um nome que diga quem está chamando, não o que ela chama: "review-bot", não "query-expert". O registro de decisões mostra esse nome ao lado de cada chamada que a chave faz. Restrinja-a ao expert publication-review e a uma tag, para que a chave alcance só o que está dentro das duas.
A chave aparece uma única vez, por completo, logo depois de gerada. Copie antes de sair da tela: depois disso o workspace só mostra o prefixo dela (sk_corpus_...). Guarde-a como CORPUS_KEY no seu shell ou no gerenciador de segredos que este ambiente já usa.
2. Uma primeira consulta, via curl
Toda chamada REST leva Authorization: Bearer $CORPUS_KEY. O caminho base é https://corpus.trysenda.xyz/v1.
Liste os experts que sua chave alcança
curl "https://corpus.trysenda.xyz/v1/experts" \
-H "Authorization: Bearer $CORPUS_KEY"Isso também confirma que a chave funciona e devolve a versão publicada atual do expert e o graphHash dele, os dois números aos quais uma trace fixa sua conclusão.
{
"experts": [
{
"id": "5d9e2c1a-6f3b-4a7d-9e1c-2b3a4c5d6e7f",
"name": "publication-review",
"description": "How many reviewers a draft needs before it can be published.",
"version": 4,
"graphHash": "sha256:2f1a9c...",
"tags": ["editorial"]
}
]
}Peça reviewer_count sem todos os fatos
curl -X POST "https://corpus.trysenda.xyz/v1/experts/publication-review/query" \
-H "Authorization: Bearer $CORPUS_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"goal": "reviewer_count",
"facts": { "audience": "external" }
}'O expert precisa saber se o rascunho contém dados de um cliente antes de concluir. Você só forneceu audience, então o resultado é needs_facts, e needs nomeia exatamente o que falta, tipado: um booleano chamado contains_customer_data.
Não há sessão para retomar. Você não repete a mesma chamada; chama de novo com o fato adicionado.
{
"decisionId": "3c6b1e2d-4f5a-6b7c-8d9e-0f1a2b3c4d5e",
"outcome": "needs_facts",
"conclusion": null,
"needs": [
{
"concept": "contains_customer_data",
"type": { "kind": "boolean" },
"description": "Whether the draft names or quotes a customer."
}
],
"reason": null,
"certainty": null,
"expert": {
"id": "5d9e2c1a-6f3b-4a7d-9e1c-2b3a4c5d6e7f",
"name": "publication-review",
"version": 4,
"graphHash": "sha256:2f1a9c..."
},
"trace": [
{
"id": "s1",
"parentId": null,
"kind": "goal",
"concept": "reviewer_count",
"provenance": { "kind": "caller", "field": "goal" },
"expert": "publication-review",
"expertVersion": 4
},
{
"id": "s2",
"parentId": "s1",
"kind": "fact",
"concept": "audience",
"value": "external",
"provenance": { "kind": "caller", "field": "audience" },
"expert": "publication-review",
"expertVersion": 4
},
{
"id": "s3",
"parentId": "s1",
"kind": "unresolved",
"concept": "contains_customer_data",
"provenance": { "kind": "none" },
"expert": "publication-review",
"expertVersion": 4
}
],
"traceHash": "sha256:9b7e21...",
"inputHash": "sha256:44ad10...",
"cached": false
}Pergunte de novo, com o fato fornecido
curl -X POST "https://corpus.trysenda.xyz/v1/experts/publication-review/query" \
-H "Authorization: Bearer $CORPUS_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"goal": "reviewer_count",
"facts": {
"audience": "external",
"contains_customer_data": "true"
}
}'Desta vez o resultado é resolved, conclusion é "3" (todo valor no fio é uma string; o tipo do concept diz como lê-la), e trace é a derivação completa: a meta, cada fato, a regra que disparou, e seu último passo, um passo de documento que cita publication-policy.md na versão 2. Essa é a citação que a especificação pede: a trace chega até o parágrafo da política em que a regra se apoiou.
{
"decisionId": "8f0f3b7a-9c2e-4d61-8a3f-1a2b3c4d5e6f",
"outcome": "resolved",
"conclusion": "3",
"needs": [],
"reason": null,
"certainty": 1,
"expert": {
"id": "5d9e2c1a-6f3b-4a7d-9e1c-2b3a4c5d6e7f",
"name": "publication-review",
"version": 4,
"graphHash": "sha256:2f1a9c..."
},
"trace": [
{
"id": "s1",
"parentId": null,
"kind": "goal",
"concept": "reviewer_count",
"provenance": { "kind": "caller", "field": "goal" },
"expert": "publication-review",
"expertVersion": 4
},
{
"id": "s2",
"parentId": "s1",
"kind": "fact",
"concept": "audience",
"value": "external",
"provenance": { "kind": "caller", "field": "audience" },
"expert": "publication-review",
"expertVersion": 4
},
{
"id": "s3",
"parentId": "s1",
"kind": "fact",
"concept": "contains_customer_data",
"value": "true",
"provenance": { "kind": "caller", "field": "contains_customer_data" },
"expert": "publication-review",
"expertVersion": 4
},
{
"id": "s4",
"parentId": "s1",
"kind": "rule",
"concept": "reviewer_count",
"value": "3",
"detail": "audience = external and contains_customer_data",
"provenance": {
"kind": "rule",
"ruleId": "external-customer-data",
"expert": "publication-review",
"version": 4
},
"expert": "publication-review",
"expertVersion": 4
},
{
"id": "s5",
"parentId": "s4",
"kind": "document",
"concept": "reviewer_count",
"detail": "Section 3: drafts naming or quoting a customer need three reviewers.",
"provenance": {
"kind": "document",
"documentId": "7a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
"version": 2,
"title": "publication-policy.md"
},
"expert": "publication-review",
"expertVersion": 4
}
],
"traceHash": "sha256:6e12ab...",
"inputHash": "sha256:c02fd7...",
"cached": false
}Faça exatamente a mesma pergunta de novo
Mesma versão do expert, mesmos fatos: cached agora é true, e traceHash não mudou. O engine não reavaliou; devolveu a decisão já registrada. traceHash é como você perceberia se tivesse reavaliado: uma política mudada ou uma regra mudada muda o hash mesmo quando a conclusão não muda.
{
"decisionId": "8f0f3b7a-9c2e-4d61-8a3f-1a2b3c4d5e6f",
"outcome": "resolved",
"conclusion": "3",
"needs": [],
"reason": null,
"certainty": 1,
"expert": {
"id": "5d9e2c1a-6f3b-4a7d-9e1c-2b3a4c5d6e7f",
"name": "publication-review",
"version": 4,
"graphHash": "sha256:2f1a9c..."
},
"trace": [ /* identical to the call above */ ],
"traceHash": "sha256:6e12ab...",
"inputHash": "sha256:c02fd7...",
"cached": true
}Reproduza a decisão
curl -X POST "https://corpus.trysenda.xyz/v1/decisions/8f0f3b7a-9c2e-4d61-8a3f-1a2b3c4d5e6f/replay" \
-H "Authorization: Bearer $CORPUS_KEY"Replay executa decisionId de novo contra a mesma versão do expert e as mesmas entradas, e reporta se a trace ainda bate. matches: true aqui significa que nada mudou sob essa decisão desde que foi registrada. Um match falso nomearia o primeiro passo que divergiu, e o que era esperado contra o que foi encontrado.
{
"matches": true,
"divergedAt": null,
"expected": null,
"actual": null,
"trace": [ /* identical to the resolved call above */ ]
}Liste os documentos e encontre um id
curl "https://corpus.trysenda.xyz/v1/documents?title=policy&limit=2" \
-H "Authorization: Bearer $CORPUS_KEY"Todo documento tem um id, e get_document, GET /v1/documents/{id}, as versões dele e a publicação o recebem. Liste os documentos publicados no escopo da chave com GET /v1/documents, ordenados por título. title mantém os que têm esse texto no título atual, sem diferenciar maiúsculas, e limit define o tamanho da página: 50 por padrão, 200 no máximo.
next é o cursor da página seguinte. Devolva-o como cursor e repita até que next seja null; o cursor é opaco, então não monte um. Passe q em vez de paginar para buscar nos conteúdos: uma busca é ordenada por relevância, limitada por limit (20 por padrão), nunca é paginada, e cada resultado traz um snippet do conteúdo em volta da correspondência.
{
"documents": [
{
"id": "7a1b2c3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d",
"type": "policy",
"language": "en",
"tags": ["editorial"],
"currentVersion": {
"version": 2,
"title": "publication-policy.md",
"publishedAt": "2026-09-14T10:00:00.000Z"
}
},
{
"id": "9c8b7a6d-5e4f-4a3b-8c2d-1e0f9a8b7c6d",
"type": "policy",
"language": "en",
"tags": ["editorial", "legal"],
"currentVersion": {
"version": 1,
"title": "Retention policy",
"publishedAt": "2026-09-02T08:30:00.000Z"
}
}
],
"next": "WyJSZXRlbnRpb24gcG9saWN5IiwiOWM4YjdhNmQtNWU0Zi00YTNiLThjMmQtMWUwZjlhOGI3YzZkIl0"
}create_document e ask_corpus recebem nomes de etiquetas. GET /v1/tags lista as que estão no escopo da chave, para você não precisar adivinhá-las.
curl "https://corpus.trysenda.xyz/v1/tags" \
-H "Authorization: Bearer $CORPUS_KEY"{ "tags": [{ "name": "editorial" }, { "name": "legal" }] }3. A mesma consulta, a partir do Claude
O Corpus expõe um único endpoint MCP, https://corpus.trysenda.xyz/mcp, sobre Streamable HTTP, sem estado (sem sessões). Toda chamada leva a chave como cabeçalho bearer, a mesma chave que você gerou acima.
Claude Code
Adicione o servidor uma única vez, a partir de um shell onde CORPUS_KEY esteja definida:
claude mcp add --transport http corpus https://corpus.trysenda.xyz/mcp \
--header "Authorization: Bearer $CORPUS_KEY"O conector MCP da Claude API
Passe o servidor como uma entrada mcp_servers na requisição, com a chave como o token de autorização dela. Confira a referência atual da API para os nomes exatos dos campos antes de colocar isso em produção; o conector ainda está evoluindo.
{
"mcp_servers": [
{
"type": "url",
"url": "https://corpus.trysenda.xyz/mcp",
"name": "corpus",
"authorization_token": "$CORPUS_KEY"
}
]
}Claude Desktop
A configuração de conectores remotos do Desktop (Settings, Connectors, adicionar um conector personalizado) espera que a maioria dos servidores entregue acesso via OAuth. O Corpus se autentica só por chave de API neste marco; a cadeia OAuth está planejada para um marco posterior. Se o fluxo de conectores do Desktop na sua versão não aceitar uma chave bearer diretamente, use o Claude Code ou o conector da API acima, que aceitam.
As ferramentas
Os nomes das ferramentas e seus schemas de entrada são gerados do mesmo documento OpenAPI que a API REST, então nunca se desalinham dela.
| tool | |
|---|---|
list_experts | Os experts que a chave alcança, com sua versão publicada e o graphHash. |
query_expert | A que você mais vai usar: expert, goal e, opcionalmente, facts e question. O mesmo envelope de POST /v1/experts/{name}/query. |
replay_decision | Executa de novo uma decisão registrada por decision_id e reporta se a trace ainda bate. |
search_documents | Busca ou lista os documentos no escopo da chave. Com query, busca de texto completo sobre as versões atuais, filtrada por type ou tag. Sem ela, os documentos publicados ordenados por título: title restringe por um trecho do título, limit define o tamanho da página (50 por padrão, 200 no máximo) e next no resultado é o cursor que você passa como cursor para a página seguinte, null na última. Cada resultado traz o id que get_document recebe. |
list_tags | As tags no escopo da chave: os nomes que create_document e ask_corpus recebem como tags. |
get_document | Um documento, na versão atual dele ou numa versão que você indicar. |
create_document | Escreve um documento novo. Cada tag que ele carrega precisa estar dentro do escopo da chave. |
{
"name": "query_expert",
"arguments": {
"expert": "publication-review",
"goal": "reviewer_count",
"facts": { "audience": "external", "contains_customer_data": "true" }
}
}O que needs_facts significa para o modelo
Quando query_expert volta com needs_facts, o modelo tem um único movimento correto: ler needs, pedir à pessoa exatamente aqueles concepts (nada mais, nada adivinhado), e chamar query_expert de novo com eles adicionados. Não há resposta parcial para dar nesse meio-tempo nem conclusão para inferir a partir dos fatos já conhecidos; esse é todo o sentido de ter um engine determinístico na frente do modelo. Um modelo que completa um fato faltante por conta própria, ou reporta uma conclusão à qual o engine nunca chegou, quebrou a única garantia que o Corpus existe para dar.
4. O contrato de recusa
Toda chamada a query_expert, via REST ou MCP, devolve um de quatro resultados. Ramifique sobre outcome, nunca sobre o texto de reason ou detail.
| outcome | significa | faça isto |
|---|---|---|
resolved | O engine chegou a uma conclusão. conclusion a contém; trace é a derivação completa. | Use conclusion. Leia trace como evidência; não pergunte de novo. |
needs_facts | Uma regra precisa de um fato que o engine não consegue derivar e que não foi fornecido. needs nomeia cada um, tipado. | Consiga os fatos que faltam e chame de novo com eles adicionados. |
out_of_scope | A meta não é um concept deste expert, ou a chave não tem escopo para alcançá-lo. | Confira a meta e o escopo da chave. Não é um caso para tentar de novo. |
error | A própria chamada foi rejeitada antes de a avaliação começar: autenticação inválida, corpo malformado, limite de taxa. | Leia error.code e error.hint no corpo da resposta (seção 5), não este envelope. |
5. Erros
Uma resposta de erro não leva o envelope da seção 4; é um corpo plano com um único objeto error: code, message, hint e traceId, o id a citar se você pedir ajuda.
{
"error": {
"code": "rate_limited",
"message": "This key has exceeded its rate limit.",
"hint": "Wait for the window in Retry-After, then retry the same request.",
"traceId": "b3c1e9f2-1a2b-4c3d-8e9f-0a1b2c3d4e5f"
}
}Idempotency-Key
Toda escrita (criar um documento, uma versão nova, uma query, um replay) leva um cabeçalho Idempotency-Key. Envie a mesma chave com o mesmo corpo e você recebe o resultado original, não uma segunda escrita. Envie a mesma chave com um corpo diferente e a chamada falha com idempotency_key_reused: use uma chave nova por requisição distinta, e repita a mesma chave numa nova tentativa só quando ela for realmente idêntica.
| code | status HTTP | significado |
|---|---|---|
unauthenticated | 401 | Sem chave, ou uma chave que não pôde ser lida. Confira o cabeçalho Authorization. |
revoked_key | 401 | A chave existiu mas foi revogada. Gere uma nova. |
rate_limited | 429 | O limite de taxa da chave foi excedido. Retry-After indica a espera. |
not_found | 404 | O recurso não existe, ou o escopo da chave não o alcança. Os dois casos parecem iguais de propósito. |
validation_failed | 422 | O corpo da requisição não bate com o schema. hint diz qual campo. |
idempotency_key_reused | 422 | O Idempotency-Key já foi enviado antes com um corpo diferente. |
conflict | 409 | A escrita colidiu com o estado atual, por exemplo publicar sobre uma versão que já mudou. |
quota_exceeded | 429 | A organização usou as decisões, respostas ou páginas indexadas do plano no período (seção 6). Retry-After diz quando a contagem reinicia. |
internal | 500 | Algo falhou do lado do Corpus. Cite traceId se for reportar. |
6. Planos e limites
Todos os planos têm todos os recursos; Basic, Pro e Enterprise mudam em quanto incluem de três coisas por mês. Não há plano grátis. O mês corre desde o início do seu período de cobrança.
- Decisões
- Uma chamada a query_expert, por REST ou MCP, com qualquer resultado. Ler, buscar e reproduzir não contam.
- Respostas
- Uma chamada a ask_corpus (ou POST /v1/answers) a cada duas partes da pergunta: uma pergunta que o Corpus divide em três subperguntas conta duas. É contada quando a pergunta é entendida e antes de qualquer busca. Uma chamada que termina em needs_facts conta uma decisão no lugar.
- Páginas indexadas
- 3.000 caracteres do texto de uma versão publicada, arquivos anexos incluídos, arredondado para cima. Cada versão que você publica é indexada de novo, então conta de novo. Um documento longo custa o mesmo por página que um curto.
- No limite
- Passadas as decisões ou as respostas, a chamada retorna quota_exceeded (429) com Retry-After, e nada é registrado. Sem páginas, publicar retorna quota_exceeded e a versão fica como rascunho; uma versão mais longa que as páginas restantes não é indexada até você publicá-la de novo. O espaço de trabalho continua funcionando e avisa em 80% e em 100%.
- Enterprise
- Quantidades e preço combinados com cada cliente, com contrato anual. Escreva para nós pela página de preços.