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
Windows e JS
Gem Ruby
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.
Trabalhando com pastas? Crie pastas, coloque notas nelas na criação, envio em massa para uma pasta.
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.
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.
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 /noteslista apenas as notas da equipe; as notas pessoais do ator nunca aparecem.POST /notescria comteam_iddefinido; o campouser_idda linha registra o ator (quem escreveu) para o log de auditoria em /team/audit_events./folders/*,/notes/bulke/notes/:id/movesão sensíveis a escopo — um token de equipe só vê as pastas da equipe, nunca as pessoais, e recusa posicionamento entre pools com404 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étodo | Caminho | Finalidade |
|---|---|---|
| GET | /notes | Listar 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/:id | Nota 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 | /notes | Criar 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/:id | Atualizar 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/:id | Excluir nota. |
| POST | /notes/:id/append | Anexação atômica a plain_body. Corpo: {text}. |
| POST | /notes/:id/move | Mover para pasta. Corpo: {folder_id} ou null. |
| GET | /notes/by-filename/:filename | Encontrar por nome de arquivo em vez de id. Aceita os mesmos ?tail e ?since que GET /notes/:id. |
| PATCH | /notes/by-filename/:filename | Mesma 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/append | Endereç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/bulk | Até 50 criações por chamada. Corpo: {notes: [...]}. |
| GET | /folders | Listar pastas. |
| GET | /folders/:id | Mostrar uma única pasta. |
| POST | /folders | Criar pasta. Corpo: {folder: {name}}. |
| PATCH | /folders/:id | Renomear pasta. |
| DELETE | /folders/:id | Excluir 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: truena 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.