Fresh Jots MCP Server

Servidor MCP FreshJotsDotCom

Documentação

REST com token Bearer. Apenas notas de texto simples. Disponível nos planos Dev e Team. Para a apresentação sistêmica — cada script tem seu próprio caderno, anexação por nome de arquivo para jobs cron e resumos de sessões de IA, fluxos de trabalho do mundo real — veja /for/developers. URL base: https://freshjots.com/api/v1. Legível por máquina: a mesma API como documento OpenAPI 3.1 em /openapi.json, para geradores de clientes, Postman e ferramentas de agentes.

Referência rápida — clientes

Linux e macOS

homebrew-freshjots

Windows e JS

freshjots-js

Gem Ruby

gem "freshjots"

Exemplos rápidos de uso da API. curl prontos para copiar para os fluxos de trabalho comuns: criar, logs somente-anexação, anexar por nome de arquivo, listar.

Exemplos rápidos →

Trabalhando com pastas? Crie pastas, coloque notas nelas na criação, envio em massa para uma pasta.

API de pastas →

Heartbeat para programadores. Uma URL de ping por fonte, verificada a cada minuto: cron, systemd, Docker, Kubernetes, Ruby, Python, Node, Go, PowerShell e o relatório JSON. Guias para Docker, seu próprio código, workers de jobs e máquinas atrás de NAT.

Exemplos de heartbeat →

No Windows, ou prefere não instalar? Escreva na sua conta pelo PowerShell sem instalação, ou com o cliente npm / pip — sem precisar de WSL.

Windows / PowerShell →

Autenticação

Gere um token pessoal em /settings/api_tokens. Os tokens são exibidos apenas uma vez na criação e armazenados como hash de via única — copie imediatamente para seu gerenciador de senhas ou perfil do shell.

Authorization: Bearer mn_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Token ausente ou inválido retorna 401 unauthenticated. Titular do token sem acesso à API (plano Free / Personal) retorna 403 forbidden.

Escopos. Cada token é emitido com um nível de permissão — read_write (acesso total, o padrão), read_only (somente GET / HEAD) ou write_only (somente POST / PATCH / PUT / DELETE). Um token também pode ser bloqueado para uma única nota, para que possa ler ou anexar exatamente àquele fluxo e nada mais — útil como credencial por script. Um quarto escopo, append, dá suporte à extensão do navegador: emitido em Configurações → Extensão do navegador (aberto a todo assinante ativo, incluindo Personal), é somente POST e sempre bloqueado a uma nota, para que a extensão possa recortar páginas naquela nota e nada mais. Uma solicitação fora do escopo do token retorna 403 forbidden com uma mensagem nomeando a restrição. Escolha o escopo ao criar o token.

Tokens de equipe

