API
La superficie HTTP: autenticación, la cabecera de espacio de trabajo, idempotencia, errores y OpenAPI.
URL base y descubrimiento
https://api.fastpad.ai| Ruta | Sirve |
|---|---|
GET /openapi.json | el documento OpenAPI 3.1, con ETag fuerte: a un cliente que consulta le cuesta un 304 |
GET /.well-known/oauth-protected-resource | metadatos de recurso protegido (RFC 9728) |
GET /health | disponibilidad más una ida y vuelta a la base de datos |
POST /mcp | la superficie de herramientas para agentes |
Autenticarse
curl https://api.fastpad.ai/v1/notes \
-H 'Authorization: Bearer fpk_live_…' \
-H 'x-fastpad-workspace: 01JB2K…'Todo está autenticado por defecto. Las únicas rutas accesibles sin credencial son la de salud, el documento OpenAPI, los metadatos well-known, las dos rutas de inicio de sesión por dispositivo y las notas publicadas bajo /v1/public/.
Las peticiones tienen un límite de frecuencia por credencial. Cada agente tiene su propio límite por minuto, 120 salvo que se haya fijado otro. Cada persona con sesión iniciada puede hacer 1.200 peticiones por minuto y 20 preguntas por minuto en POST /v1/ask, compartidas entre su sesión y todos los tokens de CLI que tenga. Las dos rutas de inicio de sesión por dispositivo no tienen credencial contra la que contar, así que se limitan por dirección: 20 inicios y 60 consultas por minuto. Un rechazo es un 429 con retry_after_ms; espera ese tiempo y vuelve a intentarlo.
x-fastpad-workspace indica en cuál de tus espacios de trabajo actuar. No es una decisión de autorización: nombrar un espacio del que no eres miembro devuelve nada, no las notas de otra persona.
Forma de las peticiones
- Los identificadores son ULID: 26 caracteres, ordenables por fecha de creación.
- Las marcas de tiempo son ISO 8601 con desplazamiento.
- Las listas se paginan por clave; sigue
next_cursoren vez de contar páginas. - Los cuerpos están limitados a 64 KiB en las rutas públicas y a 8 MiB en las rutas autenticadas y en
/mcp. Un cuerpo de más de 64 KiB debe terminar de llegar en 60 segundos y, pasados los primeros 5 segundos, mantener una velocidad media mínima que depende de su tamaño: 64 KiB por segundo para un cuerpo máximo de 8 MiB, proporcionalmente menos para uno más pequeño. Un cuerpo que no la alcanza recibe un 408 reintentable, cuyomin_bytes_per_secindica qué mínimo se aplicó. Las importaciones grandes van firmadas al almacenamiento de objetos. - Ningún array de una petición puede tener más de 10.000 elementos, ni una petición más de 50.000 valores en total. Un 422 enumera como mucho 50
field_errors, yfield_errors_truncatedcuenta el resto. - Toda mutación acepta
idempotencyKey. Envía una.
Una clave de idempotencia repite la primera respuesta tal cual. Un cuerpo que difiera bajo la misma clave es un 409, nunca una repetición silenciosa de la respuesta equivocada.
Errores
Todo fallo es application/problem+json según RFC 9457, con un type estable, retryable, retry_after_ms cuando aplica, field_errors[] y un docs_url que resuelve.
{
"type": "https://fastpad.ai/errors/stale-frontier",
"title": "Stale frontier",
"status": 409,
"retryable": true,
"docs_url": "https://fastpad.ai/errors/stale-frontier"
}Cada problema nombra la siguiente llamada que hay que hacer. El catálogo completo está en fastpad.ai/errors.
Escribir una nota
El contenido de una nota es un documento colaborativo, así que una escritura es un lote de operaciones CRDT más el estado contra el que las calculaste.
POST /v1/notes/{id}/ops
{ "ops": "<base64 Loro update>", "peerId": "…", "expectedFrontier": "fp1:…", "idempotencyKey": "…" }Si la nota cambió desde que la leíste, recibes stale-frontier (409) nombrando ambos estados: vuelve a leer y recalcula en lugar de sobrescribir. El registro de operaciones y la proyección consultable se escriben en una sola transacción, así que una escritura es visible para una búsqueda en el instante en que se confirma.
Cada guardado se comprueba antes de que nada lo importe. ops debe ser una exportación Loro en modo update, nunca un snapshot, de 1 MiB como máximo una vez decodificada. Puede añadir como mucho 100.000 átomos de cambio que la nota no tenga ya (un átomo es un carácter insertado o borrado), o 200.000 si es la edición de un solo peer construida sobre la versión actual de la nota; las operaciones que la nota ya tiene no cuentan. Un guardado que supere cualquiera de estos límites recibe un 422 validation-failed no reintentable que nombra max_bytes, max_atoms o mode. Los mismos bytes se rechazan siempre, así que conserva el cambio, vuelve a leer la nota y envíalo otra vez en actualizaciones más pequeñas, cada una exportada desde la versión que el servidor confirmó por última vez.