Aller au contenu

API — Référence v1

Introduction

L'API SalamBot v1 permet l'intégration serveur-serveur et l'accès aux canaux (webchat, sociaux) via un modèle d'envelope unifié.

Pour les détails sur l'envelope et les composants : Concepts/composants.

Base URL & Versionnage

Clé Valeur
base_url https://api.salambot.local/v1
version v1
content_type application/json; charset=utf-8
charset utf-8

Authentification & En-têtes

Support : Bearer OIDC/JWT.

En-têtes recommandés :

En-tête Valeur
Authorization Bearer <token>
X-SalamBot-Tenant <tenant>
Idempotency-Key <uuid>
X-Request-Id <uuid>

Scopes (si activés) :

scope usage
messages:analyze NLU
search:query RAG
generate:reply LLM Router
incidents:write Incidents
tickets:write Tickets
admin:read Lecture policies
admin:write Mise à jour policies

Flux d'authentification JWT

Le diagramme suivant illustre le processus d'authentification complet avec gestion des refresh tokens :

sequenceDiagram
    participant C as Client
    participant API as API Gateway
    participant AUTH as Service Auth
    participant OIDC as Provider OIDC
    participant CACHE as Cache Redis

    Note over C,CACHE: Authentification initiale
    C->>AUTH: POST /auth/login<br/>{username, password}
    AUTH->>OIDC: Validation credentials
    OIDC-->>AUTH: User claims + scopes
    AUTH->>AUTH: Génération JWT + refresh token
    AUTH->>CACHE: Store refresh token (TTL 7d)
    AUTH-->>C: {access_token, refresh_token, expires_in}

    Note over C,CACHE: Requête API avec token
    C->>API: GET /v1/messages/analyze<br/>Authorization: Bearer <jwt>
    API->>API: Validation JWT signature
    API->>API: Vérification expiration

    alt Token valide
        API->>API: Extraction claims (tenant, scopes)
        API->>API: Vérification scope messages:analyze
        API-->>C: 200 OK + données
    else Token expiré
        API-->>C: 401 Unauthorized<br/>{error: "token_expired"}
    else Token invalide
        API-->>C: 401 Unauthorized<br/>{error: "invalid_token"}
    end

    Note over C,CACHE: Refresh du token
    C->>AUTH: POST /auth/refresh<br/>{refresh_token}
    AUTH->>CACHE: Validation refresh token

    alt Refresh token valide
        AUTH->>AUTH: Génération nouveau JWT
        AUTH->>CACHE: Rotation refresh token
        AUTH-->>C: {access_token, refresh_token, expires_in}
    else Refresh token expiré/invalide
        AUTH->>CACHE: Suppression token
        AUTH-->>C: 401 Unauthorized<br/>{error: "refresh_required"}
        Note over C: Redirection vers login
    end

    Note over C,CACHE: Logout
    C->>AUTH: POST /auth/logout<br/>{refresh_token}
    AUTH->>CACHE: Blacklist refresh token
    AUTH-->>C: 200 OK

Envelope de requête/réponse

Toutes les requêtes et réponses utilisent l'envelope unifié :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "webchat",
  "message_id": "msg_123",
  "correlation_id": "corr_abc",
  "timestamp": "2025-08-14T10:30:00Z",
  "locale": "fr-MA",
  "data": {
    "NOTE": "payload spécifique à l'endpoint"
  }
}

Conventions :

  • Champs temporels en RFC3339
  • Nommage en snake_case

Erreurs & modèle d'erreur

Mapping erreurs ↔ HTTP :

erreur http
invalid_input 400
unauthorized 401
permission_denied 403
rate_limited 429
internal_error 500
upstream_error 502
timeout 504

Modèle d'erreur :

{
  "schema_version": "1.0",
  "error": "invalid_input",
  "http": 400,
  "message": "Champ 'data.text' requis",
  "correlation_id": "corr_abc"
}

Gestion des erreurs et retry logic

Le diagramme suivant illustre la stratégie de retry différenciée selon le type d'erreur :

