Resumos publicados, prontos para integração.
Uma API somente leitura para bots, automações, RAG, LLMs e serviços externos consumirem os resumos que já foram publicados no DnD Scribe — sem liberar áudio, transcrição completa ou material em revisão.
Quickstart
Ela é criada no
Edit → Integrações e começa com dnd_live_.Use variável de ambiente ou secret manager. Não coloque a chave no frontend.
Faça um
GET /api/v1/summaries com o header Authorization.Use
GET /api/v1/summaries/{id} e, se quiser, peça text/markdown.curl \
-H "Authorization: Bearer $DND_SCRIBE_API_KEY" \
https://dnd.faysk.dev/api/v1/summaries
Autenticação
Cada chave pertence a uma única campanha. A API não recebe campaignSlug na rota externa: o escopo da campanha é resolvido pela própria credencial.
summaries:read. Ele não dá acesso a áudio, ZIPs, transcrições ou ferramentas administrativas.Authorization: Bearer dnd_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
A chave completa é exibida somente na criação ou rotação. O servidor armazena apenas um hash SHA-256 e um prefixo de identificação.
Endpoints
/api/v1/healthHealth check público. Não valida uma API Key específica.
{
"status": "ok",
"apiVersion": "v1",
"service": "DnD Scribe Summary API",
"documentation": "https://dnd.faysk.dev/docs/api"
}
/api/v1/summariesLista resumos publicados. A listagem é leve: retorna a descrição curta e metadados, sem o Markdown completo.
| Parâmetro | Uso |
|---|---|
limit | 1–100, padrão 50. |
cursor | Cursor opaco retornado por pagination.nextCursor. |
updatedAfter | ISO 8601; útil para sincronização incremental. |
from | Data mínima da sessão, inclusiva, em YYYY-MM-DD. |
to | Data máxima da sessão, inclusiva, em YYYY-MM-DD. |
arc | Nome exato do arco, sem diferenciar maiúsculas/minúsculas. |
{
"object": "list",
"apiVersion": "v1",
"data": [
{
"id": "Svz6mvN0cBUk",
"title": "Sessão de 15 de agosto de 2026",
"sessionDate": "2026-08-15",
"arc": "Castelo em outro plano",
"summary": "O grupo atravessou o portal...",
"updatedAt": "2026-08-16T19:30:00.000Z",
"webUrl": "https://dnd.faysk.dev/#/sessao/Svz6mvN0cBUk/resumo"
}
],
"pagination": { "limit": 50, "hasMore": false, "nextCursor": null }
}
/api/v1/summaries/{id}Retorna o resumo completo de uma sessão publicada.
{
"object": "session_summary",
"apiVersion": "v1",
"data": {
"id": "Svz6mvN0cBUk",
"title": "Sessão de 15 de agosto de 2026",
"sessionDate": "2026-08-15",
"arc": "Castelo em outro plano",
"summary": "Descrição curta do card.",
"summaryMarkdown": "# Sessão...\n\n## O portal\n\n...",
"updatedAt": "2026-08-16T19:30:00.000Z",
"webUrl": "https://dnd.faysk.dev/#/sessao/Svz6mvN0cBUk/resumo",
"coverImageUrl": "https://...",
"heroImageUrl": "https://..."
}
}
Markdown puro
No endpoint de detalhe, envie Accept: text/markdown para receber somente o Markdown publicado.
curl \
-H "Authorization: Bearer $DND_SCRIBE_API_KEY" \
-H "Accept: text/markdown" \
https://dnd.faysk.dev/api/v1/summaries/Svz6mvN0cBUk
Paginação por cursor
Quando pagination.hasMore for true, envie exatamente o valor de pagination.nextCursor na próxima chamada. O cursor é opaco: não dependa do conteúdo interno.
GET /api/v1/summaries?limit=50
GET /api/v1/summaries?limit=50&cursor=<nextCursor>
A ordenação é por updatedAt DESC e depois id DESC.
Sincronização incremental
Guarde o instante da última sincronização concluída e use updatedAfter para pedir somente registros alterados desde então.
GET /api/v1/summaries?updatedAfter=2026-08-16T19:30:00.000Z
ETag e cache condicional
Listagens e detalhes enviam ETag. Reenvie o valor em If-None-Match. Se a representação não mudou, a resposta será 304 Not Modified, sem corpo.
If-None-Match: "sum-fAbC123..."
O detalhe também envia Last-Modified.
Rate limit
O limite atual é 300 requisições por minuto por API Key.
X-RateLimit-Limit: 300
X-RateLimit-Remaining: 294
X-RateLimit-Reset: 1786914000
Ao exceder o limite, a API responde 429 e inclui Retry-After. Respeite esse header e use backoff.
Formato de erros
{
"error": {
"code": "invalid_api_key",
"message": "API key invalida, expirada ou revogada."
}
}
| HTTP | Código típico | Significado |
|---|---|---|
| 400 | invalid_cursor | Parâmetro inválido. |
| 401 | invalid_api_key | Chave ausente, inválida, expirada ou revogada. |
| 403 | insufficient_scope | Escopo necessário não está na chave. |
| 404 | summary_not_found | Resumo publicado não encontrado. |
| 406 | not_acceptable | Formato solicitado não é aceito naquele endpoint. |
| 429 | rate_limit_exceeded | Limite por minuto excedido. |
| 500 | internal_error | Falha inesperada. |
Use o status HTTP e error.code para lógica. O texto de message pode evoluir.
Exemplos de consumo
Node.js
const apiKey = process.env.DND_SCRIBE_API_KEY;
const response = await fetch('https://dnd.faysk.dev/api/v1/summaries?limit=20', {
headers: { Authorization: `Bearer ${apiKey}` }
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const payload = await response.json();
console.log(payload.data);
Python
import os, requests
response = requests.get(
"https://dnd.faysk.dev/api/v1/summaries",
headers={"Authorization": f"Bearer {os.environ['DND_SCRIBE_API_KEY']}"},
params={"limit": 50},
timeout=30,
)
response.raise_for_status()
print(response.json()["data"])
RAG / LLM
Use a listagem para descobrir documentos e o detalhe com Accept: text/markdown para obter o corpo a ser indexado. Armazene id, updatedAt, sessionDate, arc e webUrl como metadados.
O que é e o que não é exposto
A API só devolve sessões com status published e resumo completo não vazio, dentro da campanha da chave.
Chaves podem ser revogadas ou rotacionadas em Edit → Integrações. A chave antiga é invalidada imediatamente na rotação.
Versionamento
O contrato externo vive em /api/v1/.... Dentro da v1 podemos adicionar campos sem remover os existentes. Consumidores devem ignorar propriedades desconhecidas e nunca interpretar o cursor.
Uma mudança incompatível será publicada em uma nova versão, por exemplo /api/v2/....
A especificação OpenAPI 3.1 está em /docs/api/openapi.yaml.