Se ejecuta enWrit Cloud
En esta página
Cualquier workflow, una ruta REST.
Publica un workflow — o una tarea de extracción de página guardada — y Writ lo sirve en /v1/{slug}/{path}: sin prefijo /api, sin servidor tuyo que mantener, y quienes llaman tienen sus propias claves de consumidor, nunca tus credenciales.
ruta ▸ cómo se resuelve una llamada
La ruta, resuelta.
La pasarela no tiene rutas fijas propias: el slug nombra tu tenant, la ruta se compara con un endpoint que registraste, y todo lo que no se resuelve es un 404.
| Parte | Cómo se resuelve |
|---|---|
{slug} | El public_id de tu tenant (canónico) o su slug personalizado. La pasarela también responde en el subdominio {slug}.api.usewrit.app y en los dominios personalizados verificados. |
{path} | Se compara con tus endpoints registrados en (método, ruta) — un literal como /products, o un patrón como /search/{query}. |
Métodos | GET · POST · PUT · DELETE · PATCH |
Backend | Un run de workflow guardado, o un scrape_job — una tarea de extracción de página guardada. |
Sin coincidencia | 404 — tenant desconocido, o ningún endpoint registrado en ese (método, ruta). |
llamada ▸ post, leer datos
La primera llamada.
Envía las entradas por POST y lee los datos de vuelta — estos ejemplos son todo el cliente:
call.sh
curl -X POST https://api.usewrit.app/v1/acme/price-check \
-H "Authorization: Bearer $WRIT_CONSUMER_KEY" \
-H "Content-Type: application/json" \
-d '{"url": "https://example.com/product/42"}' call.py
import os, requests
res = requests.post(
"https://api.usewrit.app/v1/acme/price-check",
headers={"Authorization": f"Bearer {os.environ['WRIT_CONSUMER_KEY']}"}, # csk_...
json={"url": "https://example.com/product/42"},
timeout=120,
)
res.raise_for_status()
payload = res.json()
print(payload["run_id"], payload["data"]) call.ts
const res = await fetch("https://api.usewrit.app/v1/acme/price-check", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.WRIT_CONSUMER_KEY}`, // csk_...
"Content-Type": "application/json",
},
body: JSON.stringify({ url: "https://example.com/product/42" }),
});
if (!res.ok) throw new Error(`Writ call failed: ${res.status}`);
const { run_id, data } = await res.json();
console.log(run_id, data); call.go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
body, _ := json.Marshal(map[string]string{"url": "https://example.com/product/42"})
req, _ := http.NewRequest("POST", "https://api.usewrit.app/v1/acme/price-check", bytes.NewReader(body))
req.Header.Set("Authorization", "Bearer "+os.Getenv("WRIT_CONSUMER_KEY")) // csk_...
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var out struct {
RunID string `json:"run_id"`
Data json.RawMessage `json:"data"`
}
json.NewDecoder(res.Body).Decode(&out)
fmt.Println(out.RunID, string(out.Data))
} call.rs
use serde_json::{json, Value};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let key = std::env::var("WRIT_CONSUMER_KEY")?; // csk_...
let res: Value = reqwest::Client::new()
.post("https://api.usewrit.app/v1/acme/price-check")
.bearer_auth(key)
.json(&json!({ "url": "https://example.com/product/42" }))
.send()
.await?
.error_for_status()?
.json()
.await?;
println!("{} {}", res["run_id"], res["data"]);
Ok(())
} Quienes llaman se autentican en esta vía con una clave de consumidor csk_ que tú generas por llamante; tu clave wt_ se queda en la superficie de gestión /api y nunca tiene que llegarles. Genera, limita, suspende y rota claves en claves de consumidor.
espera ▸ síncrona por defecto
Síncrona por defecto, asíncrona a petición.
No existe un parámetro wait= en esta vía. Una llamada se ejecuta de forma síncrona hasta el timeout_seconds del endpoint (5–300, 120 por defecto) y responde 200 con el resultado en línea; pasado el presupuesto responde 504 — llevando aún la referencia del run, así que nada se pierde.
200 con el resultado en línea, hasta timeout_seconds.
Envía Prefer: respond-async (RFC 7240) o ?async=true → 202 más una referencia de run.
GET /v1/{slug}/_runs/{run_id} — responde con Retry-After: 2 mientras el run no sea terminal.
Frescura
Envía Cache-Control: max-age=N o ?max_age=N por llamada. 0 fuerza un run nuevo; si falta, decide el cache_ttl_seconds propio del endpoint (0–86400).
Los parámetros de control nunca se filtran a tu workflow: async y max_age se retiran antes de que el resto de la query se fusione con las entradas del run.
forma ▸ response_format
Uno de tres sobres.
Cada endpoint elige cómo se envuelve su carga:
| response_format | Forma |
|---|---|
raw | La salida del run, sin envolver. |
json_wrapped | El defecto — {"success":true,"data":…}. |
with_metadata | {"data":…,"metadata":{endpoint_id,latency_ms,cached,timestamp}}. |
Los errores nunca varían con el formato: siempre {"success":false,"error":…,"detail":…}.
orden ▸ los controles
El orden de los controles, exacto.
Cada llamada recorre los mismos controles, en el mismo orden. Conocer el orden te dice qué límite alcanzaste y qué cabecera leer:
- 01Tenant
{slug}desconocido →404. - 02Endpoint
Ningún endpoint registrado en ese (método, ruta) →
404. - 03Clave de consumidor
Clave de consumidor
Bearerausente o inválida →401. - 04Límite de tasa por clave
Ventana deslizante de 60 segundos — el
rate_limit_per_minutede la clave, si no elrate_limit_overridedel endpoint, si no 60/min. Al superarlo →429conX-RateLimit-*yRetry-After: 60. - 05Fair-use diario
Un techo diario a nivel de organización sobre las llamadas relevadas a los endpoints publicados — de 2.000/día en Free a 250.000/día en Enterprise. Al superarlo →
429conRetry-After: 3600. - 06Cuota mensual por clave
El
monthly_quotade la clave, contado antes del envío — al superarlo →429«Used {n}/{quota} calls this month». - 07Cuota mensual de la organización
La cuota mensual de llamadas managed-API de tu plan (
managed_api_calls_per_month). - 08Caché
Un resultado en caché más joven que la edad permitida se devuelve aquí, sin iniciar un run.
- 09Envío
El workflow — o la tarea de extracción guardada — se ejecuta en su lugar de ejecución configurado.
- 10Uso
La llamada aterriza en las analíticas de uso por clave y por endpoint.
Cuotas por plan
Las rutas publicadas tienen tope como objetos; las llamadas, por mes a nivel de organización y por día como fair-use. El tráfico con clave desconocida o en 404 nunca cuenta en tu contra:
| Plan | Endpoints publicados | Llamadas · mes | Llamadas relevadas · día |
|---|---|---|---|
| Free | 2 | 10.000 | 2.000 |
| Starter | 5 | 50.000 | 10.000 |
| Pro | 15 | 250.000 | 25.000 |
| Growth | 40 | 1.000.000 | 50.000 |
| Scale | 100 | Ilimitado | 100.000 |
| Enterprise | Ilimitado | Ilimitado | 250.000 |
faq
Preguntas, respondidas.
¿Cuál es la diferencia entre un endpoint y un tool MCP?
¿Qué clave usan quienes llaman?
¿Qué pasa cuando un run sobrevive al timeout?
¿Dónde se ejecuta el run?
go ▸ publicar
Publica tu primer endpoint.
Elige un workflow, registra una ruta y entrega a quienes llaman una URL que responde en un POST.