S’exécute surWrit Cloud
Sur cette page
N’importe quel workflow, une route REST.
Publiez un workflow — ou une tâche d’extraction de page enregistrée — et Writ le sert sur /v1/{slug}/{path} : pas de préfixe /api, aucun serveur à faire tourner, et vos appelants détiennent leurs propres clés consommateur, jamais vos identifiants.
route ▸ résolution d’un appel
La route, résolue.
La passerelle n’a aucun chemin fixe à elle : le slug nomme votre tenant, le chemin correspond à un endpoint que vous avez enregistré, et tout ce qui ne se résout pas est un 404.
| Partie | Comment elle se résout |
|---|---|
{slug} | Le public_id de votre tenant (canonique) ou son slug personnalisé. La passerelle répond aussi sur le sous-domaine {slug}.api.usewrit.app et sur les domaines personnalisés vérifiés. |
{path} | Confronté à vos endpoints enregistrés sur (méthode, chemin) — un littéral comme /products, ou un motif comme /search/{query}. |
Méthodes | GET · POST · PUT · DELETE · PATCH |
Backend | Un run de workflow enregistré, ou un scrape_job — une tâche d’extraction de page enregistrée. |
Sans correspondance | 404 — tenant inconnu, ou aucun endpoint enregistré sur ce (méthode, chemin). |
appel ▸ post, lire les données
Le premier appel.
Envoyez les entrées en POST, relisez les données — ces exemples sont tout le client :
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(())
} Les appelants s’authentifient sur cette voie avec une clé consommateur csk_ que vous émettez par appelant ; votre clé wt_ reste sur la surface de gestion /api et n’a jamais à leur parvenir. Émettez, plafonnez, suspendez et faites tourner les clés dans clés consommateur.
attente ▸ synchrone par défaut
Synchrone par défaut, asynchrone à la demande.
Il n’existe pas de paramètre wait= sur cette voie. Un appel s’exécute de façon synchrone jusqu’au timeout_seconds de l’endpoint (5–300, 120 par défaut) et répond 200 avec le résultat en ligne ; au-delà du budget, il répond 504 — en portant toujours la référence du run, rien n’est perdu.
200 avec le résultat en ligne, jusqu’à timeout_seconds.
Envoyez Prefer: respond-async (RFC 7240) ou ?async=true → 202 plus une référence de run.
GET /v1/{slug}/_runs/{run_id} — répond avec Retry-After: 2 tant que le run n’est pas terminal.
Fraîcheur
Envoyez Cache-Control: max-age=N ou ?max_age=N par appel. 0 force un run à neuf ; sans indication, c’est le cache_ttl_seconds propre à l’endpoint (0–86400) qui décide.
Les paramètres de contrôle ne fuient jamais dans votre workflow : async et max_age sont retirés avant que le reste de la chaîne de requête ne soit fusionné dans les entrées du run.
forme ▸ response_format
Une enveloppe parmi trois.
Chaque endpoint choisit l’emballage de sa charge utile :
| response_format | Forme |
|---|---|
raw | La sortie du run, sans emballage. |
json_wrapped | Le défaut — {"success":true,"data":…}. |
with_metadata | {"data":…,"metadata":{endpoint_id,latency_ms,cached,timestamp}}. |
Les erreurs ne varient jamais avec le format : toujours {"success":false,"error":…,"detail":…}.
ordre ▸ les contrôles
L’ordre des contrôles, exactement.
Chaque appel franchit les mêmes contrôles, dans le même ordre. Connaître cet ordre vous dit quelle limite vous avez atteinte et quel en-tête lire :
- 01Tenant
{slug}inconnu →404. - 02Endpoint
Aucun endpoint enregistré sur ce (méthode, chemin) →
404. - 03Clé consommateur
Clé consommateur
Bearerabsente ou invalide →401. - 04Limite de débit par clé
Fenêtre glissante de 60 secondes — le
rate_limit_per_minutede la clé, sinon lerate_limit_overridede l’endpoint, sinon 60/min. Au-delà →429avecX-RateLimit-*etRetry-After: 60. - 05Fair-use quotidien
Un plafond quotidien à l’échelle de l’organisation sur les appels relayés vers les endpoints publiés — de 2 000/jour en Free à 250 000/jour en Enterprise. Au-delà →
429avecRetry-After: 3600. - 06Quota mensuel par clé
Le
monthly_quotade la clé, décompté avant l’envoi — au-delà →429« Used {n}/{quota} calls this month ». - 07Quota mensuel de l’organisation
Le quota mensuel d’appels managed-API de votre plan (
managed_api_calls_per_month). - 08Cache
Un résultat en cache plus jeune que l’âge autorisé est renvoyé ici, sans démarrer de run.
- 09Envoi
Le workflow — ou la tâche d’extraction enregistrée — s’exécute sur son lieu d’exécution configuré.
- 10Usage
L’appel atterrit dans les analyses d’usage par clé et par endpoint.
Quotas par plan
Les routes publiées sont plafonnées en nombre ; les appels le sont par mois à l’échelle de l’organisation et par jour en fair-use. Le trafic en clé inconnue ou en 404 ne compte jamais contre vous :
| Plan | Endpoints publiés | Appels · mois | Appels relayés · jour |
|---|---|---|---|
| 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 | Illimité | 100 000 |
| Enterprise | Illimité | Illimité | 250 000 |
faq
Vos questions, nos réponses.
Quelle est la différence entre un endpoint et un tool MCP ?
Quelle clé vos appelants utilisent-ils ?
Que se passe-t-il quand un run dépasse le timeout ?
Où l’exécution a-t-elle lieu ?
go ▸ publier
Publiez votre premier endpoint.
Choisissez un workflow, enregistrez une route, et remettez à vos appelants une URL qui répond en un POST.