Accueil docs/🔌 API & intégration native

API — intégration native

Tout ce que fait l'interface web est une API HTTP + SSE ordinaire. Tout ce que vous pouvez faire en cliquant, vous pouvez le faire depuis votre propre code : lancer un agent, streamer sa réponse, interroger une base de connaissances, créer des agents, planifier des tâches, récupérer les résultats.

URL de basehttps://ai.syctra.com/api/v1
TransportHTTPS uniquement (HTTP redirigé). TLS 1.2+
EncodageJSON, UTF-8. Content-Type: application/json sur toute écriture
StreamingServer-Sent Events (text/event-stream), reprenable
AuthentificationAuthorization: Bearer <access_token>
VersionnementLe préfixe /v1 est stable. Des champs peuvent être ajoutés ; aucun n'est supprimé sans nouveau préfixe

1. Authentification

Deux identifiants existent. Utilisez le bon :

Clé APIE-mail + mot de passe
PourServeurs, scripts, intégrations, CIApplications interactives où un humain se connecte
ObtentionParamètres → 🔑 Clés APINouvelle cléPOST /auth/register puis /auth/login
Formatsk_live_…Paire JWT (access + refresh)
Durée de vieJusqu'à révocationAccess 15 min · refresh 30 jours

N'envoyez jamais une clé API dans un navigateur, une application mobile ou un dépôt public. Elle porte tous les droits du compte qui l'a créée. Tout ce qui tourne sur un appareil client doit passer par VOTRE backend, qui détient la clé.

1.1 Clé API → jeton d'accès

Une clé API n'est pas un jeton bearer : vous l'échangez contre un jeton d'accès de courte durée, puis vous appelez n'importe quel endpoint avec ce jeton.

export SYCTRA_API_KEY="sk_live_…"

TOKEN=$(curl -sS -X POST https://ai.syctra.com/api/v1/auth/token \
  -H "X-API-Key: $SYCTRA_API_KEY" | jq -r .access_token)

POST /auth/token est le seul endpoint qui accepte la clé elle-même. Il la lit de trois façons — X-API-Key: sk_live_…, Authorization: Bearer sk_live_…, ou {"api_key": "sk_live_…"} dans le corps — et répond :

{ "access_token": "eyJhbGciOiJSUzI1NiIs…", "token_type": "Bearer", "expires_in": 900 }

Mettez le jeton en cache pendant expires_in (900 s) et ré-échangez à l'expiration. N'échangez pas à chaque requête : l'endpoint est limité à 60 échanges par minute et par IP.

1.2 Gérer les clés

MéthodeCheminRôle
POST/api-keysCréer une clé. {"name": "serveur-prod"}. Le secret n'apparaît que dans cette réponse
GET/api-keysLister les clés actives — id, nom, préfixe, created_at, last_used_at. Jamais le secret
DELETE/api-keys/{key_id}Révoquer. Bloque immédiatement tout nouvel échange

Jusqu'à 20 clés actives par utilisateur. Seul sha256(clé) est stocké : une clé perdue est irrécupérable — révoquez-la et créez-en une autre. La révocation stoppe aussitôt les nouveaux échanges ; un jeton déjà émis depuis cette clé reste valide jusqu'à son expiration (≤ 15 min), exactement comme une déconnexion de navigateur.

1.3 E-mail + mot de passe (applications interactives)

curl -X POST https://ai.syctra.com/api/v1/auth/register \
  -H 'content-type: application/json' \
  -d '{"email":"dev@acme.com","password":"…"}'          # → code à 6 chiffres par e-mail

curl -X POST https://ai.syctra.com/api/v1/auth/verify \
  -H 'content-type: application/json' \
  -d '{"email":"dev@acme.com","code":"123456"}'

curl -X POST https://ai.syctra.com/api/v1/auth/login \
  -H 'content-type: application/json' \
  -d '{"email":"dev@acme.com","password":"…"}'          # → { access_token, refresh_token }

Renouvelez avec POST /auth/refresh {"refresh_token": "…"}. La connexion Google et OIDC/SAML est disponible via /auth/google/start et /auth/oidc/start. La connexion est limitée par compte après échecs répétés ; les inscriptions sont plafonnées par IP.

2. Démarrage — interroger un agent, streamer la réponse

Le schéma est toujours le même : démarrer un run, puis streamer ses événements. Un run est durable — si votre processus meurt, reconnectez-vous et rejouez depuis votre position.

# 1 — démarrer
RUN=$(curl -sS -X POST https://ai.syctra.com/api/v1/chat \
  -H "Authorization: Bearer $TOKEN" -H 'content-type: application/json' \
  -d '{"message":"Résume la tendance du CA 2024 et fais-en un graphique","app_name":"data_copilot"}')

RUN_ID=$(echo "$RUN" | jq -r .run_id)

# 2 — streamer (reprenable)
curl -N https://ai.syctra.com/api/v1/runs/$RUN_ID/events \
  -H "Authorization: Bearer $TOKEN"

Python

import json, os, requests

BASE = "https://ai.syctra.com/api/v1"
token = requests.post(f"{BASE}/auth/token",
                      headers={"X-API-Key": os.environ["SYCTRA_API_KEY"]},
                      timeout=30).json()["access_token"]
H = {"Authorization": f"Bearer {token}"}

run = requests.post(f"{BASE}/chat", headers=H, timeout=30, json={
    "message": "Rédige un NDA mutuel pour une SARL algérienne, puis exporte-le en DOCX",
    "app_name": "legal_drafting_dz",
}).json()

