nAIxus Docs
Référence

Référence de l'API publique

Contrat route par route pour le canal API - exécution serveur-à-serveur et client-à-serveur, streaming SSE, signature HMAC, continuité de conversation et gestion des credentials.

Cette référence documente les points d'accès d'intégration publics exposés par le service Core. Les API d'administration (flows, tenants, secrets) ne font pas partie du contrat public et ne sont pas documentées ici.

URL de base

Les points d'accès publics sont servis à la racine de l'origine du service Core (pas de préfixe /api/v1). Remplacez https://api.example.com par l'origine de votre déploiement.


Modes d'intégration

Le canal API gère les appels serveur-à-serveur et client-à-serveur. Le type de clé détermine le mode ; la route, le corps de requête et la réponse sont identiques.

ModeCléAppelant typiqueVérification d'origine
Serveur-à-serveurnx_live_ (prod) · nx_test_ (dev/staging)Service backend, pipeline CI, script côté serveurNon vérifiée - ne jamais utiliser depuis un binaire client ou navigateur
Client-à-serveurnx_pub_Application mobile (iOS/Android), SPA, client natifÉvaluée par rapport à allowed_origins quand Origin est présent

OPTIONS /public/v1/api/execute - Préflighting CORS

Pour les clients navigateur et mobile web qui envoient un préflight avant le POST effectif.

Aucune authentification requise.

OPTIONS /public/v1/api/execute

En-têtes de réponse :

En-têteValeur
Access-Control-Allow-HeadersAuthorization, Content-Type
Access-Control-Allow-MethodsPOST, OPTIONS
Access-Control-Allow-OriginReflète l'Origin de la requête

Access-Control-Allow-Credentials n'est jamais renseigné.


POST /public/v1/api/execute - Exécuter un flow

Exécute le flow lié au canal identifié par la clé d'API. Retourne le résultat en JSON ou sous forme de flux SSE en direct.

POST /public/v1/api/execute
Authorization: Bearer <clé>
Content-Type: application/json

Authentification

CredentialAppelantEnvironnementComportement de l'origine
nx_live_Backend de confianceCanaux de production uniquementNon évaluée
nx_test_Backend de confianceCanaux dev / staging uniquementNon évaluée
nx_pub_Navigateur ou client natif publicTout canal supportant le mode publicÉvaluée par rapport à allowed_origins quand Origin est présent

Le mode de clé est fixé à la création du canal. Un canal à clé serveur rejette les clés nx_pub_ et vice versa.

Politique d'origine pour les clés publiques (nx_pub_) : Si Origin est présent, il doit correspondre exactement à la liste allowed_origins du canal ou la requête est rejetée avec origin_not_allowed (403). Si Origin est absent (app native ou code côté serveur), la requête est acceptée. Voir les implications de sécurité.

N'intégrez jamais les clés nx_live_ ou nx_test_ dans des bundles navigateur, des binaires d'applications mobiles, ou des dépôts de code source. Ces clés contournent les contrôles d'origine et s'authentifient en tant que backend de confiance.

En-têtes de requête

En-têteRequisNotes
Authorization: Bearer <clé>Ouinx_live_, nx_test_, ou nx_pub_.
Content-Type: application/jsonOuimultipart/form-data retourne 415.
OriginConditionnelEnvoyé automatiquement par les navigateurs. Les applications mobiles utilisant nx_pub_ doivent l'omettre sauf pour restreindre à des origines spécifiques.
X-Naixus-SignatureConditionnelRequis lorsque require_signature = true sur le canal. Voir Signature HMAC.

Corps de la requête

ChampTypeRequisContraintes
payloadstringOui1 à 10 000 caractères. Le message ou l'entrée principale de l'utilisateur.
metadataobjectNonDonnées contextuelles libres (ex. {"userId":"u_1","source":"mobile"}).
conversation_idstringNonUUID d'une conversation existante à reprendre. Omettez pour créer une nouvelle conversation.
streambooleanNonRemplace le paramètre default_stream du canal. Omettez pour utiliser la valeur par défaut du canal.

stream prend la priorité sur default_stream du canal lorsqu'il est fourni.

Exemple - application mobile, non streamée :

{
  "payload": "Quel est le statut de la commande #12345 ?",
  "metadata": { "source": "ios-app", "userId": "u_abc" },
  "stream": false
}

Exemple - service backend, streaming :

{
  "payload": "Résume cette demande",
  "metadata": { "source": "backend" },
  "stream": true
}

