API
O Papex expõe um conjunto de APIs HTTP JSON sob /api.
Referência interativa
Uma especificação OpenAPI 3.1 completa e legível por máquina é servida em /api/openapi.json, e um explorador interativo, com capacidade de Try-it (alimentado por Scalar) está disponível em /api-docs. Abra-o para navegar por cada endpoint, inspecionar esquemas de requisição e resposta, e enviar requisições ao vivo a partir do seu navegador.
Mantendo a documentação em sincronia (code-first)
O documento OpenAPI é gerado a partir do código, não escrito à mão. Cada rota possui um fragmento irmão route.openapi.ts que é a única fonte de verdade para a documentação daquele endpoint. A parte estática (info, components/schemas, components/responses, security) vive em src/lib/openapi/base.ts.
O gerador (src/lib/openapi/generate.ts) varre cada fragmento, os mescla na base e escreve src/lib/openapi/spec.generated.ts — o arquivo servido por /api/openapi.json.
# regenera após editar um fragmento (/api/openapi.json + /api-docs atualizam)
npm run openapi:generateIsto está conectado a predev e prebuild, então a especificação é sempre reconstruída antes de next dev / next build. Nunca edite spec.generated.ts à mão — ele é sobrescrito a cada execução.
Documentando um novo endpoint
Quando você adiciona um manipulador de rota src/app/api/foo/bar/route.ts, crie um irmão route.openapi.ts:
export default {
"/api/foo/bar": {
get: {
tags: ["Discovery"],
summary: "Describe what it does",
// security: [] // omita para endpoints públicos
responses: {
200: { description: "OK", content: { "application/json": { schema: { type: "object" } } } },
},
},
},
} as const;Rode npm run openapi:generate (ou apenas inicie/construa) e o endpoint aparece em /api/openapi.json e /api-docs automaticamente. Esquemas compartilhados vivem em src/lib/openapi/base.ts (ex. #/components/schemas/PaperListItem).
Autenticação
Há duas formas de autenticar:
- Cookie de sessão (
papex_session) — emitido no login e usado pelo navegador. Enviado automaticamente para requisições da mesma origem. - Chave de API (
Authorization: Bearer pk_…) — para scripts e integrações de terceiros. Crie chaves em Configurações → Chaves de API (/settings/api-keys). Uma chave é vinculada à sua conta e herda as permissões RBAC do seu papel, então todo endpoint que funciona com cookie de sessão também funciona com chave de API. O segredo cru é mostrado apenas uma vez na criação; apenas seu hash SHA-256 é armazenado.
Exemplo de requisição com chave de API:
curl -H "Authorization: Bearer pk_live_xxxx" https://your-host/api/papers?pageSize=1Endpoints públicos (não autenticados) — como listar artigos, busca, categorias, autores e health — funcionam para chamadores anônimos, cookies de sessão e chaves de API igualmente.
Auth
POST /api/auth/register— registrar{username, email, displayName, password}POST /api/auth/login— login{identifier, password}POST /api/auth/logout— logoutGET /api/auth/me— usuário atual
Chaves de API
GET /api/settings/api-keys— listar suas chavesPOST /api/settings/api-keys— criar uma chave{name, scopes?:["read"|"write"], environment?:"live"|"test", expiresAt?:ISODate|null}DELETE /api/settings/api-keys?id=<keyId>— revogar uma chave
Artigos
GET /api/papers— listar. Parâmetros de query:q(texto integral ou prefixado comtitle:/au:/abs:/cat:),category,tag,sort(new|updated|by_citations),from(data ISO, apenas artigos criados em/ após),page,pageSize. As linhas incluem umcitationCountresolvido.GET /api/papers/:id— detalhe (incluisubmitter,tags,commentCount)GET /api/papers/:id/comments— comentáriosGET /api/papers/:id/citations— grafo de citações{ outgoing, incoming }GET /api/papers/:id/tags— tags de um artigoPOST /api/papers— submeter (auth obrigatório, precisa depaper:publish); aceita JSON ou multipart (meta + arquivopdfopcional)POST /api/papers/:id/moderate— moderar{action:"approve"|"reject"|"withdraw", reason?}(precisa depaper:moderate)POST /api/papers/:id/citations— adicionar uma citação{targetArxivId?|targetDoi?|targetTitle?}(dono/moderador/admin)POST /api/papers/:id/tags/DELETE /api/papers/:id/tags— anexar/desanexar uma tag{tagId|name}(dono/moderador/admin; cria a tag se o nome for novo)POST /api/submit/archive— enviar umtar.gzde pacote de fontes para auto-ingerir, vincular citações e compilar PDF (auth obrigatório; veja Guia de submissão)
Categorias
GET /api/categories— árvore de categorias
Tags
GET /api/tags— todas as tags com contagem de uso (ordenadas por popularidade)POST /api/tags— criar uma tag{name}(auth obrigatório; idempotente por nome)
Assinaturas
GET /api/subscriptions— listar minhas assinaturas, enriquecidas (nomes de categoria/autor/artigo resolvidos emtitle+ umhrefde link profundo)POST /api/subscriptions— assinar / cancelar assinatura (alternar){type:"category"|"author"|"paper", refId}DELETE /api/subscriptions— cancelar assinatura{type, refId}
Feed e notificações
Anúncios são gerados quando um artigo entra em uma de suas assinaturas (novo-na-categoria, novo-do-autor), quando alguém responde seu comentário, ou por um broadcast de admin.
GET /api/feed— anúncios do usuário atual (?markRead=1também os marca todos como lidos)POST /api/feed— marcar um único anúncio como lido{id}
O sino de cabeçalho (FeedBell) mostra um indicador de não lidas em tempo real mantido em sincronia por uma store Zustand, então ler em qualquer lugar atualiza o indicador imediatamente.
Marcadores
GET /api/bookmarks— listar meus marcadores (cada um resolvido para o título do artigo egroupName); passe?paperId=para obter em vez disso{ bookmarked: boolean }para um único artigoPOST /api/bookmarks— alternar um marcador{paperId, group?}(retorna{ bookmarked: true|false })PATCH /api/bookmarks/:paperId— mover um marcador para um grupo{group}(null o limpa)DELETE /api/bookmarks— remover um marcador{paperId}
Mensagens
As mensagens são classificadas por kind em 8 categorias: system, ticket_reply, announcement, review_result, co_review_request, co_review_result, admin_message, community_reply.
GET /api/messages— mensagens do usuário atual + contagem de não lidas (suporta filtro?kind=)GET /api/messages/stats— estatísticas de não lidasPOST /api/messages/:id/read— marcar como lidoPOST /api/messages—{action:"read-all"}marcar tudo como lido
Tickets
GET /api/tickets— meus tickets (?scope=allapenas admin)POST /api/tickets— criar{subject, type, priority, message}GET /api/tickets/:id— detalhePOST /api/tickets/:id— responderPATCH /api/tickets/:id— admin atualiza status/prioridade
Feedback
POST /api/feedback— enviar feedback (auth obrigatório, cria um ticket automaticamente)
Co-revisão
GET /api/co-reviews?scope=mine|all— listar (minha / todas, permissão respectiva obrigatória)POST /api/co-reviews— atribuir{paperId, reviewerId, note?}(precisa deco_review:assign)GET /api/co-reviews/:id— detalhePOST /api/co-reviews/:id/respond— revisor responde{accepted:boolean}POST /api/co-reviews/:id/submit— enviar parecer{decision:"approve"|"reject"|"revise", comment}
Admin
Os endpoints de admin exigem um papel base moderator / admin e são autorizados por permissão refinada.
GET /api/admin/users— lista de usuários (paginação / busca, precisa deuser:manage)PATCH /api/admin/users/:id— definir papéis{roleKeys:string[]}ou sobrescrever{permission:{key:string, grant:boolean|null}}(precisa deuser:manage/permission:manage)GET /api/admin/roles— lista de papéis (precisa derole:manage)PUT /api/admin/roles/:id— definir permissões do papel{permissionKeys:string[]}POST /api/admin/messages— broadcast{scope:"all"|"role"|"userIds", role?, userIds?, kind:"announcement"|"system"|"admin_message", title, body, link?}(precisa demessage:broadcast)GET /api/admin/stats— estatísticas da plataforma