flowchart TD
    REQ[Requête API] --> EXEC[Exécution]
    EXEC --> SUCCESS{Succès?}

    SUCCESS -->|Oui| OK[200-299: Retour résultat]
    SUCCESS -->|Non| ERROR{Type d'erreur?}

    ERROR -->|400-499| CLIENT[Erreur client]
    ERROR -->|500-599| SERVER[Erreur serveur]
    ERROR -->|Timeout| TIMEOUT[Timeout réseau]

    CLIENT --> CHECK_400{Code spécifique?}
    CHECK_400 -->|400 invalid_input| NO_RETRY[❌ Pas de retry<br/>Corriger payload]
    CHECK_400 -->|401 unauthorized| AUTH_RETRY{Token refresh?}
    CHECK_400 -->|403 permission_denied| NO_RETRY
    CHECK_400 -->|429 rate_limited| RATE_RETRY[⏱️ Retry avec backoff<br/>Délai = Retry-After header]

    AUTH_RETRY -->|Possible| REFRESH[Refresh token]
    AUTH_RETRY -->|Impossible| NO_RETRY
    REFRESH --> SUCCESS_REFRESH{Refresh OK?}
    SUCCESS_REFRESH -->|Oui| RETRY_AUTH[🔄 Retry requête originale]
    SUCCESS_REFRESH -->|Non| NO_RETRY

    SERVER --> CHECK_500{Code spécifique?}
    CHECK_500 -->|500 internal_error| RETRY_EXP[🔄 Retry exponentiel<br/>Max 3 tentatives]
    CHECK_500 -->|502 upstream_error| RETRY_EXP
    CHECK_500 -->|503 service_unavailable| RETRY_LINEAR[🔄 Retry linéaire<br/>Délai fixe 5s]
    CHECK_500 -->|504 timeout| RETRY_EXP

    TIMEOUT --> RETRY_EXP

    RATE_RETRY --> WAIT_RATE[Attendre délai]
    WAIT_RATE --> RETRY_AUTH

    RETRY_EXP --> COUNT_EXP{Tentatives < 3?}
    COUNT_EXP -->|Oui| BACKOFF_EXP[Backoff: 2^n * 1s<br/>n = tentative]
    COUNT_EXP -->|Non| FAIL[❌ Échec définitif]
    BACKOFF_EXP --> RETRY_AUTH

    RETRY_LINEAR --> COUNT_LIN{Tentatives < 5?}
    COUNT_LIN -->|Oui| BACKOFF_LIN[Attendre 5s]
    COUNT_LIN -->|Non| FAIL
    BACKOFF_LIN --> RETRY_AUTH

    RETRY_AUTH --> EXEC

    style OK fill:#4caf50
    style NO_RETRY fill:#f44336
    style FAIL fill:#f44336
    style RETRY_EXP fill:#ff9800
    style RETRY_LINEAR fill:#ff9800
    style RATE_RETRY fill:#2196f3

Rate limiting & Idempotency

Clé Valeur
rate_limit_unit requests/min
rate_limit_scope per-tenant + per-channel
rate_limit_headers X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset
retry-after sur 429 (en secondes)
idempotency Idempotency-Key sur POST
idempotency_scope par (tenant, channel, message_id), fenêtre 24h
idempotency_behavior si même clé: retour premier résultat (HTTP 201/200), sinon 409 conflit payload

Endpoints

Tous les endpoints suivent le pattern /v1/{service}/{action} avec authentification Bearer JWT.

Endpoints canoniques

  • Génération : POST /v1/generate/reply
  • Recherche RAG : POST /v1/search/query

Toute autre variante est dépréciée. Les exemples et SDK doivent s'aligner sur ces chemins.

Endpoints — NLU (Messages)

POST /v1/messages/analyze

Analyse le texte utilisateur pour extraire l'intention, les entités et la confiance. Utilisé pour le routage intelligent des messages.

Request :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "webchat",
  "message_id": "msg_123",
  "correlation_id": "corr_abc",
  "timestamp": "2025-08-14T10:30:00Z",
  "locale": "fr-MA",
  "data": {
    "text": "Je veux annuler ma commande",
    "lang": "fr"
  }
}

Response :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "webchat",
  "message_id": "msg_123",
  "correlation_id": "corr_abc",
  "timestamp": "2025-08-14T10:30:01Z",
  "locale": "fr-MA",
  "data": {
    "intent": "cancel_order",
    "confidence": 0.92,
    "entities": [
      { "type": "action", "value": "annuler", "start": 8, "end": 15 }
    ]
  }
}

