Se ejecuta enWrit CloudDesktopAutoalojado
En esta página
Referencia API
Toda la superficie, endpoint por endpoint.
Writ responde en dos lugares: writ-agentd, el daemon agente en tu propia máquina en http://127.0.0.1:8131, y Writ Cloud en https://api.usewrit.app. Misma gramática JSON y mismo encabezado Bearer en ambos — familias de tokens distintas, y solo uno de los dos se factura.
superficies ▸ local + cloud
Dos superficies.
El daemon local es el software que la app de escritorio (o el agente autoalojado) mantiene en marcha: solo loopback, gratis. Writ Cloud es la superficie alojada que una clave wt_ desbloquea. Los SDKs descubren el primero y pueden llevar la segunda.
| Superficie | URL base | Auth |
|---|---|---|
| Agente local (writ-agentd) | http://127.0.0.1:8131 | Bearer wlt_ · wlk_ · wlo_ |
| Writ Cloud | https://api.usewrit.app | Bearer wt_ · encabezado X-Writ-Client-Id (sin clave) |
Usa 127.0.0.1, no localhost — el daemon comprueba Host y Origin contra DNS rebinding. Un gemelo HTTPS escucha en https://127.0.0.1:8132 con una CA por instalación en ~/.writ/tls/ca.pem, y WRIT_PORT sustituye el puerto.
Un workflow publicado es una puerta aparte: POST /v1/{slug}/{path} en Writ Cloud, autenticado con una consumer key csk_ que acuñas para quienes lo llaman, documentada en los endpoints gestionados. El servidor MCP (JSON-RPC en /mcp) y las entregas webhook firmadas también tienen sus propias páginas: servidor MCP, webhooks.
auth ▸ cinco prefijos
Familias de tokens.
Un solo encabezado en todas partes: Authorization: Bearer …. Lo que cambia es la familia del token — cada prefijo está limitado a su superficie. Rotación y detalle de scopes: autenticación.
| Token | Superficie | Rol |
|---|---|---|
wlt_ | Local | Token runtime — toda la superficie. La única familia que puede acuñar claves acotadas. |
wlk_ | Local | Clave acotada acuñada vía POST /v1/keys; los scopes son un CSV de read|run|admin. |
wlo_ | Local | Token OAuth 2.1 con el scope run. |
wt_ | Cloud | Clave API para llamadas cloud medidas en api.usewrit.app. |
X-Writ-Client-Id | Cloud | No es un token — un encabezado de id de dispositivo para las rutas sin clave y su asignación fija. |
Acuñar una clave wlk_ exige el token runtime wlt_ — así una clave acotada filtrada nunca puede ampliarse a sí misma:
const key = await client.keys.create({ name: "ci-runner", scopes: "read,run" });key = client.keys.create("ci-runner", scopes="read,run")key, err := client.Keys.Create(ctx, "ci-runner", "read,run")let key = agent.keys().create("ci-runner", Some("read,run")).await?;# Minting keys requires the full-access runtime token (wlt_)
curl -X POST http://127.0.0.1:8131/v1/keys \
-H "Authorization: Bearer $WRIT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "ci-runner", "scopes": "read,run"}'errores ▸ códigos estables
Una sola forma de error.
Los errores son objetos JSON pequeños: {"error": "…", "code": "…"} — una frase legible y un código estable. Los códigos:
| Código | HTTP | Significado |
|---|---|---|
bad_request | 400 | JSON malformado, un campo ausente o un parámetro fuera de su rango permitido. |
unauthorized | 401 | Sin token Bearer, o uno que el daemon no reconoce. |
captcha_required | 402 | La operación encontró un paso de verificación que necesita a un humano. |
forbidden | 403 | El token es válido pero sus scopes no cubren esta operación. |
not_found | 404 | No hay ningún recurso con ese id o esa ruta. |
device_capacity | 409 | El dispositivo está en su límite de capacidad para este recurso. |
vault_locked | 423 | El vault cifrado está bloqueado; desbloquéalo y reintenta. |
too_many_requests | 429 | Demasiadas solicitudes en poco tiempo; espácialas y reintenta. |
internal | 500 | Fallo inesperado dentro del daemon. |
Algunas rutas 4xx responden text/plain en lugar de JSON. Al leer cuerpos de error, tolera el no-JSON.
runs ▸ el contrato
Semántica de ejecución.
El contrato que hay que entender antes de cablear nada:
- Asíncrono por defecto.
POST /v1/workflows/{id}/runresponde en cuanto la ejecución se despacha, con su id. - O bloquea hasta el resultado. Añade
?wait=true— la llamada espera el veredicto.timeoutestá en segundos, acotado a 1–3600, 120 por defecto. - Un run fallido es un resultado, no un error. Recibes la ejecución con
status: "failed"; reserva el manejo de errores para transporte y auth. - Dos formas de id. El feed de runs devuelve ids compuestos como
workflow-3; cada llamada/v1/runs/{id}/*toma el id numérico de fila.
referencia ▸ 98 operaciones
La superficie local.
Cada operación del daemon local, exactamente como la enuncia la descripción OpenAPI — 98 operaciones en 16 grupos, todas bajo /v1 en loopback, todas autenticadas con Bearer.
Cada llamada tiene esta forma — un encabezado Bearer, JSON de entrada, JSON de salida:
monitor.sh
# Local agent daemon — loopback, wlt_/wlk_ token (use 127.0.0.1, not localhost)
curl -X POST http://127.0.0.1:8131/v1/monitors \
-H "Authorization: Bearer $WRIT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/pricing"}'Los sobres de lista varían a propósito. La mayoría responde {"data": [...], "count": n}; /v1/runs añade "total"; monitors, selectors, extractors, automations y /v1/changes/recent responden arrays desnudos.
Agente
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/agent | Estado ligero del agente |
GET | /v1/health | Sonda de salud profunda |
Workflows
La semántica de arriba aplica a POST /v1/workflows/{id}/run. El par session gestiona la sesión del carril HTTP sin navegador que un workflow puede mantener.
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/workflows | Listar workflows |
POST | /v1/workflows | Crear un workflow |
GET | /v1/workflows/{id} | Leer un workflow |
PATCH | /v1/workflows/{id} | Actualizar un workflow |
DELETE | /v1/workflows/{id} | Eliminar un workflow |
POST | /v1/workflows/{id}/run | Ejecutar un workflow (asíncrono por defecto, o esperar el resultado) |
POST | /v1/workflows/{id}/cancel | Cancelar la ejecución viva más reciente de un workflow |
GET | /v1/workflows/{id}/session | Estado de la sesión del carril HTTP sin navegador |
DELETE | /v1/workflows/{id}/session | Borrar la sesión persistida |
Ejecuciones
GET /v1/runs/{id}/events transmite el progreso en vivo por SSE. Cancelar una ejecución ya resuelta responde 409 — con la propia ejecución como cuerpo.
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/runs | Listar ejecuciones (feed enriquecido) |
GET | /v1/runs/{id} | Leer una ejecución |
GET | /v1/runs/{id}/results | Payload bruto del resultado de una ejecución |
GET | /v1/runs/{id}/data | Datos extraídos de una ejecución |
GET | /v1/runs/{id}/events | Stream de eventos de ejecución en vivo (SSE) |
POST | /v1/runs/{id}/cancel | Cancelar una ejecución viva por su id |
Monitores
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/monitors | Listar monitores |
POST | /v1/monitors | Crear un monitor |
GET | /v1/monitors/capacity | Medidor de capacidad de comprobación del dispositivo |
GET | /v1/monitors/{id} | Leer un monitor |
PATCH | /v1/monitors/{id} | Actualizar un monitor |
DELETE | /v1/monitors/{id} | Eliminar un monitor |
POST | /v1/monitors/{id}/run | Lanzar una comprobación ahora |
GET | /v1/monitors/{id}/changes | Historial de cambios y disponibilidad de un monitor |
GET | /v1/changes/recent | Cambios recientes en todos los monitores |
Selectores
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/monitors/{id}/selectors | Listar los selectores de un monitor |
POST | /v1/monitors/{id}/selectors | Añadir un selector a un monitor |
GET | /v1/monitors/{id}/selectors/{selector_id} | Leer un selector |
PATCH | /v1/monitors/{id}/selectors/{selector_id} | Actualizar un selector |
DELETE | /v1/monitors/{id}/selectors/{selector_id} | Eliminar un selector |
POST | /v1/monitors/{id}/selectors/{selector_id}/toggle | Alternar el estado activo de un selector |
POST | /v1/monitors/{id}/selectors/{selector_id}/test | Sondear un selector contra la página real |
POST | /v1/monitors/{id}/selectors/{selector_id}/set-baseline | Capturar la línea base del selector |
POST | /v1/monitors/{id}/selectors/{selector_id}/clear-baseline | Borrar la línea base guardada |
Extractores
Atención al toggle: PATCH, no POST como en los selectores.
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/selectors/{selector_id}/extractors | Listar los extractores de un selector |
POST | /v1/extractors | Crear un extractor |
GET | /v1/extractors/{extractor_id} | Leer un extractor |
PATCH | /v1/extractors/{extractor_id} | Actualizar un extractor |
DELETE | /v1/extractors/{extractor_id} | Eliminar un extractor |
PATCH | /v1/extractors/{extractor_id}/toggle | Alternar el estado activo de un extractor |
POST | /v1/extractors/{extractor_id}/test | Probar un extractor guardado |
Automatizaciones
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/automations | Listar automatizaciones |
POST | /v1/automations | Crear una automatización |
GET | /v1/automations/{id} | Leer una automatización |
PATCH | /v1/automations/{id} | Actualizar una automatización |
DELETE | /v1/automations/{id} | Eliminar una automatización |
POST | /v1/automations/{id}/enable | Activar / desactivar una automatización |
POST | /v1/automations/{id}/run | Disparar una automatización ahora |
Personas
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/personas | Listar personas |
POST | /v1/personas | Crear una persona |
GET | /v1/personas/{id} | Leer una persona |
PATCH | /v1/personas/{id} | Actualizar una persona |
DELETE | /v1/personas/{id} | Eliminar una persona |
GET | /v1/personas/{id}/runs | Ejecuciones recientes que actuaron como esta persona |
POST | /v1/personas/validate-totp | Validar una semilla TOTP |
POST | /v1/personas/{id}/test-2fa | Probar la 2FA de la persona |
Secretos
Solo metadatos — ningún endpoint devuelve jamás el valor de un secreto.
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/secrets | Listar secretos (solo metadatos) |
POST | /v1/secrets | Crear un secreto |
GET | /v1/secrets/{key} | Leer los metadatos de un secreto |
DELETE | /v1/secrets/{key} | Eliminar un secreto |
Vault
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/vault/status | Estado del bloqueo de la aplicación |
POST | /v1/vault/lock | Bloquear el vault ahora |
POST | /v1/vault/unlock | Desbloquear el vault |
Archivos
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/files | Listar identificadores de archivo |
POST | /v1/files | Subir un archivo (multipart) |
POST | /v1/files/from-data | Exportar datos de workflow a un archivo |
GET | /v1/files/{id} | Leer un identificador de archivo |
DELETE | /v1/files/{id} | Eliminar un archivo |
GET | /v1/files/{id}/content | Descargar los bytes del archivo |
Datos
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/data | Selector de workflow del explorador de datos |
GET | /v1/workflows/{id}/data | Tabla agregada de datos extraídos |
DELETE | /v1/workflows/{id}/data | Eliminar filas de datos extraídos |
GET | /v1/workflows/{id}/data/runs | Índice de instantáneas de datos |
GET | /v1/workflows/{id}/data/facets | Facetas por columna |
GET | /v1/workflows/{id}/data/export | Exportar la tabla de datos extraídos |
Conjuntos de datos
?format=json|csv|markdown|html — cualquier formato no-json responde texto renderizado en lugar de un cuerpo JSON.
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/datasets | El catálogo unificado de datasets |
GET | /v1/datasets/search | Búsqueda global de texto completo en todos los datasets |
GET | /v1/datasets/{id} | Metadatos del dataset + esquema inferido |
GET | /v1/datasets/{id}/records | Paginar los registros de un dataset |
GET | /v1/datasets/{id}/export | Descargar todos los registros de un dataset |
GET | /v1/datasets/{id}/search | Búsqueda de texto completo dentro de un dataset |
Crawl
Las definiciones son crawls guardados e invocables. POST /v1/crawl/definitions/{ref}/run acepta max_age — un crawl previo lo bastante reciente se reutiliza en lugar de recargarse.
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/crawl | Listar crawls |
POST | /v1/crawl | Iniciar un crawl |
GET | /v1/crawl/{id} | Leer un crawl |
POST | /v1/crawl/{id}/cancel | Solicitar la cancelación de un crawl |
GET | /v1/crawl/definitions | Listar crawls guardados |
POST | /v1/crawl/definitions | Guardar una configuración de crawl |
GET | /v1/crawl/definitions/{ref} | Leer un crawl guardado |
PATCH | /v1/crawl/definitions/{ref} | Actualizar un crawl guardado |
DELETE | /v1/crawl/definitions/{ref} | Eliminar un crawl guardado |
POST | /v1/crawl/definitions/{ref}/run | Ejecutar un crawl guardado (con reutilización de frescura opcional) |
GET | /v1/crawl/definitions/{ref}/data | Leer lo que un crawl guardado ya recolectó |
Claves
Acuñar exige el token runtime wlt_.
| Método | Ruta | Resumen |
|---|---|---|
GET | /v1/keys | Listar claves API |
POST | /v1/keys | Acuñar una clave API acotada |
GET | /v1/keys/{id} | Leer el registro de una clave |
DELETE | /v1/keys/{id} | Eliminar el registro de una clave |
Tickets WebSocket
| Método | Ruta | Resumen |
|---|---|---|
POST | /v1/ws-ticket | Acuñar un ticket WebSocket de un solo uso |
cloud ▸ medido + sin clave
La superficie cloud.
Writ Cloud es la superficie alojada y medida en https://api.usewrit.app. La spec declara ahí cuatro operaciones REST: el Scrape de una página con una clave wt_, más un nivel sin clave identificado solo por un id de dispositivo. Todo lo demás del host cloud — endpoints publicados, MCP, webhooks — se documenta en su propia página.
| Método | Ruta | Resumen |
|---|---|---|
POST | /api/v1/website-to-api | Convertir un sitio en API |
GET | /api/v1/website-to-api/{id} | Consultar un build sitio-a-API |
POST | /api/crawl/scrape | Scrape de una página (medido) |
POST | /api/crawl | Iniciar un rastreo de sitio completo |
GET | /api/crawl/{id} | Consultar un rastreo |
GET | /api/targets | Listar monitores |
POST | /api/targets | Crear un monitor |
GET | /api/targets/{id} | Obtener un monitor |
PATCH | /api/targets/{id} | Actualizar un monitor |
DELETE | /api/targets/{id} | Eliminar un monitor |
PATCH | /api/targets/{id}/toggle | Pausar o reanudar un monitor |
POST | /api/targets/{id}/run | Comprobar un monitor ahora |
GET | /api/targets/{id}/changes | Historial de cambios de un monitor |
GET | /api/targets/changes/recent | Cambios recientes en todos los monitores |
POST | /v1/keyless/crawl | Rastrear unas páginas (sin clave) |
POST | /v1/keyless/scrape | Scrape de una página (sin clave) |
POST | /v1/keyless/map | Mapear las URLs de un sitio (sin clave) |
GET | /v1/keyless/quota | Asignación sin clave restante |
El sin clave responde 429 keyless_rate_limited cuando la asignación se agota; el medido responde 402 insufficient_credits cuando el fondo de créditos está vacío.
cloud.ts
const cloud = new CloudApi({ apiKey: process.env.WRIT_API_KEY }); // wt_…
const page = await cloud.scrape("https://example.com");
const site = await cloud.map("https://example.com", { search: "pricing", limit: 20 });
console.log(cloud.tier); // "metered" | "keyless"cloud.py
cloud = Cloud(api_key=os.environ["WRIT_API_KEY"]) # wt_… — no daemon needed
page = cloud.scrape("https://example.com")
site = cloud.map("https://example.com", search="pricing", limit=20)
print(cloud.tier) # "metered" | "keyless"metered.sh
# Metered — wt_ API key, billed from your credit pool
curl -X POST https://api.usewrit.app/api/crawl/scrape \
-H "Authorization: Bearer $WRIT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'keyless.sh
# Keyless — no account, no key: a stable device id is the only identity.
# 429 keyless_rate_limited when the allowance is spent.
curl -X POST https://api.usewrit.app/v1/keyless/scrape \
-H "X-Writ-Client-Id: $WRIT_CLIENT_ID" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com"}'
curl https://api.usewrit.app/v1/keyless/quota -H "X-Writ-Client-Id: $WRIT_CLIENT_ID"Opciones de build sitio-a-API
Un build sube una escalera y se detiene en el primer peldaño que demuestra la API: un rastreo estático por HTTP, luego un rastreo renderizado y, por último, el barrido con navegador IA. Dos interruptores gobiernan los peldaños de rastreo. Ambos se aceptan en POST /api/v1/website-to-api y en POST /api/v1/deep-discovery, valen true por defecto y no se aplican al peldaño IA, que maneja un navegador como lo haría una persona.
| Campo | Dónde | Significado |
|---|---|---|
respect_robots | petición | Los peldaños de rastreo obedecen el robots.txt del sitio. Envía false solo para un sitio que tengas permiso de automatizar. Activado, un sitio cuyo robots.txt prohíbe tu objetivo no admite ninguna página: los dos rastreos no encuentran nada y el build pasa directamente al peldaño IA. |
ai_supervise | petición | Cuando el rastreo ha recogido los formularios, las peticiones y los listados del sitio, una única llamada IA acotada redacta la API a partir de ellos: qué funciones conservar para el objetivo, sus nombres, sus entradas y valores de ejemplo. Solo puede elegir entre lo que el rastreo encontró, nunca inventar una URL ni una entrada. false conserva el mapa mecánico y no gasta IA en los peldaños de rastreo. |
escalations | vista del build | Los peldaños que se ejecutaron antes de este y por qué cedió cada uno, del más antiguo al más reciente: [{build_id, rung, mode, reason}]. Lo lleva el peldaño más reciente, aquel al que conduce fallback_build_id, de modo que el build en el que terminas explica toda la subida. Ausente si nada escaló. |
fallback_reason | vista del build | En un peldaño reemplazado, la misma frase solo para ese peldaño, tal como se midió: robots.txt rechazó el rastreo, no se descargó ninguna página, nada coincidía con el objetivo o no volvió ninguna lista estructurada. |
La vista del build también devuelve respect_robots y ai_supervise tal como los aplicó el peldaño, así que un build terminado deja constancia de cómo se rastreó. Ambos están ausentes en el peldaño IA.
referencia ▸ siguiente
Sigue construyendo.
faq
Preguntas de API, respondidas.
¿Es esta la API que llaman los SDKs?
¿Cómo espero a que termine una ejecución?
¿Por qué esta lista devolvió un array desnudo?
¿Qué token va dónde?
¿Llamar a la API local cuesta algo?
fin ▸ enviar
Apunta algo hacia ella.
El quickstart te lleva de la cuenta a la primera llamada en minutos; los SDKs envuelven toda esta página en clientes tipados.