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