Exemple cURL :

curl -X POST https://api.salambot.local/v1/messages/analyze \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
  -H "Content-Type: application/json" \
  -H "X-SalamBot-Tenant: acme" \
  -d '{
    "schema_version": "1.0",
    "tenant": "acme",
    "channel": "webchat",
    "message_id": "msg_123",
    "correlation_id": "corr_abc",
    "timestamp": "2025-08-14T10:30:00Z",
    "locale": "fr-MA",
    "data": {
      "text": "Je veux annuler ma commande",
      "lang": "fr"
    }
  }'

Codes : 200, 400, 401, 429, 500

POST /v1/generate/stream

Stream SSE de la réponse LLM (recommandé pour Webchat). Contenu: text/event-stream.

Request :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "webchat",
  "message_id": "msg_789",
  "correlation_id": "corr_ghi",
  "timestamp": "2025-08-14T10:34:00Z",
  "locale": "fr-MA",
  "data": {
    "prompt": "Comment puis-je annuler ma commande ?",
    "context": [
      "L'utilisateur a une commande active #12345",
      "Politique: annulation possible sous 24h"
    ],
    "model": "llama3"
  }
}

Réponse (SSE) :

event: token
data: {"text":"Pour annuler ","seq":1}

event: token
data: {"text":"votre commande...","seq":2}

event: done
data: {"model_used":"llama3","tokens":156,"confidence":0.94}

Exemple cURL :

curl -N -X POST https://api.salambot.local/v1/generate/stream \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: text/event-stream" \
  -d '{
    "schema_version": "1.0",
    "tenant": "acme",
    "channel": "webchat",
    "message_id": "msg_789",
    "correlation_id": "corr_ghi",
    "timestamp": "2025-08-14T10:34:00Z",
    "locale": "fr-MA",
    "data": {
      "prompt": "Comment puis-je annuler ma commande ?",
      "context": ["commande #12345"],
      "model": "llama3"
    }
  }'

Codes : 200 (SSE), 400, 401, 429, 500

Endpoints — Recherche (RAG)

POST /v1/search/query

Recherche contextuelle dans la base de connaissances. Retourne les passages les plus pertinents avec scores de similarité.

Request :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "webchat",
  "message_id": "msg_456",
  "correlation_id": "corr_def",
  "timestamp": "2025-08-14T10:32:00Z",
  "locale": "fr-MA",
  "data": {
    "query": "politique de remboursement",
    "filters": { "category": "support" },
    "limit": 5
  }
}

Response :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "webchat",
  "message_id": "msg_456",
  "correlation_id": "corr_def",
  "timestamp": "2025-08-14T10:32:01Z",
  "locale": "fr-MA",
  "data": {
    "passages": [
      "Les remboursements sont traités sous 7 jours...",
      "Pour demander un remboursement, contactez..."
    ],
    "ids": ["doc_123", "doc_456"],
    "scores": [0.89, 0.76]
  }
}

Exemple cURL :

curl -X POST https://api.salambot.local/v1/search/query \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "schema_version": "1.0",
    "tenant": "acme",
    "channel": "webchat",
    "message_id": "msg_456",
    "correlation_id": "corr_def",
    "timestamp": "2025-08-14T10:32:00Z",
    "locale": "fr-MA",
    "data": {
      "query": "politique de remboursement",
      "filters": {"category": "support"},
      "limit": 5
    }
  }'

Codes : 200, 400, 401, 429, 500, 502

Endpoints — Génération (LLM Router)

POST /v1/generate/reply

Génère une réponse contextuelle via LLM. Route automatiquement vers le modèle optimal selon le contexte.

Request :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "webchat",
  "message_id": "msg_789",
  "correlation_id": "corr_ghi",
  "timestamp": "2025-08-14T10:34:00Z",
  "locale": "fr-MA",
  "data": {
    "prompt": "Comment puis-je annuler ma commande ?",
    "context": [
      "L'utilisateur a une commande active #12345",
      "Politique: annulation possible sous 24h"
    ],
    "model": "llama3"
  }
}

