Saltar al contenido
API de Origami

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.

01

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.

02

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.

03

Leer

Obtienes el reporte JSON del run: hallazgos, puntajes, metadatos o la descripción para la página de la publicación.

04

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

GET /api/v1/account library:read

El workspace detrás de la clave: plan, entitlement y cupo de publicaciones.

GET /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

GET /api/v1/tools tools:run

El catálogo de herramientas que expone la API, con sus flags de medición.

POST /api/v1/uploads library:write

Genera un PUT prefirmado para un archivo nuevo (30 minutos).

POST /api/v1/publications library:write

Registra un objeto subido como publicación.

GET /api/v1/publications library:read

Lista las publicaciones del workspace (paginación por cursor).

GET /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

GET /api/v1/publications/{id}/files library:read

El historial completo de versiones: el original inmutable más cada copia mejorada.

GET /api/v1/publications/{id}/files/current library:read

La versión de trabajo — sobre la que actúa la próxima herramienta.

GET /api/v1/files/{id}/download library:read

302 a un enlace de descarga de corta duración para cualquier versión.

GET /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.

POST /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

POST /api/v1/publications/{id}/runs tools:run

Inicia una ejecución (202). Acepta un objeto options y un header Idempotency-Key.

GET /api/v1/publications/{id}/runs library:read

Lista los runs de una publicación, filtrable por herramienta.

GET /api/v1/runs/{id} library:read

Estado, tiempos y detalle de error del run.

GET /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 MCP

Conectar 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.