reponse, cursor = [], 0
with requests.get(f"{BASE}/runs/{run['run_id']}/events", headers=H, stream=True) as r:
    event = None
    for line in r.iter_lines(decode_unicode=True):
        if not line:
            continue
        if line.startswith("id:"):
            cursor = int(line[3:].strip())          # à conserver : c'est votre point de reprise
        elif line.startswith("event:"):
            event = line[6:].strip()
        elif line.startswith("data:"):
            payload = json.loads(line[5:].strip())
            if event == "token":
                reponse.append(payload.get("text", ""))
            elif event == "artifact":
                print("fichier :", payload.get("artifact_id"), payload.get("filename"))
            elif event in ("done", "error"):
                break
print("".join(reponse))

Node

const BASE = 'https://ai.syctra.com/api/v1';

const { access_token } = await (await fetch(`${BASE}/auth/token`, {
  method: 'POST', headers: { 'X-API-Key': process.env.SYCTRA_API_KEY },
})).json();

const { run_id } = await (await fetch(`${BASE}/chat`, {
  method: 'POST',
  headers: { authorization: `Bearer ${access_token}`, 'content-type': 'application/json' },
  body: JSON.stringify({ message: 'Liste les risques de ce contrat', app_name: 'contract_review_dz' }),
})).json();

const res = await fetch(`${BASE}/runs/${run_id}/events`, {
  headers: { authorization: `Bearer ${access_token}` },
});
for await (const chunk of res.body) process.stdout.write(Buffer.from(chunk).toString());

3. Conventions

Erreurs. Codes HTTP standard avec un corps JSON {"detail": "…"}.

CodeSignificationQue faire
400Requête malforméeCorrigez le payload ; le message nomme le champ
401Jeton absent / expiré / invalideRé-échangez la clé
402Crédits insuffisantsRechargez ; le run n'a pas démarré et n'est pas facturé
403Authentifié mais non autoriséMauvaise organisation, agent non accordé à votre équipe, ou compte suspendu
404Inconnu ou pas à vousLes identifiants sont cloisonnés par organisation
409Conflit (session occupée…)Lisez le corps ; une mise en file est signalée dans POST /chat
429Débit dépasséBackoff exponentiel, respectez Retry-After s'il est présent
5xxCôté serveurRéessayez avec backoff ; les runs sont idempotents avec idempotency_key

Idempotence. Envoyez idempotency_key sur POST /chat — rejouer la même clé renvoie le run d'origine (resumed: true) au lieu d'en démarrer et d'en facturer un second.

Identifiants : UUID. Horodatages : ISO-8601 UTC. Envoyez client_tz (IANA, ex. "Africa/Algiers") sur POST /chat pour que l'agent raisonne à votre heure locale.

Limites de débit.

SurfaceLimite
POST /chat et runs20 req/s en régime, rafale 40, par utilisateur
POST /auth/token60/min par IP cliente
POST /auth/loginlimité par compte après échecs répétés
POST /auth/registerplafonné par IP et par heure
Corps de requête300 Mo (uploads base64 inclus)

Concurrence. Les runs sont sérialisés par session : un second message envoyé pendant qu'un run tourne est accepté, mis en file côté serveur et exécuté ensuite (status: "queued", avec un queue_seq). Utilisez des session_id différents pour paralléliser.

4. Facturation

Chaque action IA débite des crédits du solde de votre organisation — le même solde que celui affiché dans l'application, que le travail vienne d'un navigateur, d'un appel API, d'une tâche planifiée ou d'un widget embarqué. Chaque run émet un événement usage avec ses compteurs de jetons, et GET /balance renvoie le solde courant. Un run impayable est refusé en 402 avant tout travail.

5. Sécurité

Ce que la plateforme garantit :

  • Clés stockées hachées (SHA-256). Le secret n'existe qu'une fois, dans la réponse de création.
  • Jetons de courte durée. 15 minutes ; désactiver un compte ou incrémenter sa version de jeton invalide ses sessions et bloque tout nouvel échange.
  • Rédaction des secrets. Les agents ne peuvent pas divulguer les secrets de la plateforme : clés API, mots de passe SMTP, chaînes de connexion et clés privées sont retirés des résultats d'outils, des erreurs d'outils et des réponses du modèle par un filtre déterministe — pas par une consigne de prompt qu'on peut contourner.
  • Confinement des injections de prompt. Dès qu'un run ingère du contenu non fiable (fichier importé, page scrapée, résultat de recherche), les actions sortantes — envoi d'e-mail, publication, écriture en mémoire, HTTP arbitraire — sont bloquées tant que vous ne les confirmez pas explicitement.
  • Exécution en bac à sable. Le code écrit par un agent tourne dans un conteneur gVisor à usage unique, sans secrets ni réseau direct : la sortie passe par un proxy avec filtre SSRF.
  • Cloisonnement. Chaque identifiant est rattaché à votre organisation et revérifié côté serveur ; l'identité vient toujours du jeton, jamais d'un champ de la requête.
  • Traçabilité. Chaque appel d'outil est enregistré avec son run, son organisation et son statut.

Ce qui vous revient :

  • Gardez les clés côté serveur, dans un coffre ou une variable d'environnement — jamais dans un navigateur, un bundle mobile, un dépôt git ou un log de CI.
  • Une clé par système, nommée d'après ce système, pour pouvoir la révoquer isolément.
  • Faites tourner vos clés : créez la nouvelle, déployez, puis révoquez l'ancienne. last_used_at vous dit quand une clé peut être retirée sans risque.
  • Toute clé apparue dans un log, une capture d'écran ou un ticket est compromise — révoquez-la.
  • Si vous réexposez l'API à vos propres utilisateurs, appliquez votre propre autorisation : une clé plateforme ne connaît pas vos utilisateurs finaux.

Catalogue complet : Référence API.