Response :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "webchat",
  "message_id": "msg_789",
  "correlation_id": "corr_ghi",
  "timestamp": "2025-08-14T10:34:02Z",
  "locale": "fr-MA",
  "data": {
    "reply_text": "Pour annuler votre commande #12345, vous pouvez...",
    "model_used": "llama3",
    "tokens": 156,
    "confidence": 0.94,
    "filtered": false
  }
}

Exemple cURL :

curl -X POST https://api.salambot.local/v1/generate/reply \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "schema_version": "1.0",
    "tenant": "acme",
    "channel": "webchat",
    "message_id": "msg_789",
    "correlation_id": "corr_ghi",
    "timestamp": "2025-08-14T10:34:00Z",
    "locale": "fr-MA",
    "data": {
      "prompt": "Comment puis-je annuler ma commande ?",
      "context": ["L'utilisateur a une commande active #12345"],
      "model": "llama3"
    }
  }'

Codes : 200, 400, 401, 429, 500

Endpoints — Incidents & Broadcast

POST /v1/incidents

Request :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "system",
  "message_id": "msg_inc1",
  "correlation_id": "corr_inc",
  "timestamp": "2025-08-14T10:36:00Z",
  "locale": "fr-MA",
  "data": {
    "trigger": "Panne serveur principal",
    "severity": "high",
    "details": {
      "affected_services": ["webchat", "api"],
      "estimated_impact": "30min"
    }
  }
}

Response :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "system",
  "message_id": "msg_inc1",
  "correlation_id": "corr_inc",
  "timestamp": "2025-08-14T10:36:01Z",
  "locale": "fr-MA",
  "data": {
    "ticket_id": "INC-2025-001",
    "status": "assigned",
    "assignee": "ops-team"
  }
}

Codes : 201, 400, 401, 403, 429, 500

POST /v1/broadcasts

Request :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "admin",
  "message_id": "msg_bcast",
  "correlation_id": "corr_bcast",
  "timestamp": "2025-08-14T10:38:00Z",
  "locale": "fr-MA",
  "data": {
    "message": "Maintenance programmée ce soir 22h-23h",
    "audience": "all_users",
    "schedule": "2025-08-14T20:00:00Z"
  }
}

Response :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "admin",
  "message_id": "msg_bcast",
  "correlation_id": "corr_bcast",
  "timestamp": "2025-08-14T10:38:01Z",
  "locale": "fr-MA",
  "data": {
    "event": "broadcast_scheduled",
    "recipients": 1250,
    "status": "pending"
  }
}

Codes : 202, 400, 401, 403, 429, 500

Endpoints — Tickets

POST /v1/tickets

Request :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "webchat",
  "message_id": "msg_tkt",
  "correlation_id": "corr_tkt",
  "timestamp": "2025-08-14T10:40:00Z",
  "locale": "fr-MA",
  "data": {
    "incident": "Problème complexe non résolu",
    "user_context": {
      "id": "user_123",
      "history": ["previous_interactions"]
    },
    "priority": "high"
  }
}

Response :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "webchat",
  "message_id": "msg_tkt",
  "correlation_id": "corr_tkt",
  "timestamp": "2025-08-14T10:40:01Z",
  "locale": "fr-MA",
  "data": {
    "ticket_id": "TKT-2025-456",
    "status": "assigned",
    "eta_hours": 2
  }
}

Codes : 201, 400, 401, 403, 429, 500

Endpoints — Admin

GET /v1/admin/policies

Response :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "admin",
  "message_id": "msg_pol_get",
  "correlation_id": "corr_pol",
  "timestamp": "2025-08-14T10:42:00Z",
  "locale": "fr-MA",
  "data": {
    "policies": {
      "max_tokens": 1000,
      "allowed_models": ["llama3"],
      "branding": {
        "logo_url": "https://cdn.example.com/logo.png",
        "colors": "#123456"
      }
    }
  }
}

Codes : 200, 401, 403, 500

PUT /v1/admin/policies

Request :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "admin",
  "message_id": "msg_pol_put",
  "correlation_id": "corr_pol_upd",
  "timestamp": "2025-08-14T10:44:00Z",
  "locale": "fr-MA",
  "data": {
    "policies": {
      "max_tokens": 1500,
      "allowed_models": ["llama3", "gpt-4"]
    }
  }
}

