Intégration directe de l'API
Appelez l'API en HTTP brut — sans SDK, n'importe quel langage. Les formes exactes des requêtes/réponses anonymes et authentifiées, avec des exemples curl.
Sans SDK, n’importe quel langage — voici le contrat HTTP exact pour appeler l’API directement. Si vous utilisez JavaScript, le SDK enveloppe tout cela (soumission, polling, gestion des erreurs) pour vous — voir Intégration directe d’une page web simple ou Choisissez votre parcours d’intégration si vous n’avez pas encore choisi de guide.
Chaque exemple ci-dessous affiche l’URL de base réelle de l’API pour cet environnement — copiez-la telle quelle, sans segment de chemin supplémentaire ajouté après.
Deux endpoints différents, pas une seule URL avec deux modes d’authentification
Ce sont deux endpoints HTTP distincts. Ils retournent aussi des formes de réponse différentes pour le même outil — sachez lequel vous construisez.
Anonyme — POST /tools/:slug/run + GET /tools/runs/:ticketId
Aucun identifiant requis. Utilise le slug de l’outil. Les champs de réponse sont normalisés
pour chaque outil vers result.text (plus des métadonnées de tokens/latence), afin que le Tool
Portal public puisse afficher n’importe quel outil avec un seul rendu générique. Limité en débit
par IP ; voir Erreurs plus bas pour le tableau d’erreurs du chemin anonyme (en
particulier, ce chemin nécessite un véritable navigateur — voir la note sur 403).
curl -X POST "https://api.quravin.com/tools/translate/run" \
-H "Content-Type: application/json" \
-d '{
"inputs": { "text": "Hello", "target_language": "de" }
}'
Réponse (202) :
{ "ticket_id": "01J...ULID", "status": "QUEUED" }
Polling :
curl "https://api.quravin.com/tools/runs/01J...ULID"
Réponse une fois terminé :
{
"ticket_id": "01J...ULID",
"status": "DONE",
"mode": "pipeline",
"pipeline_id": "translate-string",
"result": { "text": "Hallo", "model": "gpt-4o-mini", "latency_ms": 812 },
"created_at": "2026-07-10T12:00:00.000Z",
"updated_at": "2026-07-10T12:00:02.000Z"
}
Notez result.text, pas result.translation — le chemin anonyme normalise toujours vers text.
Authentifié — POST /tickets + GET /tickets/:id
Nécessite x-api-key: <key> ou Authorization: Bearer <jwt> (voir Choisissez votre mode
d’authentification pour savoir comment obtenir l’un ou
l’autre). Utilise le pipeline_id, et retourne la forme de sortie brute et non normalisée de
chaque pipeline — pour translate-string, c’est translation, pas text. Voir Référence des
outils pour le pipeline_id de chaque outil et ses vrais champs de
sortie.
Avec une clé API statique :
curl -X POST "https://api.quravin.com/tickets" \
-H "Content-Type: application/json" \
-H "x-api-key: <your-api-key>" \
-d '{
"mode": "pipeline",
"pipeline_id": "translate-string",
"inputs": { "text": "Hello", "target_language": "de" }
}'
Avec un session JWT (créé via POST /auth/token — voir Choisissez votre mode
d’authentification) — corps identique, seul l’en-tête change :
curl -X POST "https://api.quravin.com/tickets" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <your-jwt>" \
-d '{
"mode": "pipeline",
"pipeline_id": "translate-string",
"inputs": { "text": "Hello", "target_language": "de" }
}'
Les deux retournent la même réponse (202) :
{ "ticket_id": "01J...ULID", "status": "QUEUED" }
Polling — avec une clé API :
curl "https://api.quravin.com/tickets/01J...ULID" \
-H "x-api-key: <your-api-key>"
Polling — avec un session JWT :
curl "https://api.quravin.com/tickets/01J...ULID" \
-H "Authorization: Bearer <your-jwt>"
Réponse une fois terminé :
{
"ticket_id": "01J...ULID",
"status": "DONE",
"mode": "pipeline",
"pipeline_id": "translate-string",
"pipeline_version": "v3",
"result": { "translation": "Hallo", "applied_terms": [] },
"created_at": "2026-07-10T12:00:00.000Z",
"updated_at": "2026-07-10T12:00:02.000Z"
}
En cas d’échec :
{
"ticket_id": "01J...ULID",
"status": "FAILED",
"mode": "pipeline",
"pipeline_id": "translate-string",
"error": "Ticket failed: <reason>",
"created_at": "2026-07-10T12:00:00.000Z",
"updated_at": "2026-07-10T12:00:02.000Z"
}
Annuler un ticket — DELETE /tickets/:id
Demande l’annulation (en écrivant un cancel.flag). Vous ne pouvez annuler qu’un ticket créé avec
vos propres identifiants — une clé API avec portée opérateur peut annuler n’importe quel ticket, un
appelant JWT/webtoken uniquement le sien (sinon 403).
curl -X DELETE "https://api.quravin.com/tickets/01J...ULID" \
-H "x-api-key: <your-api-key>"
# or with a session JWT:
curl -X DELETE "https://api.quravin.com/tickets/01J...ULID" \
-H "Authorization: Bearer <your-jwt>"
Réponse :
{ "ticket_id": "01J...ULID", "status": "CANCELED" }
Un GET /tickets/:id sur un ticket annulé, une fois interrogé, retourne la même forme que
DONE/FAILED ci-dessus, avec simplement status: "CANCELED".
Lister vos tickets — GET /tickets
Retourne les tickets rattachés à votre propre identité — un appelant JWT/webtoken récupère
toujours ses propres tickets ; seule une clé API à portée opérateur peut passer ?user= / ?app=
pour consulter les tickets d’un utilisateur/application arbitraire.
curl "https://api.quravin.com/tickets?limit=20" \
-H "Authorization: Bearer <your-jwt>"
Paramètres de requête :
| Paramètre | Défaut | Notes |
|---|---|---|
limit | 50 | Maximum 1000. |
hydrate | true | false retourne uniquement [{ ticket_id }] — économique, évite de récupérer l’état complet de chaque ticket. |
user | id de l’appelant | Seule une clé API à portée opérateur peut passer l’id d’un autre utilisateur. |
app | application de l’appelant | Seule une clé API à portée opérateur peut passer l’id d’une autre application. |
Réponse :
{ "tickets": [ { "ticket_id": "01J...ULID", "status": "DONE", "...": "..." } ] }
Le point important si vous passez du widget public « essayer » à une véritable intégration :
le nom du champ de sortie change, passant de result.text (anonyme) au champ propre au
pipeline (authentifié) — pour translate, c’est result.translation.
Erreurs
Les erreurs que vous pouvez obtenir en retour dépendent de la façon dont vous appelez — les appels anonymes et authentifiés sont validés différemment. Ne supposez pas que le comportement d’erreur d’un chemin s’applique à l’autre.
Appel anonyme (sans clé API)
Tous les outils publiés, avec limitation de débit par IP.
| Statut | Code d’erreur | Signification |
|---|---|---|
| 404 | — | Slug d’outil inconnu. |
| 403 | — | Échec de la vérification anti-bot. Ce chemin nécessite un véritable contexte navigateur capable d’exécuter un challenge Cloudflare Turnstile — il retourne toujours 403 depuis un serveur, curl, ou un script headless sans token Turnstile. Utilisez un chemin authentifié (clé API ou session JWT) pour les appels serveur à serveur. |
| 429 | guest_quota_exceeded | Vous avez atteint la limite quotidienne de requêtes anonymes, par IP ou globale. |
| 429 | guest_cost_exceeded | Vous avez atteint la limite quotidienne de coût anonyme, par IP ou globale (les outils coûteux épuisent ce budget plus vite). |
| 202 | — | Succès — { ticket_id, status: "QUEUED" }. Effectuez le polling du résultat comme pour tout autre appel. |
Important : sur ce chemin, vos inputs ne sont pas vérifiées par rapport au schéma de chaque
outil avant que l’appel soit accepté — un champ obligatoire manquant ou une valeur invalide ne vous
vaut pas une erreur 400. C’est l’exécution elle-même qui échouera à la place, ce que vous verrez en
faisant le polling du résultat. Une fois que vous avez une clé API, passez au chemin authentifié
ci-dessous, qui valide en amont.
Appel avec une clé API ou un token de session
L’un quelconque des outils auxquels vous avez accès.
| Statut | Code d’erreur | Signification |
|---|---|---|
| 401 | — | Identifiants manquants, invalides, ou expirés. |
| 403 | app_disabled | Votre application a été désactivée. |
| 403 | org_suspended | Votre organisation a été suspendue. |
| 403 | org_pending_deletion | Votre organisation est programmée pour suppression. |
| 403 | — | Cet outil ne fait pas partie de la liste des pipelines autorisés de vos identifiants. |
| 403 | origin_not_allowed | Votre Application a un allowed_origins configuré et l’en-tête Origin de la requête ne correspond exactement à aucun d’eux. S’applique uniquement si vous (ou l’administrateur de votre organisation) avez choisi cette option dans la Console — les applications qui ne la configurent jamais ne sont pas concernées. |
| 400 | — | Vos inputs ne correspondent pas aux exigences de l’outil (champ obligatoire manquant, valeur invalide, ou entrée totale trop volumineuse). Ce chemin valide bien en amont, contrairement au chemin anonyme. |
| 429 | rate_limited | Vous envoyez des requêtes plus vite que votre limite par minute — une fenêtre fixe d’une minute par organisation, pas une fenêtre glissante. Comme elle est fixe plutôt que glissante, une rafale peut brièvement dépasser le plafond nominal jusqu’à environ 2x juste à la frontière d’une minute (par exemple une rafale à 12:00:59 suivie d’une autre à 12:01:00) ; patientez et réessayez plutôt que de supposer un plafond exact. |
| 429 | quota_exceeded | Vous avez atteint votre quota mensuel de requêtes. |
| 429 | daily_cost_exceeded | Vous avez atteint un plafond de coût quotidien — une limite de vélocité, distincte du fait de manquer de solde ci-dessous. |
| 402 | insufficient_credits | Le solde de crédits de votre organisation est trop faible pour couvrir cet appel. Rechargez et réessayez. Remarque : une x-api-key nue n’a aucun portefeuille d’organisation, donc un appel facturable en facturation par portefeuille retourne cette erreur même avec des fonds disponibles — passez à un session JWT (voir Choisissez votre mode d’authentification). |
| 202 | — | Succès — { ticket_id, status: "QUEUED" }. Effectuez le polling du résultat. |
Pour en savoir plus sur le fonctionnement des identifiants et des décisions d’authentification 401/403/429, voir Choisissez votre mode d’authentification. Pour les entrées, valeurs valides et forme de sortie de chaque outil, voir Référence des outils.