Workspaces no plano Team emitem tokens em /team/api_tokens (somente proprietário / administrador). Mesmo formato de token Bearer, mesmos endpoints, mesmo envelope de erro — mas cada leitura e escrita resolve para as notas, pastas e pool de armazenamento da equipe, não para o pool pessoal do ator.

  • GET /notes lista apenas as notas da equipe; as notas pessoais do ator nunca aparecem.
  • POST /notes cria com team_id definido; o campo user_id da linha registra o ator (quem escreveu) para o log de auditoria em /team/audit_events.
  • /folders/*, /notes/bulk e /notes/:id/move são sensíveis a escopo — um token de equipe só vê as pastas da equipe, nunca as pessoais, e recusa posicionamento entre pools com 404 not_found.
  • Armazenamento, limites de taxa e limites por plano vêm da assinatura da equipe, não do ator. Bulk é habilitado em todo token de equipe — a assinatura da equipe é o direito.

O limite de tokens ativos de uma equipe é de 30 tokens simultâneos. A revogação fica no mesmo painel — tokens revogados permanecem listados para auditoria, mas param de autenticar imediatamente.

Endpoints

MétodoCaminhoFinalidade
GET/notesListar notas (resumo). Pesquisar ?q=<words> (a mesma busca de texto completo do aplicativo: título, nome de arquivo e corpo, melhor correspondência primeiro, a menos que ?sort seja fornecido). Filtrar ?format=plain|rich, ?folder_id=N (ou none para sem pasta). Ordenar ?sort=created|updated|appended (padrão atualizado). Paginar ?limit=N&offset=N (máx. 200/página).
GET/notes/:idNota completa (plain_body + byte_size). Para acompanhar um log: ?tail=50 retorna as 50 linhas mais recentes, ?since=<bytes> apenas o que foi anexado depois que o corpo tinha aquele tamanho (armazene o byte_size retornado como o próximo since); slice na resposta descreve o corte.
POST/notesCriar nota de texto simples. Corpo: {note: {title, plain_body, folder_id?, append_only?, client_encrypted?}}. client_encrypted: true armazena uma nota que você criptografou localmente com sua própria chave — mantida literalmente, nunca lida ou indexada (somente contas pessoais; imutável após a criação).
PATCH/notes/:idAtualizar título / plain_body / configurações. As configurações incluem append_deadline_hours, alert_email, match_pattern e os campos de webhook. O formato é imutável. Em notas somente-anexação, campos de conteúdo são recusados, mas configurações (folder_id, append_deadline_hours, alert_email, webhook_url, webhook_secret) são aceitas.
DELETE/notes/:idExcluir nota.
POST/notes/:id/appendAnexação atômica a plain_body. Corpo: {text}.
POST/notes/:id/moveMover para pasta. Corpo: {folder_id} ou null.
GET/notes/by-filename/:filenameEncontrar por nome de arquivo em vez de id. Aceita os mesmos ?tail e ?since que GET /notes/:id.
PATCH/notes/by-filename/:filenameMesma forma de corpo que PATCH /notes/:id, endereçada pelo nome do fluxo. Útil para reconfigurar a nota de um script (prazo, URL de webhook) sem primeiro consultar o id.
POST/notes/by-filename/:filename/appendEndereçamento por fluxo — anexar por nome de arquivo. Cria a nota se ausente (somente-anexação por padrão). No primeiro toque, client_encrypted: true a abre como fluxo criptografado pelo cliente — envie uma linha de ciphertext autocontida por anexação (somente contas pessoais). Ignorado depois que a nota existe.
POST/notes/bulkAté 50 criações por chamada. Corpo: {notes: [...]}.
GET/foldersListar pastas.
GET/folders/:idMostrar uma única pasta.
POST/foldersCriar pasta. Corpo: {folder: {name}}.
PATCH/folders/:idRenomear pasta.
DELETE/folders/:idExcluir pasta. As notas dentro são preservadas (movidas para a pasta interna default).

Limites de taxa

  • Dev: 600 leituras / 60 escritas / 300 anexações por minuto, por token. 3 tokens ativos. 15 GB de armazenamento. Endpoint bulk habilitado.
  • Team: 2.000 leituras / 200 escritas / 1.000 anexações por minuto, por token. 30 tokens ativos por equipe. 50 GB de armazenamento do workspace. Endpoint bulk habilitado. 50.000 notas de texto simples / 1.000 notas ricas por equipe.

Solicitações limitadas retornam 429 rate_limited com os cabeçalhos Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining e X-RateLimit-Reset.

A divisão completa por plano — incluindo limites de tamanho por nota, limites de navegador, tetos de paginação, política de expiração de token e a janela de exportação — está na página Service limits.

Repetições idempotentes

Todo endpoint de escrita (POST, PATCH, PUT, DELETE) aceita um cabeçalho de solicitação Idempotency-Key. Uma solicitação repetida com a mesma chave + mesma impressão digital do corpo reproduz a resposta original em vez de executar a ação duas vezes — seguro para repetições de "a solicitação anterior teve sucesso antes do meu timeout?" em jobs cron e scripts de CI.

  • Cabeçalho: Idempotency-Key: <your-key>
  • Formato da chave: 8..255 caracteres. UUIDs funcionam; qualquer string nesse intervalo também.
  • Janela de repetição: 24 horas. Depois disso, a mesma chave é aceita como uma nova solicitação.
  • Repetições carregam Idempotent-Replay: true na resposta para que seu cliente possa identificar.

Uma chave repetida com uma impressão digital do corpo diferente retorna 409 idempotency_key_conflict — o servidor se recusa a sobrescrever silenciosamente a resposta de uma solicitação anterior. Repita com o corpo original ou gere uma nova chave.

curl -X POST https://freshjots.com/api/v1/notes \
  -H "Authorization: Bearer $FRESH_JOTS_TOKEN" \
  -H "Idempotency-Key: cron-2026-05-06-evening-digest" \
  -H "Content-Type: application/json" \
  -d '{"note":{"title":"Evening digest","plain_body":"...","format":"plain"}}'

Envelope de erro

Todas as respostas de erro compartilham a mesma forma:

{ "error": { "code": "validation_failed", "message": "Title can't be blank", "details": ["Title can't be blank"] } }

Códigos de erro estáveis:

  • unauthenticated — token ausente / inválido (401)
  • forbidden — token sem acesso à API, fora do escopo ou conta não confirmada (403)
  • note_locked — edição de conteúdo tentada em nota somente-anexação; anexações e exclusão são permitidas, edições não (403)
  • not_found — registro ausente ou pertencente a outro usuário (404)
  • validation_failed — entrada inválida ou violação de esquema (422)
  • cap_exceeded — contagem de notas acima do limite do seu plano (422)
  • storage_cap_exceeded — bytes totais excederiam seu limite de armazenamento (422)
  • content_too_large — nota única acima do limite de bytes por formato (413). Uma nota somente-anexação faz roll-over em vez de recusar (veja notas somente-anexação), então nesse caso isso significa que o texto sozinho excede o limite ou o roll-over foi bloqueado; a mensagem diz qual.
  • content_type_mismatch — tentativa de escrita de nota rica via API (422)
  • rate_limited — janela de throttle excedida (429)
  • idempotency_key_conflict — chave de repetição reutilizada com corpo diferente (409). Veja Repetições idempotentes acima.
  • service_unavailable — backend temporariamente inacessível; seguro repetir com backoff (503)

Watchdog e webhooks

Dois controles por nota para transformar um fluxo em substrato de monitoramento. Ambos são configurados via PATCH /notes/:id (ou PATCH /notes/by-filename/:filename) e também podem ser definidos na página de Configurações por nota na interface web. Ambos exigem que a nota seja somente-anexação; o sinalizador subjacente é definido na criação ({"append_only": true}) ou pelo botão de bloqueio no navegador.

Alertas de interruptor de homem morto

Defina append_deadline_hours em uma nota somente-anexação (1–720) e o Fresh Jots envia um e-mail quando nenhuma anexação chega nessa janela. A próxima anexação limpa o alerta; se o script se recuperar e falhar novamente, você recebe um novo e-mail — sem redefinição manual. alert_email opcional substitui o destino (padrão: e-mail da sua conta).

curl -X PATCH https://freshjots.com/api/v1/notes/by-filename/cron-jobs-prod \
  -H "Authorization: Bearer $FRESHJOTS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"note":{"append_deadline_hours":2,"alert_email":"oncall@example.com"}}'

Alertas de correspondência de conteúdo

O interruptor de homem morto reporta silêncio; uma regra de correspondência de conteúdo reporta uma linha. Defina match_pattern em uma nota somente-anexação (texto sem diferenciar maiúsculas/minúsculas, até 200 caracteres) e o Fresh Jots envia um e-mail quando uma anexação o contém, com a linha correspondente no e-mail. Uma rajada de linhas correspondentes envia um alerta e, no máximo, mais um a cada 10 minutos enquanto continuarem chegando; a nota em si tem todas as linhas. O destino é o mesmo do interruptor de homem morto: alert_email ou o e-mail da sua conta para nota pessoal, o destino de alerta do workspace para nota de equipe (um evento team_alert.content_match assinado quando esse destino é um webhook). O campo de resposta match_alerted_at mostra o último alerta.

curl -X PATCH https://freshjots.com/api/v1/notes/by-filename/cron-jobs-prod \
  -H "Authorization: Bearer $FRESHJOTS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"note":{"match_pattern":"Traceback"}}'

Webhooks de saída

Uma URL para tudo? Defina um webhook de alerta padrão em Configurações: uma nota ou slot de heartbeat sem URL própria posta lá (somente notas pessoais; notas de equipe usam o destino do workspace). Defina webhook_url em uma nota somente-anexação e cada anexação bem-sucedida faz POST do novo conteúdo para seu endpoint. Por padrão, o corpo é um envelope JSON assinado — HMAC-SHA256 no cabeçalho X-FreshJots-Signature (formato: sha256=<hex>) usando o webhook_secret que você configura; um segredo em branco assina com a string vazia, e o segredo nunca é lido de volta via API. Defina webhook_format para um formato de chat — slack, discord, google_chat, mattermost ou rocket_chat — para enviar uma mensagem de chat nativa em vez disso (veja "Formatos de payload" abaixo).

Dez respostas consecutivas não-2xx (ou falhas de transporte) desabilitam automaticamente o webhook para que um receptor morto não drene a fila de jobs. Reative-o salvando a nota novamente — seja com uma nova URL ou, uma vez desabilitado automaticamente, com a URL inalterada (o salvamento deliberado é o reconhecimento). webhook_failure_count e webhook_disabled_at são expostos em GET /notes/:id para monitoramento.

curl -X PATCH https://freshjots.com/api/v1/notes/by-filename/payments-prod \
  -H "Authorization: Bearer $FRESHJOTS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"note":{"webhook_url":"https://hooks.example.com/x","webhook_secret":"sk_..."}}'

Forma do payload:

{
  "event": "note.appended",
  "delivered_at": "2026-05-02T12:34:56Z",
  "delivery_id": "<uuid>",
  "note": {
    "id": 123, "filename": "payments-prod", "title": "payments-prod",
    "byte_size": 4096, "last_appended_at": "2026-05-02T12:34:55Z"
  },
  "appended_text": "...new chunk (capped at 8 KB)...",
  "appended_bytes": 42,
  "appended_truncated": false
}

Formatos de payload

webhook_format controla a forma do payload e aceita seis valores. generic (padrão — o envelope assinado note.appended mostrado acima, para seu próprio servidor ou um hub de automação como Zapier / Make / n8n) é o único formato assinado. Os outros cinco são mensagens de chat nativas — cole a URL do webhook de entrada daquela plataforma e pronto, sem precisar escrever adaptador: slack (https://hooks.slack.com/services/...), discord (https://discord.com/api/webhooks/...), google_chat (https://chat.googleapis.com/v1/spaces/...), mattermost (uma URL <your-host>/hooks/<token>) e rocket_chat (uma URL <your-host>/hooks/<id>/<token>). Defina isso no menu suspenso da página de Configurações ou no mesmo endpoint PATCH que aceita webhook_url / webhook_secret; o valor atual volta em cada GET /notes/:id. Uma nota não definida usa o padrão generic.

Mensagens em formato de chat são limitadas para caber em cada plataforma: Slack a 3.500 caracteres, Discord a 1.900 (abaixo do limite rígido de 2.000 caracteres do Discord no campo content) e Google Chat, Mattermost e Rocket.Chat a 4.000 cada. A prévia Genérica appended_text permanece inalterada (limitada a 8 KB; o corpo completo está sempre acessível via GET /notes/:id).

Entregas em formato de chat não são assinadas. Nenhum dos webhooks de entrada das plataformas de chat pode verificar uma assinatura, então o cabeçalho X-FreshJots-Signature é omitido inteiramente para os formatos slack, discord, google_chat, mattermost e rocket_chat. Para esses, a URL do hook impossível de adivinhar é a credencial; trate-a como uma senha. As regras de assinatura em "Verificando entregas" abaixo se aplicam apenas ao formato Genérico.

curl -X PATCH https://freshjots.com/api/v1/notes/by-filename/cron-jobs-prod \
  -H "Authorization: Bearer $FRESHJOTS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"note":{"webhook_url":"https://hooks.slack.com/services/T.../B.../...","webhook_format":"slack"}}'

Verificando entregas (somente Genérico)

Seu endpoint é uma URL pública, então verifique cada entrega antes de confiar nela. Defina o mesmo webhook_secret no seu servidor receptor, recalcule o HMAC sobre o corpo da requisição bruto, não analisado e compare-o com o cabeçalho X-FreshJots-Signature usando uma comparação de tempo constante. Rejeite em caso de incompatibilidade antes de analisar o JSON. (Se você configurou um segredo em branco, a chave é a string vazia.)

require "openssl"
require "active_support/security_utils"

SECRET = ENV.fetch("FRESHJOTS_WEBHOOK_SECRET")  # the same value you set on the note

def verified?(raw_body, header)
  expected = "sha256=#{OpenSSL::HMAC.hexdigest("SHA256", SECRET, raw_body)}"
  header.to_s.bytesize == expected.bytesize &&
    ActiveSupport::SecurityUtils.secure_compare(header.to_s, expected)
end

# Rails: verify request.raw_post (NOT params) against
# request.headers["X-FreshJots-Signature"], then head(:unauthorized) on false.

Atualizações ao vivo no navegador

Aviso: quando você tem uma nota aberta na interface web e uma escrita via API a atinge, apenas notas somente-acréscimo são atualizadas no lugar — o navegador assina um stream por usuário e substitui o corpo conforme novo conteúdo chega, sem precisar atualizar. Experimente: abra uma nota somente-acréscimo em uma aba e depois curl um acréscimo de outra janela.

Notas editáveis (CRUD) atualizadas via API (PATCH /api/v1/notes/:id, POST /notes/:id/append em uma nota não bloqueada) não são enviadas para uma aba aberta no navegador — atualize a página para pegar o novo conteúdo. Isso é por design: enviar trocas de conteúdo no meio da edição conflitaria com o autosave em andamento do editor.

Perguntas? Entre em contato diretamente.