Response :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "admin",
  "message_id": "msg_pol_put",
  "correlation_id": "corr_pol_upd",
  "timestamp": "2025-08-14T10:44:01Z",
  "locale": "fr-MA",
  "data": {
    "ack": true,
    "version": "v2.1",
    "applied_at": "2025-08-14T10:44:01Z"
  }
}

Exemple cURL :

curl -X PUT https://api.salambot.local/v1/admin/policies \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9..." \
  -H "Content-Type: application/json" \
  -d '{
    "schema_version": "1.0",
    "tenant": "acme",
    "channel": "admin",
    "message_id": "msg_pol_put",
    "correlation_id": "corr_pol_upd",
    "timestamp": "2025-08-14T10:44:00Z",
    "locale": "fr-MA",
    "data": {
      "policies": {
        "max_tokens": 1500,
        "allowed_models": ["llama3", "gpt-4"]
      }
    }
  }'

Codes : 200, 400, 401, 403, 429, 500

Healthcheck

GET /v1/health

Response :

{
  "schema_version": "1.0",
  "data": {
    "status": "ok",
    "time": "2025-08-14T10:46:00Z"
  }
}

Codes : 200

GET /v1/ready

Response :

{
  "schema_version": "1.0",
  "data": {
    "status": "ready",
    "deps": {
      "database": "ok",
      "redis": "ok",
      "llm_service": "ok"
    }
  }
}

Codes : 200, 503

Webhooks & HMAC (source unique)

En-têtes requis

  • X-Hub-Signature-256: sha256=<hex> (HMAC-SHA256 du payload brut)
  • X-Request-Id: UUID (minuscule)
  • X-SalamBot-Tenant: identifiant tenant
  • X-Timestamp: ISO 8601 UTC Z (ex: 2025-08-14T10:30:00Z)

Fenêtre temporelle & idempotence

  • Tolérance horloge : ±5 min (refus au-delà)
  • Idempotence : dédupliquer via event_id ou hash du corps 24h

Génération de la signature (éditeur tiers)

sha256 = HMAC_SHA256(secret, raw_body)
header  = "sha256=" + hex(sha256)

Vérification (Python)

import hmac, hashlib
sig_hdr = req.headers['X-Hub-Signature-256']
calc = 'sha256=' + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
assert hmac.compare_digest(calc, sig_hdr)

Réponses & retries

  • 2xx : accepté (pas de retry)
  • 4xx : invalide (no retry)
  • 5xx : retry exponentiel jusqu'à 5 tentatives (2s,4s,8s,16s,32s)

Exemples cURL

curl -X POST "$PUBLIC/webhooks/<canal>" \
  -H "Content-Type: application/json" \
  -H "X-Request-Id: $(uuidgen | tr 'A-Z' 'a-z')" \
  -H "X-SalamBot-Tenant: demo" \
  -H "X-Timestamp: 2025-08-14T10:30:00Z" \
  -H "X-Hub-Signature-256: sha256=<...>" \
  --data-binary @payload.json

Les documents Security/iam.md, Security/pii.md, Security/audit.md référencent cette section pour éviter les doublons.

Exemples d'ingestion par canal

POST /webhooks/facebook

Request :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "facebook",
  "message_id": "msg_fb",
  "correlation_id": "corr_fb",
  "timestamp": "2025-08-14T10:48:00Z",
  "locale": "fr-MA",
  "data": {
    "webhook_data": {
      "object": "page",
      "entry": [
        {
          "messaging": [
            {
              "sender": { "id": "123" },
              "message": { "text": "Bonjour" }
            }
          ]
        }
      ]
    }
  }
}

Response : 202 Accepted

POST /webhooks/whatsapp

Request :

{
  "schema_version": "1.0",
  "tenant": "acme",
  "channel": "whatsapp",
  "message_id": "msg_wa",
  "correlation_id": "corr_wa",
  "timestamp": "2025-08-14T10:50:00Z",
  "locale": "fr-MA",
  "data": {
    "webhook_data": {
      "contacts": [{ "profile": { "name": "John" } }],
      "messages": [{ "text": { "body": "Salut" } }]
    }
  }
}

Response : 202 Accepted