Exemple - reprise d'une conversation existante :

{
  "payload": "Que t'avais-je demandé avant ?",
  "conversation_id": "b1f2c3d4-e5f6-7890-abcd-ef1234567890",
  "stream": false
}

Réponse : JSON (stream = false)

Statut : 200 OK · Content-Type : application/json

ChampTypeNotes
run_idstring (UUID)Identifiant stable d'exécution.
conversation_idstring (UUID)Identifiant stable de la conversation. À transmettre pour reprendre la conversation.
statusstring"success", "error", "pending" ou "cancelled".
outputobject | nullEnveloppe stable décrite ci-dessous.
usageobject | nulltotal_nodes (nombre de nœuds exécutés) et duration_ms (durée de l'exécution).
errorobject | nullPrésent lorsque status vaut "error".

Champs de output :

ChampTypeNotes
output.resultany | nullSortie du flow lui-même. La forme dépend du flow.
output.errorstring | nullMessage d'erreur du flow, si l'exécution a échoué.
output.messagesarray<{role, content}>Messages produits par cette exécution uniquement (pas l'historique complet) : réponse de l'assistant, ou question posée si le flow s'est mis en pause. role vaut toujours "assistant" aujourd'hui. Tableau vide si aucun message produit. Pour l'historique complet, voir Conversations.
output.metadataobjectMétadonnées d'exécution : channel_id, flow_id, flow_name, flow_version, environment, execution_order, started_at/completed_at (ISO 8601), et livechat_mode ("livechat_waiting" uniquement lorsque l'exécution s'est mise en pause sur un transfert livechat, sinon null).
{
  "run_id": "9c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
  "conversation_id": "b1f2c3d4-e5f6-7890-abcd-ef1234567890",
  "status": "success",
  "output": {
    "result": { "text": "Voici le résumé demandé." },
    "error": null,
    "messages": [{ "role": "assistant", "content": "Voici le résumé demandé." }],
    "metadata": {
      "channel_id": "6f1e2d3c-4b5a-6978-8f9e-0a1b2c3d4e5f",
      "flow_id": "1a2b3c4d-5e6f-7890-abcd-ef1234567890",
      "flow_name": "Résumé de demande",
      "flow_version": null,
      "environment": "prod",
      "livechat_mode": null,
      "execution_order": ["trigger_from_channel", "agent", "direct_message"],
      "started_at": "2026-08-04T10:00:00.000Z",
      "completed_at": "2026-08-04T10:00:01.500Z"
    }
  },
  "usage": { "total_nodes": 3, "duration_ms": 1500 },
  "error": null
}

Réponse : flux SSE (stream = true)

Statut : 200 OK · Content-Type : text/event-stream; charset=utf-8

En-têtes de réponse supplémentaires :

En-têteValeur
Cache-Controlno-cache, no-transform
Connectionkeep-alive
X-Accel-Bufferingno

Le flux suit le protocole Vercel AI SDK UI Message Stream. Chaque événement est une ligne data: {json}\n\n.

Séquence des événements :

data: {"type":"data-run","data":{"run_id":"9c1d…","conversation_id":"b1f2…"}}
data: {"type":"text-start","id":"<message_id>"}
data: {"type":"text-delta","id":"<message_id>","delta":"Voici "}
data: {"type":"text-delta","id":"<message_id>","delta":"le résumé…"}
data: {"type":"text-end","id":"<message_id>"}
data: {"type":"finish","finishReason":"stop"}

Types d'événements :

TypeÉmis quandChamps clés
data-runPremier événement, avant text-startdata.run_id, data.conversation_id
text-startAvant le premier delta de texteid (identifiant du message)
text-deltaÀ chaque token incrémentalid, delta (fragment de texte)
text-endAprès tous les deltas de texteid
finishFin normalefinishReason: "stop"
errorErreur en cours de fluxerrorText (message lisible)

data-run porte les identifiants stables run_id et conversation_id. Lisez-le avant de traiter les événements text-delta si vous avez besoin de ces valeurs pendant le flux.

Sémantique des erreurs :

  • Les erreurs avant l'envoi du premier octet : réponse HTTP d'erreur normale (aucun flux n'est ouvert).
  • Les erreurs après le début du streaming : un événement error est émis puis le flux se termine. Le statut HTTP est déjà 200 ; l'événement error est le signal d'échec. Aucun événement finish ne suit un error.

Signature des requêtes avec HMAC

Fonctionnalité optionnelle par canal. Principalement pertinente pour les appelants serveur-à-serveur.

Format de l'en-tête de signature :

X-Naixus-Signature: t=<unix_timestamp>,v1=<hex_minuscules>
  • t - horodatage Unix décimal en secondes depuis l'epoch.
  • v1 - digest HMAC-SHA256 en hexadécimal minuscule.

Séquence d'octets signée : <chaine_timestamp> + "." + <corps_http_brut_exact>

Signez les octets bruts exactement tels qu'envoyés. Tout changement d'espacement, d'ordre des clés JSON ou d'encodage invalide la signature. Le serveur accepte |maintenant − t| ≤ 300 secondes.

Il n'existe pas de base de données de nonce anti-replay. La fenêtre de 300 secondes est le seul mécanisme de prévention du rejeu. Voir l'explication de l'architecture pour les compromis de sécurité.

Exemple shell / curl :

BODY='{"payload":"Résume cette demande","stream":false}'
SECRET='votre_secret_de_signature'
TS=$(date +%s)
SIG=$(printf '%s.%s' "$TS" "$BODY" | \
  openssl dgst -sha256 -hmac "$SECRET" -hex | awk '{print $2}')

curl -X POST "https://api.example.com/public/v1/api/execute" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -H "X-Naixus-Signature: t=$TS,v1=$SIG" \
  -d "$BODY"

Exemple TypeScript / Node.js :

import { createHmac } from 'crypto';

function signRequest(body: string, secret: string): string {
  const ts = Math.floor(Date.now() / 1000);
  const digest = createHmac('sha256', secret).update(`${ts}.${body}`).digest('hex');
  return `t=${ts},v1=${digest}`;
}

const body = JSON.stringify({ payload: 'Résume cette demande', stream: false });

await fetch('https://api.example.com/public/v1/api/execute', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.API_KEY}`,
    'Content-Type': 'application/json',
    'X-Naixus-Signature': signRequest(body, process.env.SIGNING_SECRET!),
  },
  body,
});

Exemple Python :

import hashlib, hmac, time, os

def sign_request(body: bytes, secret: str) -> str:
    ts = str(int(time.time()))
    signed = ts.encode("ascii") + b"." + body
    digest = hmac.new(secret.encode("utf-8"), signed, hashlib.sha256).hexdigest()
    return f"t={ts},v1={digest}"

body = b'{"payload":"R\u00e9sume cette demande","stream":false}'
header = sign_request(body, os.environ["SIGNING_SECRET"])

Reprise d'une conversation

  • L'omission de conversation_id crée une nouvelle conversation. L'identifiant conversation_id attribué est retourné dans la réponse JSON ou dans l'événement SSE data-run. Transmettez-le dans les requêtes suivantes pour maintenir le contexte.
  • La fourniture de conversation_id reprend une conversation existante. La conversation doit appartenir au tenant authentifié et au même canal.
  • La reprise inter-tenant ou inter-canal retourne conversation_not_found (404) - la même erreur non-énumérative utilisée pour les identifiants inconnus.
  • conversation_id n'est pas une métadonnée de corrélation. Il identifie le contexte de conversation avec état utilisé par le flow (ex. historique de chat, fenêtre de contexte).

État du canal

ÉtatExécutionGestion
activeAutoriséeAutorisée
inactive409Autorisée
archived410Non autorisée

Erreurs

Toutes les erreurs utilisent une enveloppe cohérente :

{ "error": { "code": "<code_machine>", "message": "<message_lisible>" } }
HTTPCodeSignificationCause fréquenteAction
401missing_api_keyPas d'en-tête AuthorizationClé non envoyéeAjouter Authorization: Bearer <clé>
401invalid_api_keyClé invalide, révoquée ou expiréeMauvaise clé ou clé renouveléeVérifier la valeur ; renouveler si nécessaire
401signature_requiredX-Naixus-Signature absentSignature activée mais en-tête absentAjouter l'en-tête de signature
401signature_expiredHorodatage hors de ±300 sDécalage d'horloge ou requête périméeSynchroniser l'horloge ; re-signer avec l'horodatage actuel
401invalid_signatureEn-tête mal formé ou digest incorrectMauvais secret, mauvais corps d'octets ou encodage incorrectVérifier le secret ; signer les octets bruts tels qu'envoyés
403environment_mismatchEnvironnement de la clé incompatible avec le canalClé nx_live_ sur canal dev, ou nx_test_ sur canal prodUtiliser la bonne clé pour l'environnement du canal
403origin_not_allowedOrigin présente mais absente de la liste des origines autoriséesOrigine navigateur non dans la liste du canalAjouter l'origine dans la console ; ou utiliser un proxy backend
403wrong_channel_typeLe canal n'est pas de type APILa clé appartient à un canal non-APIUtiliser une clé d'un canal de type API
403conversation_forbiddenconversation_id appartient à un autre canalTentative de reprise inter-canalReprendre uniquement dans le canal d'origine
404conversation_not_foundconversation_id inconnu ou inter-tenantMauvais identifiant, mauvais tenant ou inexistantVérifier l'identifiant de conversation
409channel_inactiveCanal inactifCanal désactivé dans la consoleRéactiver le canal
410channel_archivedCanal archivéCanal retiré définitivementUtiliser un autre canal
415unsupported_media_typeCorps multipart/form-data envoyéEnvoi de fichier vers le point d'accès JSONUtiliser la variante upload
422validation_errorCorps de requête invalidepayload manquant, types incorrectsVérifier les contraintes de champs dans cette référence
503signing_secret_unavailableSignature requise mais aucun secret configuréSecret jamais créé ou suppriméCréer ou renouveler le secret de signature
500runtime_errorErreur d'exécution inattendueProblème côté serveurRéessayer ; contacter le support si persistant

Conversations

Une conversation s'étend sur plusieurs appels à /execute. Le conversation_id retourné par /execute sert de session_id pour le point d'accès ci-dessous. output.messages (voir plus haut) n'expose que les messages produits par le dernier appel ; utilisez cette route pour récupérer l'historique complet de la conversation.

GET /public/v1/api/conversations/{session_id}/messages - Récupérer l'historique d'une conversation

Authentification : même clé bearer que /execute (nx_live_, nx_test_ ou nx_pub_). Aucune signature HMAC requise, quelle que soit la configuration du canal.

GET /public/v1/api/conversations/b1f2c3d4-e5f6-7890-abcd-ef1234567890/messages?limit=50
Authorization: Bearer <clé>

Paramètres :

ParamètreTypeEmplacementRequisContraintes
session_idstringCheminOuiLe conversation_id retourné par /execute.
limitintRequêteNon1 à 200. Défaut : 50.
token_budgetintRequêteNonDéfaut : 0 (illimité). Tronque l'historique à ce budget de tokens.

Réponse : 200 OK · Content-Type : application/json

ChampTypeNotes
session_idstringReprend le session_id de la requête.
messagesarrayChronologique, du plus ancien au plus récent. Voir le détail des champs ci-dessous.
total_messagesintNombre total de messages retournés.
total_tokensintEstimation du nombre de tokens dans messages.
modestring | nullbot, livechat_waiting, livechat_active, livechat_closed, livechat_expired, livechat_cancelled ou livechat_unavailable.
livechat_status_messagestring | nullMessage configuré par le tenant pour le mode courant (ex. message d'attente ou de clôture).
livechat_conversation_idstring | nullIdentifiant de la conversation livechat active ou clôturée, si applicable.

Champs de chaque entrée de messages :

ChampTypeNotes
originstring"agent" (produit par un run) ou "livechat" (produit par un agent humain).
rolestringuser, assistant, system ou tool.
contentstringContenu du message.
timestampstringISO 8601.
turn_indexintPosition dans la conversation, à partir de 0.
token_countintEstimation du nombre de tokens du message.
run_idstring | nullDéfini uniquement lorsque origin vaut "agent".
step_idstring | nullDéfini uniquement lorsque origin vaut "agent".
node_typestring | nullType de nœud exécuteur, uniquement lorsque origin vaut "agent".
tool_callsarray | nullAppels d'outils au format OpenAI, si présents.
tool_call_idstring | nullAppel d'outil auquel ce message répond, si applicable.
livechat_message_idstring | nullDéfini uniquement lorsque origin vaut "livechat".
senderstring | nullExpéditeur brut côté livechat (user, agent, supervisor, bot ou system). origin="livechat" uniquement.

Exemple de réponse :

{
  "session_id": "b1f2c3d4-e5f6-7890-abcd-ef1234567890",
  "messages": [
    {
      "origin": "agent",
      "role": "user",
      "content": "Je veux parler à un humain",
      "timestamp": "2026-08-04T10:00:00.000Z",
      "turn_index": 0,
      "token_count": 6,
      "run_id": "9c1d2e3f-4a5b-6c7d-8e9f-0a1b2c3d4e5f",
      "step_id": "2b3c4d5e-6f70-8192-a3b4-c5d6e7f80912",
      "node_type": "trigger_from_channel",
      "tool_calls": null,
      "tool_call_id": null,
      "livechat_message_id": null,
      "sender": null
    },
    {
      "origin": "livechat",
      "role": "assistant",
      "content": "Bonjour, je suis Claire, en quoi puis-je vous aider ?",
      "timestamp": "2026-08-04T10:01:12.000Z",
      "turn_index": 1,
      "token_count": 12,
      "run_id": null,
      "step_id": null,
      "node_type": null,
      "tool_calls": null,
      "tool_call_id": null,
      "livechat_message_id": "3c4d5e6f-7081-92a3-b4c5-d6e7f8091234",
      "sender": "agent"
    }
  ],
  "total_messages": 2,
  "total_tokens": 18,
  "mode": "livechat_active",
  "livechat_status_message": null,
  "livechat_conversation_id": "4d5e6f70-8192-a3b4-c5d6-e7f809123456"
}

Erreurs :

HTTPSignificationCause fréquenteAction
404Aucune conversation trouvée pour ce session_id, ou appartenant à un autre canal/tenantMauvais session_id, ou tentative inter-canalVérifier le session_id et le canal utilisé

Contrairement aux erreurs de /execute, cette route ne renvoie pas l'enveloppe {"error": {"code", "message"}} de cette référence : elle renvoie le format d'erreur HTTP par défaut, {"detail": "No conversation found for session {session_id}"}.


GET /public/widget/config - Charger la configuration du widget

Alimente le widget de chat navigateur intégrable. Nécessite une clé publique (nx_pub_) et applique les origines autorisées.

GET /public/widget/config
Authorization: Bearer nx_pub_xxxxxxxx
Origin: https://site-client.example

Retourne la configuration widget du canal (localisation et options de fonctionnalités).


POST /public/widget/chat - Streamer une conversation widget

POST /public/widget/chat
Authorization: Bearer nx_pub_xxxxxxxx
Origin: https://site-client.example
Content-Type: application/json

Diffuse la réponse du flow en Server-Sent Events avec le même protocole Vercel AI SDK UI Message Stream.


POST /api/v1/channels/{channel_id}/rotate-key - Renouveler la clé d'API

Nécessite un JWT admin tenant (Authorization: Bearer <JWT> avec X-Tenant-ID). Non accessible avec les clés d'API de canal.

Révoque la clé existante et génère une nouvelle clé avec le même préfixe et environnement. La nouvelle clé en clair est retournée une seule fois dans api_key. Stockez-la immédiatement.

{
  "api_key": "nx_live_nouvelleClé1a2b3c…",
  "channel": { "id": "…", "api_key_hint": "nx_live_••••abcd" }
}

POST /api/v1/channels/{channel_id}/rotate-signing-secret - Renouveler le secret de signature

Nécessite un JWT admin tenant.

Génère un nouveau secret HMAC. Le secret précédent est immédiatement invalidé. Le nouveau secret en clair est retourné une seule fois dans signing_secret.

{
  "signing_secret": "whs_…",
  "channel": { "id": "…", "has_signing_secret": true }
}

PATCH /api/v1/channels/{channel_id} - Activer ou désactiver la signature obligatoire

Nécessite un JWT admin tenant.

PATCH /api/v1/channels/{channel_id}
{ "config": { "require_signature": true } }

Lorsque require_signature passe de false à true, la réponse inclut un signing_secret à usage unique. La désactivation désactive le contrôle sans supprimer le secret stocké.


Confirmé, non spécifié et non supporté

  • Confirmé : tous les points d'accès, schémas de requête/réponse, règles d'authentification, codes d'erreur et événements SSE décrits dans cette référence sont vérifiés par rapport à l'implémentation du service Core.
  • Non spécifié : les seuils de limitation de débit, le comportement de retry, les clés d'idempotence et la pagination ne font pas partie du contrat vérifié. Traitez 429 comme un signal de temporisation.
  • Non supporté : les points d'accès d'administration (flows, tenants, secrets, gestion des canaux) sont internes et ne doivent pas être appelés par des intégrateurs externes.

Voir aussi

On this page