Fresh Jots MCP Server
Servidor MCP de FreshJotsDotCom
Documentación
REST con token Bearer. Solo notas de texto plano. Disponible en los planes Dev y Team. Para la propuesta sistémica — cada script tiene su propio cuaderno, anexar por nombre de archivo para trabajos cron y resúmenes de sesiones de IA, flujos de trabajo del mundo real — consulta /for/developers. URL base: https://freshjots.com/api/v1
Referencia rápida — clientes
Linux y macOS
Windows y JS
Gema Ruby
Ejemplos rápidos de uso de la API. Listos para copiar curl para los flujos de trabajo comunes: crear, registros de solo anexar, anexar por nombre de archivo, listar.
¿Trabajas con carpetas? Crea carpetas, coloca notas en ellas al crearlas, sube en lote a una carpeta.
¿En Windows o prefieres no instalar? Escribe en tu cuenta desde PowerShell sin instalación, o con el cliente npm / pip — sin necesidad de WSL.
Autenticación
Genera un token personal en /settings/api_tokens. Los tokens se muestran una sola vez al crearlos y se almacenan como un hash de un solo sentido — cópialo a tu gestor de contraseñas o perfil de shell de inmediato.
Authorization: Bearer mn_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Un token faltante o inválido devuelve 401 unauthenticated. Un titular de token sin acceso a la API (plan Free / Personal) devuelve 403 forbidden.
Ámbitos. Cada token se acuña con un nivel de permiso — read_write (acceso completo, el predeterminado), read_only (solo GET / HEAD), o write_only (solo POST / PATCH / PUT / DELETE). Un token también puede estar bloqueado a una sola nota, de modo que pueda leer o anexar exactamente a ese flujo y nada más — útil como credencial por script. Una solicitud que quede fuera del ámbito del token devuelve 403 forbidden con un mensaje que nombra la restricción. Elige el ámbito al crear el token.
Tokens de equipo
Los espacios de trabajo en el plan Team acuñan tokens en /team/api_tokens (solo propietario / administrador). Mismo formato de cable de token Bearer, mismos endpoints, mismo envoltorio de errores — pero cada lectura y escritura se resuelve a las notas, carpetas y grupo de almacenamiento del equipo, no al grupo personal del actor.
GET /noteslista solo las notas del equipo; las notas personales del actor nunca aparecen.POST /notescrea conteam_idestablecido; eluser_idde la fila registra al actor (quién lo escribió) para el registro de auditoría en /team/audit_events./folders/*,/notes/bulky/notes/:id/moveson conscientes del ámbito — un token de equipo solo ve las carpetas del equipo, nunca las personales, y rechaza la colocación entre grupos con404 not_found.- El almacenamiento, los límites de velocidad y los topes por nivel provienen de la suscripción del equipo, no del actor. El lote está habilitado en cada token de equipo — la suscripción del equipo es el derecho.
El límite de tokens activos de un equipo es de 30 tokens activos a la vez. La revocación vive en el mismo panel — los tokens revocados permanecen listados para auditoría pero dejan de autenticar de inmediato.
Endpoints
| Método | Ruta | Propósito |
|---|---|---|
| GET | /notes | Listar notas (resumen). Filtrar ?format=plain|rich, ?folder_id=N (o none para las sin carpeta). Ordenar ?sort=created|updated|appended (predeterminado actualizado). Paginar ?limit=N&offset=N (máx. 200/página). |
| GET | /notes/:id | Nota completa (plain_body + byte_size). |
| POST | /notes | Crear nota de texto plano. Cuerpo: {note: {title, plain_body, folder_id?, append_only?, client_encrypted?}}. client_encrypted: true almacena una nota que cifraste localmente con tu propia clave — se conserva tal cual, nunca se lee ni indexa (solo cuentas personales; inmutable tras la creación). |
| PATCH | /notes/:id | Actualizar título / plain_body / ajustes. El formato es inmutable. En notas de solo anexar, los campos de contenido se rechazan, pero los ajustes (folder_id, append_deadline_hours, alert_email, webhook_url, webhook_secret) se aceptan. |
| DELETE | /notes/:id | Eliminar nota. |
| POST | /notes/:id/append | Anexo atómico a plain_body. Cuerpo: {text}. |
| POST | /notes/:id/move | Mover a carpeta. Cuerpo: {folder_id} o null. |
| GET | /notes/by-filename/:filename | Buscar por nombre de archivo en lugar de id. |
| PATCH | /notes/by-filename/:filename | Misma forma de cuerpo que PATCH /notes/:id, dirigida por nombre de flujo. Útil para reconfigurar la nota de un script (plazo, URL de webhook) sin buscar primero el id. |
| POST | /notes/by-filename/:filename/append | Direccionamiento por flujo — anexar por nombre de archivo. Crea la nota si falta (solo anexar por defecto). En el primer toque, client_encrypted: true la abre como flujo cifrado por cliente — envía una línea de texto cifrado autocontenida por anexo (solo cuentas personales). Se ignora una vez que la nota existe. |
| POST | /notes/bulk | Hasta 50 creaciones por llamada. Cuerpo: {notes: [...]}. |
| GET | /folders | Listar carpetas. |
| GET | /folders/:id | Mostrar una sola carpeta. |
| POST | /folders | Crear carpeta. Cuerpo: {folder: {name}}. |
| PATCH | /folders/:id | Renombrar carpeta. |
| DELETE | /folders/:id | Eliminar carpeta. Las notas dentro se conservan (sin carpeta). |
Límites de velocidad
- Dev: 600 lecturas / 60 escrituras / 300 anexos por minuto, por token. 3 tokens activos. 15 GB de almacenamiento. Endpoint de lote habilitado.
- Team: 2,000 lecturas / 200 escrituras / 1,000 anexos por minuto, por token. 30 tokens activos por equipo. 50 GB de almacenamiento del espacio de trabajo. Endpoint de lote habilitado. 50,000 notas de texto plano / 1,000 notas enriquecidas por equipo.
Las solicitudes limitadas devuelven 429 rate_limited con los encabezados Retry-After, X-RateLimit-Limit, X-RateLimit-Remaining y X-RateLimit-Reset.
El desglose completo por nivel — incluidos los topes de tamaño por nota, los límites de velocidad en la superficie del navegador, los techos de paginación, la política de expiración de tokens y la ventana de exportación — está en la página de Límites del servicio.
Reintentos idempotentes
Cada endpoint de escritura (POST, PATCH, PUT, DELETE) acepta un encabezado de solicitud Idempotency-Key. Una solicitud repetida con la misma clave + la misma huella de cuerpo reproduce la respuesta original en lugar de ejecutar la acción dos veces — seguro para reintentos de "¿la solicitud anterior tuvo éxito antes de mi tiempo de espera?" desde trabajos cron y scripts de CI.
- Encabezado:
Idempotency-Key: <your-key> - Formato de clave: 8..255 caracteres. Los UUID funcionan; cualquier cadena en ese rango también.
- Ventana de reproducción: 24 horas. Después, la misma clave se acepta como una solicitud nueva.
- Las reproducciones llevan
Idempotent-Replay: trueen la respuesta para que tu cliente pueda distinguirlas.
Una clave repetida con una huella de cuerpo diferente devuelve 409 idempotency_key_conflict — el servidor se niega a sobrescribir silenciosamente la respuesta de una solicitud anterior. O reintenta con el cuerpo original o genera una clave nueva.
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"}}'
Envoltorio de errores
Todas las respuestas de error comparten la misma forma:
{ "error": { "code": "validation_failed", "message": "Title can't be blank", "details": ["Title can't be blank"] } }
Códigos de error estables:
unauthenticated— token faltante / inválido (401)forbidden— el token carece de acceso a la API, está fuera de ámbito, o la cuenta no está confirmada (403)note_locked— actualización o eliminación intentada en una nota de solo anexar; solo se permiten más anexos (403)not_found— registro ausente o propiedad de otro usuario (404)validation_failed— entrada incorrecta o violación de esquema (422)cap_exceeded— recuento de notas por encima del tope de tu nivel (422)storage_cap_exceeded— los bytes totales excederían tu tope de almacenamiento (422)content_too_large— una sola nota por encima del tope de bytes por formato (413)content_type_mismatch— intento de escritura de nota enriquecida vía API (422)rate_limited— ventana de limitación excedida (429)idempotency_key_conflict— clave de reproducción reutilizada con un cuerpo diferente (409). Consulta Reintentos idempotentes arriba.service_unavailable— backend temporalmente inalcanzable; seguro reintentar con retroceso (503)
Vigilante y webhooks
Dos perillas por nota para convertir un flujo en un sustrato de monitoreo. Ambas se configuran vía PATCH /notes/:id (o PATCH /notes/by-filename/:filename) y también se pueden establecer desde la página de Ajustes por nota en la interfaz web. Ambas requieren que la nota sea de solo anexar; la bandera subyacente se establece al crear ({"append_only": true}) o mediante el interruptor de bloqueo del lado del navegador.
Alertas de interruptor de hombre muerto
Establece append_deadline_hours en una nota de solo anexar (1–720) y Fresh Jots te envía un correo cuando no ha llegado ningún anexo en esa ventana. El siguiente anexo limpia la alerta; si el script se recupera y luego falla de nuevo, recibes un correo nuevo — sin reinicio manual. El alert_email opcional anula el destino (por defecto, el correo de tu cuenta).
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"}}'
Webhooks salientes
Establece webhook_url en una nota de solo anexar y cada anexo exitoso hace POST del contenido nuevo a tu endpoint. Por defecto, el cuerpo es un envoltorio JSON firmado — HMAC-SHA256 en el encabezado X-FreshJots-Signature (formato: sha256=<hex>) usando el webhook_secret que configures; un secreto en blanco firma con la cadena vacía, y el secreto nunca se lee de vuelta vía la API. Establece webhook_format a slack o discord para enviar un mensaje de chat nativo en su lugar (consulta "Formatos de carga útil" abajo).
Diez respuestas no-2xx consecutivas (o fallos de transporte) desactivan automáticamente el webhook para que un receptor muerto no drene la cola de trabajos. Re-ármalo guardando la nota de nuevo — ya sea con una URL nueva, o, una vez desactivado automáticamente, con la URL sin cambios (el guardado deliberado es el acuse de recibo). webhook_failure_count y webhook_disabled_at se exponen en GET /notes/:id para monitoreo.
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 de la carga útil:
{
"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 carga útil
webhook_format controla la forma de la carga útil y acepta tres valores: generic (predeterminado — el envoltorio note.appended firmado mostrado arriba, para tu propio servidor o un centro de automatización como Zapier / Make / n8n); slack (un mensaje de chat nativo de Slack — pega una URL de https://hooks.slack.com/services/... y listo, sin adaptador que escribir); y discord (la misma idea para URLs de https://discord.com/api/webhooks/...). Configúralo desde el menú desplegable de la página de Ajustes o desde el mismo endpoint PATCH que acepta webhook_url / webhook_secret; el valor actual viaja de vuelta en cada GET /notes/:id. Una nota sin configurar usa por defecto generic.
Los mensajes en formato de chat se recortan para ajustarse a cada plataforma: Slack a 3 500 caracteres, Discord a 1 900 caracteres bajo el techo duro de 2 000 caracteres de Discord en el campo content. La vista previa Genérica appended_text no cambia (limitada a 8 KB; el cuerpo completo siempre es accesible a través de GET /notes/:id).
Las entregas de Slack y Discord no están firmadas. Los webhooks entrantes de ninguna de las dos plataformas pueden verificar una firma, por lo que el encabezado X-FreshJots-Signature se omite por completo. Para esos formatos, la URL de hook no adivinable es la credencial; trátala como una contraseña. Las reglas de firma en "Verificación de entregas" abajo aplican solo al 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"}}'
Verificación de entregas (solo Genérico)
Tu endpoint es una URL pública, así que verifica cada entrega antes de confiar en ella. Establece el mismo webhook_secret en tu servidor receptor, recalcula el HMAC sobre el cuerpo de solicitud crudo, sin analizar, y compáralo con el encabezado X-FreshJots-Signature usando una comparación de tiempo constante. Rechaza en caso de discrepancia antes de analizar el JSON. (Si configuraste un secreto en blanco, la clave es la cadena vacía.)
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.
Actualizaciones en vivo del navegador
Aviso: cuando tienes una nota abierta en la interfaz web y una escritura de API la alcanza, solo las notas de solo anexar se actualizan en su lugar — el navegador se suscribe a un flujo por usuario y reemplaza el cuerpo a medida que llega contenido nuevo, sin necesidad de refrescar. Pruébalo: abre una nota de solo anexar en una pestaña, luego curl un anexo desde otra ventana.
Las notas editables (CRUD) actualizadas a través de la API (PATCH /api/v1/notes/:id, POST /notes/:id/append en una nota no bloqueada) no se empujan a una pestaña abierta del navegador — refresca la página para recoger el contenido nuevo. Esto es por diseño: empujar intercambios de contenido a mitad de edición chocaría con el autoguardado en vuelo del editor.
¿Preguntas? Contáctame directamente.