Pagination & filtres

Paramètres standards :

Paramètre Valeur
page int
page_size 1..100
sort created_at:desc
filter k:v

Exemples cURL groupés

NLU :

curl -X POST https://api.salambot.local/v1/messages/analyze \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"schema_version":"1.0","tenant":"acme","channel":"webchat","message_id":"msg_123","correlation_id":"corr_abc","timestamp":"2025-08-14T10:30:00Z","locale":"fr-MA","data":{"text":"Je veux annuler","lang":"fr"}}'

RAG :

curl -X POST https://api.salambot.local/v1/search/query \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"schema_version":"1.0","tenant":"acme","channel":"webchat","message_id":"msg_456","correlation_id":"corr_def","timestamp":"2025-08-14T10:32:00Z","locale":"fr-MA","data":{"query":"remboursement","limit":5}}'

LLM :

curl -X POST https://api.salambot.local/v1/generate/reply \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"schema_version":"1.0","tenant":"acme","channel":"webchat","message_id":"msg_789","correlation_id":"corr_ghi","timestamp":"2025-08-14T10:34:00Z","locale":"fr-MA","data":{"prompt":"Comment annuler ?","context":["commande #12345"],"model":"llama3"}}'

Tickets :

curl -X POST https://api.salambot.local/v1/tickets \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"schema_version":"1.0","tenant":"acme","channel":"webchat","message_id":"msg_tkt","correlation_id":"corr_tkt","timestamp":"2025-08-14T10:40:00Z","locale":"fr-MA","data":{"incident":"Problème complexe","priority":"high"}}'

Admin :

curl -X GET https://api.salambot.local/v1/admin/policies \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json"

Endpoints — Admin

POST /v1/admin/channels

Active un canal pour un tenant donné. Utilisé lors de l'onboarding.

Request :

{
  "schema_version": "1.0",
  "tenant": "nouveau-tenant",
  "channel": "admin",
  "message_id": "msg_channel_setup",
  "correlation_id": "corr_channel_setup",
  "timestamp": "2025-08-14T10:30:00Z",
  "locale": "fr-MA",
  "data": {
    "tenant": "nouveau-tenant",
    "channel": "facebook",
    "config": {
      "page_access_token": "...",
      "verify_token": "..."
    }
  }
}

Response :

{
  "schema_version": "1.0",
  "tenant": "nouveau-tenant",
  "channel": "admin",
  "message_id": "msg_channel_setup",
  "correlation_id": "corr_channel_setup",
  "timestamp": "2025-08-14T10:30:01Z",
  "locale": "fr-MA",
  "data": {
    "status": "activated",
    "channel": "facebook",
    "webhook_url": "https://api.salambot.local/webhooks/facebook"
  }
}

Exemple cURL :

curl -X POST https://api.salambot.local/v1/admin/channels \
  -H "Authorization: Bearer TOKEN" \
  -H "Content-Type: application/json" \
  -H "X-SalamBot-Tenant: nouveau-tenant" \
  -d '{
    "schema_version": "1.0",
    "tenant": "nouveau-tenant",
    "channel": "admin",
    "message_id": "msg_channel_setup",
    "correlation_id": "corr_channel_setup",
    "timestamp": "2025-08-14T10:30:00Z",
    "locale": "fr-MA",
    "data": {
      "tenant": "nouveau-tenant",
      "channel": "facebook",
      "config": {
        "page_access_token": "...",
        "verify_token": "..."
      }
    }
  }'

Codes : 201, 400, 401, 403, 409, 500

Changelog API

Version Description
v1.0 endpoints initiaux
date 2025-08-14

Time & IDs — Normes API

  • Horodatage : ISO-8601 strict en UTC avec suffixe Z (ex. 2025-08-14T10:30:00.123Z). L'affichage local (UTC+1) est UI seulement.
  • Corrélation : propager trace_id, span_id, correlation_id, X-Request-Id à chaque hop ; corrélation SIEM sur trace_id/correlation_id.
  • Idempotence : POST critiques exigent Idempotency-Key unique (TTL ≥ 24h) pour éviter les doublons.
  • Endpoints canoniques : n'utiliser que /v1/generate/reply et /v1/search/query dans cette référence (les variantes sont dépréciées).