La línea de producción, programable
Todo lo que hace el workspace — subir, evaluar, mejorar, descargar — manejado desde tus propios sistemas con una API JSON simple.
El ciclo
La API refleja el workspace: cuatro pasos, repetidos por libro.
Subir
Declaras el archivo, haces PUT de los bytes a una URL prefirmada y registras la publicación. Los archivos nunca pasan por la API.
Correr
Inicias cualquier herramienta contra la versión de trabajo de la publicación. La respuesta es un run que consultas — las herramientas sincrónicas terminan en la primera consulta.
Leer
Obtienes el reporte JSON del run: hallazgos, puntajes, metadatos o la descripción para la página de la publicación.
Descargar
Las reparaciones y ediciones agregan nuevas versiones del archivo. Descargas la versión de trabajo con un enlace de corta duración.
URL base y autenticación
Cada request lleva una clave de API de workspace como token Bearer. La clave está atada a un workspace: todo lo que creas y lees con ella vive ahí, exactamente como si trabajaras en la app. No existe ningún parámetro de workspace en esta API — la clave es la que nombra al tenant, así que una clave solo puede alcanzar su propia biblioteca.
URL base
https://origami.publica.la/api/v1
Todas las rutas de esta página son relativas a eso. Los requests y las respuestas son JSON; manda Accept: application/json y, en un POST, Content-Type: application/json.
Una primera llamada
curl https://origami.publica.la/api/v1/publications \
-H "Authorization: Bearer ${ORIGAMI_API_KEY}" \
-H "Accept: application/json"
Obtener una clave
Una persona con rol de propietario o admin genera las claves en Configuración → Claves de API. El valor se muestra una sola vez y nunca más — solo se guarda su hash — así que llévalo directo a tu gestor de secretos. La misma pantalla revoca una clave, y la revocación toma efecto en el request siguiente.
Una clave pertenece tanto a quien la generó como al workspace. Si esa persona deja el workspace, sus claves dejan de funcionar con membership_revoked — así que genera las claves de integración desde una cuenta que vaya a durar más que la integración.
Permisos
Una clave lleva solo los permisos con los que fue creada, y cada ruta verifica exactamente uno. Un request que presenta una clave sin el permiso que su ruta necesita se rechaza con 403.
library:read
Leer el workspace, sus publicaciones, versiones de archivo, ejecuciones, reportes y espejos del reader.
library:write
Generar lugares de subida, registrar publicaciones y volver a enviar una publicación a la tienda reader.
tools:run
Listar el catálogo de herramientas e iniciar ejecuciones.
Inicio rápido
Seis requests llevan un libro de tu disco a un reporte de validación, ida y vuelta. Reemplaza el token y el nombre de archivo; el resto es copiar y pegar.
1 · Pide un lugar de subida
curl -X POST https://origami.publica.la/api/v1/uploads \
-H "Authorization: Bearer ${ORIGAMI_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"original_filename": "book.epub", "size_bytes": 4180294}'
Responde 201 con un publication_id, la key de storage, la upload_url a la que hacer PUT, los headers con los que se firmó esa URL, y su expires_at — 30 minutos después.
2 · PUT de los bytes a la URL prefirmada
curl -X PUT "UPLOAD_URL_FROM_STEP_1" \
-H "Content-Type: application/epub+zip" \
--data-binary @book.epub
Manda cada header que devolvió el paso anterior, tal cual. La subida va directo al storage: los bytes nunca pasan por la API, así que un libro de 2 GB sube a la velocidad del bucket y no a la de un request de PHP.
3 · Registra la publicación
curl -X POST https://origami.publica.la/api/v1/publications \
-H "Authorization: Bearer ${ORIGAMI_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"publication_id": "ID_FROM_STEP_1", "key": "KEY_FROM_STEP_1", "original_filename": "book.epub", "size_bytes": 4180294}'
Devuelve el mismo publication_id, key, original_filename y size_bytes. El tamaño se compara con lo que realmente llegó, así que una subida truncada se rechaza aquí y no se descubre en la primera ejecución.
4 · Corre una herramienta
curl -X POST https://origami.publica.la/api/v1/publications/PUBLICATION_ID/runs \
-H "Authorization: Bearer ${ORIGAMI_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"tool": "epub-check"}'
Responde 202 con un run. Un run siempre actúa sobre la versión de trabajo de la publicación — el archivo más nuevo que produjo una herramienta, o el original si no hay ninguno.
5 · Consulta el run y lee el reporte
curl https://origami.publica.la/api/v1/runs/RUN_ID \
-H "Authorization: Bearer ${ORIGAMI_API_KEY}"
curl https://origami.publica.la/api/v1/runs/RUN_ID/report \
-H "Authorization: Bearer ${ORIGAMI_API_KEY}"
Consulta hasta que status sea terminal. El endpoint de reporte responde 409 run_not_finished mientras el run sigue corriendo, así que consultar primero el run es el ciclo más económico.
6 · Descarga la versión de trabajo
curl https://origami.publica.la/api/v1/publications/PUBLICATION_ID/files/current \
-H "Authorization: Bearer ${ORIGAMI_API_KEY}"
curl -L -o book-improved.epub \
https://origami.publica.la/api/v1/files/FILE_ID/download \
-H "Authorization: Bearer ${ORIGAMI_API_KEY}"
El endpoint de descarga responde un 302 a un GET prefirmado válido por 10 minutos. Sigue los redirects (curl -L) y no guardes en caché el destino.
Endpoints
Todos los endpoints viven bajo /api/v1, hablan JSON y responden errores como {error, reason, detail} con un reason estable para máquinas. La columna de permiso es el que verifica cada ruta.
Workspace
/api/v1/account
library:read
El workspace detrás de la clave: plan, entitlement y cupo de publicaciones.
/api/v1/account/usage
library:read
El uso de hoy de herramientas medidas contra el límite diario, más lo que queda del cupo de publicaciones.
Request y respuesta
Request
curl "https://origami.publica.la/api/v1/account" \
-H "Authorization: Bearer ${ORIGAMI_API_KEY}"
Respuesta
{
"data": {
"id": "01JQ8ZW4M3S9F0AC2KQ7X6HV1B",
"name": "Ediciones Ejemplo",
"type": "standalone",
"plan": { "slug": "pro", "name": "Pro" },
"subscribed": true,
"on_trial": false,
"entitled": true,
"unlimited": false,
"publications": {
"used": 2,
"limit": 5,
"remaining": 3,
"resets_at": "2026-10-08T00:00:00+00:00",
"granted_remaining": 0,
"promotional_remaining": 0
}
}
}
Publicaciones y subidas
/api/v1/tools
tools:run
El catálogo de herramientas que expone la API, con sus flags de medición.
/api/v1/uploads
library:write
Genera un PUT prefirmado para un archivo nuevo (30 minutos).
/api/v1/publications
library:write
Registra un objeto subido como publicación.
/api/v1/publications
library:read
Lista las publicaciones del workspace (paginación por cursor).
/api/v1/publications/{id}
library:read
Una publicación: tipo, estado, título, archivo de trabajo.
Request y respuesta
Request
curl -X POST "https://origami.publica.la/api/v1/uploads" \
-H "Authorization: Bearer ${ORIGAMI_API_KEY}" \
-H "Content-Type: application/json" \
-d '{"original_filename": "book.epub", "size_bytes": 4180294}'
Respuesta
{
"data": {
"publication_id": "01JQ90R2VTA6E4M8YN5D3PKC7F",
"key": "accounts/01JQ8ZW4.../uploads/01JQ90R2....epub",
"upload_url": "https://storage.example/...&X-Amz-Signature=...",
"headers": { "Content-Type": "application/epub+zip" },
"expires_at": "2026-09-04T13:21:00+00:00"
}
}
Versiones de archivo
/api/v1/publications/{id}/files
library:read
El historial completo de versiones: el original inmutable más cada copia mejorada.
/api/v1/publications/{id}/files/current
library:read
La versión de trabajo — sobre la que actúa la próxima herramienta.
/api/v1/files/{id}/download
library:read
302 a un enlace de descarga de corta duración para cualquier versión.
/api/v1/publications/{id}/reader
library:read
Los espejos de la publicación en la tienda reader de publica.la, y si la integración está configurada.
/api/v1/publications/{id}/reader/sync
library:write
Vuelve a enviar un espejo al reader de publica.la (202) — rescata una primera publicación fallida o reintenta una conversión atascada.
Request y respuesta
Request
curl "https://origami.publica.la/api/v1/publications/PUBLICATION_ID/files/current" \
-H "Authorization: Bearer ${ORIGAMI_API_KEY}"
Respuesta
{
"data": {
"id": "01JQ93B8KDW1H7RZ4T0NQXME62",
"publication_id": "01JQ90R2VTA6E4M8YN5D3PKC7F",
"role": "derived",
"derived_kind": "epub-repair",
"original_filename": "book.epub",
"mime_type": "application/epub+zip",
"size_bytes": 4194512,
"reverted": false,
"current": true,
"created_at": "2026-09-04T12:58:41+00:00"
}
}
Ejecuciones de herramientas
/api/v1/publications/{id}/runs
tools:run
Inicia una ejecución (202). Acepta un objeto options y un header Idempotency-Key.
/api/v1/publications/{id}/runs
library:read
Lista los runs de una publicación, filtrable por herramienta.
/api/v1/runs/{id}
library:read
Estado, tiempos y detalle de error del run.
/api/v1/runs/{id}/report
library:read
El reporte JSON del run terminado.
Request y respuesta
Request
curl -X POST "https://origami.publica.la/api/v1/publications/PUBLICATION_ID/runs" \
-H "Authorization: Bearer ${ORIGAMI_API_KEY}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 5f3a-nightly-sweep-0042" \
-d '{"tool": "epub-accessibility"}'
Respuesta
{
"data": {
"id": "01JQ95T1YB0PJ6VF8C3WKR2HQD",
"publication_id": "01JQ90R2VTA6E4M8YN5D3PKC7F",
"tool": "epub-accessibility",
"status": "queued",
"error_reason": null,
"error_detail": null,
"started_at": null,
"finished_at": null,
"duration_ms": null,
"created_at": "2026-09-04T13:02:10+00:00"
}
}
Los cuatro recursos
Cuatro formas cubren toda la API, y cada endpoint que devuelve una la devuelve igual.
Workspace
En qué workspace cayó la clave, si puede hacer trabajo pago (entitled — más amplio que subscribed, porque una tienda de publica.la lleva un plan sin suscripción de Stripe detrás) y el cupo de publicaciones. remaining suma el cupo del ciclo del plan, los lugares que otorgó el equipo y lo que cubra una promoción — es la única cifra que responde «¿puedo agregar otro libro?». Cuando no se puede alcanzar a Stripe, plan y publications vuelven en null y no en cero: desconocido, no agotado.
Publicación
Un libro, un audiolibro o un documento fuente del workspace. current_file_id apunta a la versión de trabajo, que es la que lee la próxima ejecución, y current_file_bytes es lo que esa versión pesa hoy — storage_bytes sigue siendo el tamaño del original tal como se subió, así que un libro optimizado informa dos números distintos.
Versión de archivo
Una versión de una publicación. El original es inmutable y nunca se reemplaza; cada herramienta que escribe agrega una versión nueva, así que el historial está completo. current marca la de trabajo y reverted marca una versión revertida en la app.
Ejecución
Una ejecución de una herramienta sobre una publicación. status llega a succeeded, failed o rejected y ya no cambia; error_reason y error_detail dicen por qué en los otros dos casos.
Catálogo de herramientas
Los slugs que acepta POST /publications/{id}/runs, leídos en vivo de este despliegue — GET /api/v1/tools devuelve la misma lista en JSON. Dos flags importan cuando encadenas llamadas.
| Slug | Herramienta | Categoría | Flags |
|---|---|---|---|
epub-metadata |
EPUB Metadata Extractor | metadata | medida |
epub-description |
EPUB Description Writer | description | medida |
epub-metadata-write |
EPUB Metadata Editor | metadata | escribe la versión de trabajo |
epub-toc |
EPUB Table of Contents | toc | medida |
epub-toc-generate |
EPUB Table of Contents Generator | toc | medida |
epub-toc-write |
EPUB Table of Contents Editor | toc | escribe la versión de trabajo |
epub-check |
EPUB Checker | validation | |
epub-accessibility |
EPUB Accessibility Checker | accessibility | |
epub-repair |
EPUB Repair | repair | medida escribe la versión de trabajo |
epub-qualebook |
Qualebook Quality Checklist | quality | |
epub-qualebook-review |
Qualebook AI Review | quality | medida |
cover-replace |
Cover Change | cover | escribe la versión de trabajo |
onix-export |
ONIX File Creator | metadata | |
social-copy |
Social Campaign Writer | marketing | medida |
social-image |
Social Image Generator | marketing | medida |
social-carousel |
Social Carousel Generator | marketing | medida |
social-video |
Social Video Generator | marketing | medida |
pdf-check |
PDF Diagnosis | validation | |
pdf-accessibility |
PDF Accessibility Checker | accessibility | |
pdf-accessibility-fix |
PDF Accessibility Fix | accessibility | escribe la versión de trabajo |
pdf-autotag |
PDF Auto-Tag | accessibility | medida escribe la versión de trabajo |
pdf-alt-text |
PDF Alt Text | accessibility | medida escribe la versión de trabajo |
pdf-repair |
PDF Repair | repair | medida escribe la versión de trabajo |
asset-optimize |
Asset Optimizer | repair | medida escribe la versión de trabajo |
pdf-epub-analyze |
PDF to EPUB Analyzer | conversion | |
pdf-epub-convert |
PDF to EPUB Converter | conversion | medida |
document-epub-convert |
Document to EPUB Converter | conversion | medida |
Un slug que este despliegue no expone responde 404 unknown_tool; uno que existe pero todavía no se liberó responde 409 tool_unavailable. Las opciones son propias de cada herramienta y van en un objeto options — una opción que una herramienta no reconoce se ignora en vez de rechazarse, así que revisa el reporte para confirmar qué corrió de verdad. La excepción es una opción que cambiaría lo que la corrida tiene permitido hacer: una opción que haría que la corrida dependa de una persona, enviada a una corrida que se aplica sola, se rechaza con 422 invalid_options en vez de ampliarla en silencio.
Convenciones
Asincrónico por defecto
Los runs responden 202 con un recurso que consultas. Un run es terminal en succeeded, failed o rejected. Todavía no hay webhooks, así que consulta — con uno o dos segundos entre intentos alcanza; nada aquí termina más rápido de lo que la cola lo levanta.
Idempotencia
Manda un header Idempotency-Key en POST /runs; un reintento devuelve el run original con 200 en vez de gastar dos veces, y un reintento que llega mientras el primero se está registrando responde 409 idempotency_in_flight.
Paginación
Las listas responden {data, next_cursor, has_more} con 50 filas por página, de más nueva a más vieja. Pasa next_cursor como ?cursor= hasta que has_more sea false. El cursor es un id y no un offset, así que un libro agregado mientras paginas no puede correr la ventana.
Límites de tasa
Los requests se limitan a 60 por minuto por clave — por clave, así que dos integraciones en un workspace no comparten el mismo cupo. Pasado el techo hay un 429; espera y reintenta.
Dos techos de gasto
Las subidas consumen el cupo de publicaciones del plan (422 publication_quota_exceeded) y las herramientas medidas consumen el cupo diario de uso justo del workspace (429 daily_limit_reached con Retry-After). GET /account y GET /account/usage informan los dos antes de gastar cualquiera.
Los archivos van directo
Las subidas son PUTs prefirmados y las descargas GETs prefirmados contra el storage — la API nunca hace de proxy de los bytes, así que las transferencias son rápidas a cualquier tamaño. Los enlaces de subida viven 30 minutos; los de descarga, 10.
Descripciones
La herramienta de descripciones devuelve el texto para la página de la publicación: un solo estilo (back_cover) con una versión larga para el cuerpo y una corta para tarjetas y metadatos. Los estilos de catálogo y de librería que también escribe quedan dentro de la app.
Fechas e ids
Cada timestamp es ISO 8601 con offset. Cada id es un ULID — ordenable, así que ordenar por id es ordenar por fecha de creación.
Errores
Cada falla responde el mismo envelope. reason es la cadena estable para máquinas sobre la que ramificar — el status HTTP dice la clase de problema, el reason dice cuál.
El envelope
{
"error": "Publication not found.",
"reason": "publication_not_found",
"detail": null
}
Un cuerpo de request mal formado devuelve la respuesta de validación propia de Laravel ({message, errors}) y no este envelope — el envelope es para fallas que la API decidió, la forma de validación para un cuerpo que no pudo leer.
| Status | Reason | Qué significa |
|---|---|---|
| 401 | invalid_token |
Sin clave, con una clave desconocida, o con una que fue revocada. |
| 403 | token_without_workspace |
La clave no está atada a un workspace — una clave de operador en una ruta de cliente. |
| 403 | membership_revoked |
Quien generó la clave ya no pertenece a este workspace. Genera una clave nueva desde un miembro actual. |
| 404 | publication_not_found |
No hay publicación con ese id en este workspace. Los ids nunca se comparten entre workspaces. |
| 404 | file_not_found |
No existe esa versión de archivo, o la publicación todavía no tiene archivo. |
| 404 | run_not_found |
No hay ejecución con ese id en este workspace. |
| 404 | unknown_tool |
Ese slug no es una herramienta que exponga esta API. GET /tools es la lista autoritativa. |
| 409 | tool_unavailable |
La herramienta existe pero todavía no está liberada. |
| 409 | working_file_busy |
Otra ejecución que escribe la versión de trabajo tiene tomada esta publicación. Se libera cuando esa ejecución termina — ningún Retry-After puede decir cuándo, así que consulta el run. |
| 409 | run_not_finished |
Se pidió el reporte antes de que la ejecución llegara a un estado terminal. |
| 409 | run_not_succeeded |
La ejecución es terminal pero no tuvo éxito, así que no hay reporte. El recurso del run dice por qué. |
| 409 | approval_required |
El resultado de esta ejecución necesita que una persona compare ejemplos de antes y después, algo que solo la app de Origami puede mostrar. Ni esperar ni suscribirse lo resuelve. |
| 409 | idempotency_in_flight |
Un request con la misma Idempotency-Key todavía se está registrando. Reintenta en un momento. |
| 409 | reader_not_configured |
Este despliegue no tiene integración con el reader de publica.la. |
| 409 | reader_sync_in_progress |
Ya hay en curso un envío de la misma variante a la tienda reader. |
| 409 | downloads_unavailable |
El backend de storage no puede generar enlaces prefirmados. Solo se ve en el storage local de desarrollo. |
| 422 | publication_quota_exceeded |
El workspace gastó su cupo de publicaciones del ciclo. El detalle dice cuándo se renueva. |
| 422 | unsupported_format |
No es un formato de libro, documento fuente o audiolibro que Origami acepte. |
| 422 | invalid_options |
El objeto options no es válido para esa herramienta. |
| 402 | plan_required |
El workspace no tiene plan activo, así que no puede iniciar ninguna ejecución. Los endpoints de lectura siguen funcionando — por eso no es un 403. |
| 429 | daily_limit_reached |
El workspace alcanzó su límite diario de uso justo en ejecuciones medidas. Lleva Retry-After. |
También por MCP
Origami habla MCP (Model Context Protocol) en https://origami.publica.la/mcp por Streamable HTTP, así un agente puede explorar tu workspace y correr herramientas igual que esta API.
Conéctate con una clave de API de workspace como token Bearer, o por OAuth desde los clientes que lo soporten. Cada herramienta refleja un endpoint de arriba — mismo servicio, mismo workspace, mismas cuotas — así un agente y un cliente HTTP ven un solo producto.
Qué puedes hacer por MCPConectar desde Claude Code
claude mcp add --transport http origami https://origami.publica.la/mcp \
--header "Authorization: Bearer ${ORIGAMI_API_KEY}"
¿Construyendo una integración?
Cuéntanos qué estás automatizando — ingesta masiva, un hook desde tu DAM, un pipeline de preflight — y te ayudamos a conectarlo.