S’exécute surWrit CloudDesktopAuto-hébergé
Sur cette page
Référence API
Toute la surface, endpoint par endpoint.
Writ répond à deux endroits : writ-agentd, le daemon agent sur votre propre machine à http://127.0.0.1:8131, et Writ Cloud à https://api.usewrit.app. Même grammaire JSON et même en-tête Bearer des deux côtés — familles de tokens différentes, et un seul des deux est facturé.
surfaces ▸ local + cloud
Deux surfaces.
Le daemon local est le logiciel que l’app de bureau (ou l’agent auto-hébergé) fait tourner : loopback uniquement, gratuit. Writ Cloud est la surface hébergée qu’une clé wt_ déverrouille. Les SDKs découvrent le premier et peuvent porter la seconde.
| Surface | URL de base | Auth |
|---|---|---|
| Agent local (writ-agentd) | http://127.0.0.1:8131 | Bearer wlt_ · wlk_ · wlo_ |
| Writ Cloud | https://api.usewrit.app | Bearer wt_ · en-tête X-Writ-Client-Id (sans clé) |
Utilisez 127.0.0.1, pas localhost — le daemon vérifie Host et Origin contre le DNS rebinding. Un jumeau HTTPS écoute sur https://127.0.0.1:8132 avec une CA locale par installation dans ~/.writ/tls/ca.pem, et WRIT_PORT remplace le port.
Un workflow publié est une porte à part : POST /v1/{slug}/{path} sur Writ Cloud, authentifié par une consumer key csk_ que vous fabriquez pour ses appelants, documentée sur les endpoints gérés. Le serveur MCP (JSON-RPC sur /mcp) et les livraisons webhook signées ont aussi leurs propres pages : serveur MCP, webhooks.
auth ▸ cinq préfixes
Familles de tokens.
Un seul en-tête partout : Authorization: Bearer …. Ce qui change, c’est la famille du token — chaque préfixe est cantonné à sa surface. Rotation et détail des scopes : authentification.
| Token | Surface | Rôle |
|---|---|---|
wlt_ | Local | Token runtime — toute la surface. La seule famille qui peut fabriquer des clés restreintes. |
wlk_ | Local | Clé restreinte fabriquée via POST /v1/keys ; les scopes sont un CSV de read|run|admin. |
wlo_ | Local | Token OAuth 2.1 portant le scope run. |
wt_ | Cloud | Clé API pour les appels cloud facturés sur api.usewrit.app. |
X-Writ-Client-Id | Cloud | Pas un token — un en-tête d’identifiant d’appareil pour les routes sans clé et leur allocation fixe. |
Fabriquer une clé wlk_ exige le token runtime wlt_ — une clé restreinte qui fuite ne peut donc jamais s’élargir elle-même :
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"}' erreurs ▸ codes stables
Une seule forme d’erreur.
Les erreurs sont de petits objets JSON : {"error": "…", "code": "…"} — une phrase lisible et un code stable. Les codes :
| Code | HTTP | Signification |
|---|---|---|
bad_request | 400 | JSON malformé, champ manquant ou paramètre hors de sa plage autorisée. |
unauthorized | 401 | Pas de token Bearer, ou un token que le daemon ne reconnaît pas. |
captcha_required | 402 | L’opération a rencontré une étape de vérification qui demande un humain. |
forbidden | 403 | Le token est valide mais ses scopes ne couvrent pas cette opération. |
not_found | 404 | Aucune ressource à cet id ou ce chemin. |
device_capacity | 409 | L’appareil est à sa limite de capacité pour cette ressource. |
vault_locked | 423 | Le vault chiffré est verrouillé ; déverrouillez-le puis réessayez. |
too_many_requests | 429 | Trop de requêtes en peu de temps ; espacez-les puis réessayez. |
internal | 500 | Défaillance inattendue à l’intérieur du daemon. |
Quelques chemins 4xx répondent en text/plain plutôt qu’en JSON. En lisant un corps d’erreur, tolérez le non-JSON.
runs ▸ le contrat
Sémantique des exécutions.
Le contrat à comprendre avant de câbler quoi que ce soit :
- Asynchrone par défaut.
POST /v1/workflows/{id}/runrépond dès que l’exécution est dispatchée, avec son id. - Ou bloquez jusqu’au résultat. Ajoutez
?wait=true— l’appel attend le verdict.timeoutest en secondes, borné à 1–3600, 120 par défaut. - Un run en échec est un résultat, pas une erreur. Vous récupérez l’exécution avec
status: "failed"; réservez la gestion d’erreurs au transport et à l’auth. - Deux formes d’id. Le fil des runs renvoie des ids composites comme
workflow-3; chaque appel/v1/runs/{id}/*prend l’id numérique de ligne.
référence ▸ 98 opérations
La surface locale.
Chaque opération du daemon local, exactement comme la description OpenAPI l’énonce — 98 opérations en 16 groupes, toutes sous /v1 en loopback, toutes authentifiées par Bearer.
Chaque appel a cette forme — un en-tête Bearer, JSON en entrée, JSON en sortie :
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"}' Les enveloppes de liste varient à dessein. La plupart répondent {"data": [...], "count": n} ; /v1/runs ajoute "total" ; moniteurs, selectors, extractors, automations et /v1/changes/recent répondent des tableaux nus.
Agent
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/agent | État léger de l’agent |
GET | /v1/health | Sonde de santé approfondie |
Workflows
La sémantique ci-dessus s’applique à POST /v1/workflows/{id}/run. La paire session gère la session de la voie HTTP sans navigateur qu’un workflow peut détenir.
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/workflows | Lister les workflows |
POST | /v1/workflows | Créer un workflow |
GET | /v1/workflows/{id} | Lire un workflow |
PATCH | /v1/workflows/{id} | Modifier un workflow |
DELETE | /v1/workflows/{id} | Supprimer un workflow |
POST | /v1/workflows/{id}/run | Exécuter un workflow (asynchrone par défaut, ou attendre le résultat) |
POST | /v1/workflows/{id}/cancel | Annuler l’exécution vivante la plus récente d’un workflow |
GET | /v1/workflows/{id}/session | État de la session de la voie HTTP sans navigateur |
DELETE | /v1/workflows/{id}/session | Effacer la session persistée |
Exécutions
GET /v1/runs/{id}/events diffuse la progression en direct via SSE. Annuler une exécution déjà réglée répond 409 — avec l’exécution elle-même en corps.
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/runs | Lister les exécutions (fil enrichi) |
GET | /v1/runs/{id} | Lire une exécution |
GET | /v1/runs/{id}/results | Charge utile brute du résultat d’une exécution |
GET | /v1/runs/{id}/data | Données extraites d’une exécution |
GET | /v1/runs/{id}/events | Flux d’événements d’exécution en direct (SSE) |
POST | /v1/runs/{id}/cancel | Annuler une exécution vivante par son id |
Moniteurs
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/monitors | Lister les moniteurs |
POST | /v1/monitors | Créer un moniteur |
GET | /v1/monitors/capacity | Jauge de capacité de vérification de l’appareil |
GET | /v1/monitors/{id} | Lire un moniteur |
PATCH | /v1/monitors/{id} | Modifier un moniteur |
DELETE | /v1/monitors/{id} | Supprimer un moniteur |
POST | /v1/monitors/{id}/run | Lancer une vérification maintenant |
GET | /v1/monitors/{id}/changes | Historique des changements et de disponibilité d’un moniteur |
GET | /v1/changes/recent | Changements récents, tous moniteurs confondus |
Sélecteurs
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/monitors/{id}/selectors | Lister les sélecteurs d’un moniteur |
POST | /v1/monitors/{id}/selectors | Ajouter un sélecteur à un moniteur |
GET | /v1/monitors/{id}/selectors/{selector_id} | Lire un sélecteur |
PATCH | /v1/monitors/{id}/selectors/{selector_id} | Modifier un sélecteur |
DELETE | /v1/monitors/{id}/selectors/{selector_id} | Supprimer un sélecteur |
POST | /v1/monitors/{id}/selectors/{selector_id}/toggle | Basculer l’état actif d’un sélecteur |
POST | /v1/monitors/{id}/selectors/{selector_id}/test | Sonder un sélecteur contre la page réelle |
POST | /v1/monitors/{id}/selectors/{selector_id}/set-baseline | Capturer la référence du sélecteur |
POST | /v1/monitors/{id}/selectors/{selector_id}/clear-baseline | Effacer la référence enregistrée |
Extracteurs
Notez le toggle : PATCH, et non POST comme côté sélecteurs.
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/selectors/{selector_id}/extractors | Lister les extracteurs d’un sélecteur |
POST | /v1/extractors | Créer un extracteur |
GET | /v1/extractors/{extractor_id} | Lire un extracteur |
PATCH | /v1/extractors/{extractor_id} | Modifier un extracteur |
DELETE | /v1/extractors/{extractor_id} | Supprimer un extracteur |
PATCH | /v1/extractors/{extractor_id}/toggle | Basculer l’état actif d’un extracteur |
POST | /v1/extractors/{extractor_id}/test | Tester un extracteur enregistré |
Automatisations
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/automations | Lister les automatisations |
POST | /v1/automations | Créer une automatisation |
GET | /v1/automations/{id} | Lire une automatisation |
PATCH | /v1/automations/{id} | Modifier une automatisation |
DELETE | /v1/automations/{id} | Supprimer une automatisation |
POST | /v1/automations/{id}/enable | Activer / désactiver une automatisation |
POST | /v1/automations/{id}/run | Déclencher une automatisation maintenant |
Personas
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/personas | Lister les personas |
POST | /v1/personas | Créer un persona |
GET | /v1/personas/{id} | Lire un persona |
PATCH | /v1/personas/{id} | Modifier un persona |
DELETE | /v1/personas/{id} | Supprimer un persona |
GET | /v1/personas/{id}/runs | Exécutions récentes ayant agi comme ce persona |
POST | /v1/personas/validate-totp | Valider un secret TOTP |
POST | /v1/personas/{id}/test-2fa | Tester la 2FA du persona |
Secrets
Métadonnées uniquement — aucun endpoint ne renvoie jamais la valeur d’un secret.
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/secrets | Lister les secrets (métadonnées uniquement) |
POST | /v1/secrets | Créer un secret |
GET | /v1/secrets/{key} | Lire les métadonnées d’un secret |
DELETE | /v1/secrets/{key} | Supprimer un secret |
Vault
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/vault/status | État du verrou d’application |
POST | /v1/vault/lock | Verrouiller le vault maintenant |
POST | /v1/vault/unlock | Déverrouiller le vault |
Fichiers
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/files | Lister les descripteurs de fichiers |
POST | /v1/files | Importer un fichier (multipart) |
POST | /v1/files/from-data | Exporter des données de workflow vers un fichier |
GET | /v1/files/{id} | Lire un descripteur de fichier |
DELETE | /v1/files/{id} | Supprimer un fichier |
GET | /v1/files/{id}/content | Télécharger les octets du fichier |
Données
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/data | Sélecteur de workflow de l’explorateur de données |
GET | /v1/workflows/{id}/data | Table agrégée des données extraites |
DELETE | /v1/workflows/{id}/data | Supprimer des lignes de données extraites |
GET | /v1/workflows/{id}/data/runs | Index des snapshots de données |
GET | /v1/workflows/{id}/data/facets | Facettes par colonne |
GET | /v1/workflows/{id}/data/export | Exporter la table des données extraites |
Jeux de données
?format=json|csv|markdown|html — tout format non-json répond du texte rendu plutôt qu’un corps JSON.
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/datasets | Le catalogue unifié des datasets |
GET | /v1/datasets/search | Recherche plein texte globale sur tous les datasets |
GET | /v1/datasets/{id} | Métadonnées du dataset + schéma inféré |
GET | /v1/datasets/{id}/records | Parcourir les enregistrements d’un dataset |
GET | /v1/datasets/{id}/export | Télécharger tous les enregistrements d’un dataset |
GET | /v1/datasets/{id}/search | Recherche plein texte dans un seul dataset |
Crawl
Les définitions sont des crawls enregistrés et rappelables. POST /v1/crawl/definitions/{ref}/run accepte max_age — un crawl précédent assez récent est réutilisé au lieu d’être rechargé.
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/crawl | Lister les crawls |
POST | /v1/crawl | Démarrer un crawl |
GET | /v1/crawl/{id} | Lire un crawl |
POST | /v1/crawl/{id}/cancel | Demander l’annulation d’un crawl |
GET | /v1/crawl/definitions | Lister les crawls enregistrés |
POST | /v1/crawl/definitions | Enregistrer une configuration de crawl |
GET | /v1/crawl/definitions/{ref} | Lire un crawl enregistré |
PATCH | /v1/crawl/definitions/{ref} | Modifier un crawl enregistré |
DELETE | /v1/crawl/definitions/{ref} | Supprimer un crawl enregistré |
POST | /v1/crawl/definitions/{ref}/run | Exécuter un crawl enregistré (réutilisation de fraîcheur en option) |
GET | /v1/crawl/definitions/{ref}/data | Lire ce qu’un crawl enregistré a déjà collecté |
Clés
La fabrication exige le token runtime wlt_.
| Méthode | Chemin | Résumé |
|---|---|---|
GET | /v1/keys | Lister les clés API |
POST | /v1/keys | Fabriquer une clé API restreinte |
GET | /v1/keys/{id} | Lire la fiche d’une clé |
DELETE | /v1/keys/{id} | Supprimer la fiche d’une clé |
Tickets WebSocket
| Méthode | Chemin | Résumé |
|---|---|---|
POST | /v1/ws-ticket | Fabriquer un ticket WebSocket à usage unique |
cloud ▸ facturé + sans clé
La surface cloud.
Writ Cloud est la surface hébergée et facturée à https://api.usewrit.app. La spec y déclare quatre opérations REST : le Scrape d’une page avec une clé wt_, plus un palier sans clé identifié par un simple id d’appareil. Tout le reste de l’hôte cloud — endpoints publiés, MCP, webhooks — est documenté sur sa propre page.
| Méthode | Chemin | Résumé |
|---|---|---|
POST | /api/v1/website-to-api | Transformer un site en API |
GET | /api/v1/website-to-api/{id} | Interroger un build site-vers-API |
POST | /api/crawl/scrape | Scrape d’une page (facturé) |
POST | /api/crawl | Lancer un crawl de site complet |
GET | /api/crawl/{id} | Interroger un crawl |
GET | /api/targets | Lister les moniteurs |
POST | /api/targets | Créer un moniteur |
GET | /api/targets/{id} | Récupérer un moniteur |
PATCH | /api/targets/{id} | Modifier un moniteur |
DELETE | /api/targets/{id} | Supprimer un moniteur |
PATCH | /api/targets/{id}/toggle | Mettre en pause ou relancer un moniteur |
POST | /api/targets/{id}/run | Vérifier un moniteur maintenant |
GET | /api/targets/{id}/changes | Historique des changements d’un moniteur |
GET | /api/targets/changes/recent | Changements récents sur tous les moniteurs |
POST | /v1/keyless/crawl | Crawler quelques pages (sans clé) |
POST | /v1/keyless/scrape | Scrape d’une page (sans clé) |
POST | /v1/keyless/map | Cartographier les URLs d’un site (sans clé) |
GET | /v1/keyless/quota | Allocation sans clé restante |
Le sans-clé répond 429 keyless_rate_limited quand l’allocation est épuisée ; le facturé répond 402 insufficient_credits quand le pool de crédits est vide.
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" faq
Questions API, répondues.
Est-ce l’API que les SDKs appellent ?
Comment attendre la fin d’une exécution ?
Pourquoi cette liste renvoie-t-elle un tableau nu ?
Quel token va où ?
Appeler l’API locale coûte-t-il quelque chose ?
fin ▸ livrer
Branchez quelque chose dessus.
Le quickstart vous mène du compte au premier appel en quelques minutes ; les SDKs enveloppent toute cette page dans des clients typés.