Skip to content

Modèles d’IA ​

Les routes sont relatives à <PUBLIC_BASE_URL> ; les corps de requête et de réponse sont en JSON sauf mention contraire. L’authentification, les portées, la pagination et le format des erreurs sont décrits dans les guides de l’API REST.

GET /api/v1/llm-defaults ​

Lire les défauts d’IA résolus

Le modèle que chaque usage obtiendra réellement sur cette instance, après le routage de l’administrateur. null pour un usage qu’aucun fournisseur ne sert. Des noms de modèles seulement : ni clé, ni catalogue.

Accès — Session de membre ou clé d’API portant connections:read.

Réponses

200 — Les défauts.

ChampTypeRequis
defaultsobjectoui
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "defaults": {
      "type": "object",
      "properties": {
        "classify": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "model": { "type": "string" }
              },
              "required": [ "provider", "model" ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        },
        "compose": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "model": { "type": "string" }
              },
              "required": [ "provider", "model" ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        },
        "extract": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "model": { "type": "string" }
              },
              "required": [ "provider", "model" ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        },
        "general": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "model": { "type": "string" }
              },
              "required": [ "provider", "model" ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        },
        "analyzer": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "model": { "type": "string" }
              },
              "required": [ "provider", "model" ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        },
        "assistant": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "model": { "type": "string" }
              },
              "required": [ "provider", "model" ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        }
      },
      "required": [ "classify", "compose", "extract", "general", "analyzer", "assistant" ],
      "additionalProperties": false
    }
  },
  "required": [ "defaults" ],
  "additionalProperties": false
}

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

GET /api/v1/llm-connections ​

Lister les connexions d’IA

Ce dont le sélecteur d’un nœud a besoin : identifiant, libellé, fournisseur et drapeau par défaut. Construit sans rien déchiffrer.

Accès — Session de membre ou clé d’API portant connections:read.

Réponses

200 — Les connexions.

ChampTypeRequis
connectionsobject[]oui
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "connections": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "provider": {
            "type": "string",
            "enum": [
              "anthropic",
              "openai",
              "mistral",
              "openrouter",
              "ollama",
              "openai_compatible"
            ]
          },
          "label": { "type": "string" },
          "isDefault": { "type": "boolean" }
        },
        "required": [ "id", "provider", "label", "isDefault" ],
        "additionalProperties": false
      }
    }
  },
  "required": [ "connections" ],
  "additionalProperties": false
}

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

GET /api/v1/llm-connections/{id}/models ​

Lister les modèles d’une connexion d’IA

restricted dit si l’administrateur a fermé la liste (seulement ces modèles) ou s’il s’agit d’une aide à la saisie (modèles connus, les autres restent valides).

Accès — Session de membre ou clé d’API portant connections:read.

Paramètres

NomOùTypeRequis
idcheminstringoui

Réponses

200 — Les modèles.

ChampTypeRequis
connectionIdstring (uuid)oui
provider"anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible"oui
modelsstring[]oui
restrictedbooleanoui
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "connectionId": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "provider": {
      "type": "string",
      "enum": [
        "anthropic",
        "openai",
        "mistral",
        "openrouter",
        "ollama",
        "openai_compatible"
      ]
    },
    "models": { "type": "array", "items": { "type": "string" } },
    "restricted": { "type": "boolean" }
  },
  "required": [ "connectionId", "provider", "models", "restricted" ],
  "additionalProperties": false
}

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

Codes d’erreur — llm.connection_not_found. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.

GET /api/v1/admin/llm/connections ​

Lister toutes les connexions d’IA

Accès — Session de membre ou clé d’API portant admin:read (rôle administrateur requis).

Réponses

200 — Les connexions.

ChampTypeRequis
connectionsobject[]oui
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "connections": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "provider": {
            "type": "string",
            "enum": [
              "anthropic",
              "openai",
              "mistral",
              "openrouter",
              "ollama",
              "openai_compatible"
            ]
          },
          "label": { "type": "string" },
          "isDefault": { "type": "boolean" },
          "hasApiKey": { "type": "boolean" },
          "apiKeyHint": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "baseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "enabledModels": {
            "anyOf": [
              { "type": "array", "items": { "type": "string" } },
              { "type": "null" }
            ]
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
          }
        },
        "required": [
          "id",
          "provider",
          "label",
          "isDefault",
          "hasApiKey",
          "apiKeyHint",
          "baseUrl",
          "enabledModels",
          "updatedAt"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [ "connections" ],
  "additionalProperties": false
}

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

POST /api/v1/admin/llm/connections ​

Ajouter une connexion d’IA

Ajoute une clé à côté des autres pour un fournisseur (le PUT du fournisseur remplace la connexion par défaut). Le libellé est unique sur l’instance. Journalisée dans l’audit, jamais avec la clé.

Accès — Session de membre ou clé d’API portant admin:write (rôle administrateur requis).

Corps de la requête (application/json)

ChampTypeRequis
provider"anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible"oui
labelstringoui
apiKeystringnon
baseUrlstring (uri)non
withoutApiKeybooleannon
enabledModelsstring[]non
makeDefaultbooleannon
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "provider": {
      "type": "string",
      "enum": [
        "anthropic",
        "openai",
        "mistral",
        "openrouter",
        "ollama",
        "openai_compatible"
      ]
    },
    "label": { "type": "string", "minLength": 1, "maxLength": 80 },
    "apiKey": { "type": "string", "minLength": 1, "maxLength": 512 },
    "baseUrl": { "type": "string", "maxLength": 2048, "format": "uri" },
    "withoutApiKey": { "type": "boolean" },
    "enabledModels": {
      "minItems": 1,
      "maxItems": 64,
      "type": "array",
      "items": { "type": "string", "minLength": 1, "maxLength": 128 }
    },
    "makeDefault": { "type": "boolean" }
  },
  "required": [ "provider", "label" ]
}

Réponses

201 — L’état de la connexion.

ChampTypeRequis
idstring (uuid)oui
provider"anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible"oui
labelstringoui
isDefaultbooleanoui
hasApiKeybooleanoui
apiKeyHintstring | nulloui
baseUrlstring | nulloui
enabledModelsstring[] | nulloui
updatedAtstring (date-time)oui
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid",
      "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
    },
    "provider": {
      "type": "string",
      "enum": [
        "anthropic",
        "openai",
        "mistral",
        "openrouter",
        "ollama",
        "openai_compatible"
      ]
    },
    "label": { "type": "string" },
    "isDefault": { "type": "boolean" },
    "hasApiKey": { "type": "boolean" },
    "apiKeyHint": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
    "baseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
    "enabledModels": {
      "anyOf": [ { "type": "array", "items": { "type": "string" } }, { "type": "null" } ]
    },
    "updatedAt": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
    }
  },
  "required": [
    "id",
    "provider",
    "label",
    "isDefault",
    "hasApiKey",
    "apiKeyHint",
    "baseUrl",
    "enabledModels",
    "updatedAt"
  ],
  "additionalProperties": false
}

400 — La requête ne respecte pas son schéma.

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

Cette opération accepte un en-tête Idempotency-Key : rejouer la même requête avec la même clé rend la réponse d’origine au lieu d’agir deux fois. Voir Idempotence.

Codes d’erreur — request.bad_request, llm.encryption_disabled, llm.invalid_base_url, llm.api_key_required, llm.connection_label_taken. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.

PUT /api/v1/admin/llm/connections/{id} ​

Modifier une connexion d’IA

Renommer, remplacer la clé, changer l’URL ou les modèles autorisés. Une apiKey absente conserve celle en place.

Accès — Session de membre ou clé d’API portant admin:write (rôle administrateur requis).

Paramètres

NomOùTypeRequis
idcheminstringoui

Corps de la requête (application/json)

ChampTypeRequis
labelstringnon
apiKeystringnon
baseUrlstring (uri)non
withoutApiKeybooleannon
enabledModelsstring[]non
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "label": { "type": "string", "minLength": 1, "maxLength": 80 },
    "apiKey": { "type": "string", "minLength": 1, "maxLength": 512 },
    "baseUrl": { "type": "string", "maxLength": 2048, "format": "uri" },
    "withoutApiKey": { "type": "boolean" },
    "enabledModels": {
      "minItems": 1,
      "maxItems": 64,
      "type": "array",
      "items": { "type": "string", "minLength": 1, "maxLength": 128 }
    }
  }
}

Réponses

200 — L’état de la connexion.

ChampTypeRequis
idstring (uuid)oui
provider"anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible"oui
labelstringoui
isDefaultbooleanoui
hasApiKeybooleanoui
apiKeyHintstring | nulloui
baseUrlstring | nulloui
enabledModelsstring[] | nulloui
updatedAtstring (date-time)oui

Même schéma que POST /api/v1/admin/llm/connections.

400 — La requête ne respecte pas son schéma.

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

Codes d’erreur — request.bad_request, llm.connection_not_found, llm.connection_label_taken, llm.invalid_base_url, llm.encryption_disabled. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.

DELETE /api/v1/admin/llm/connections/{id} ​

Retirer une connexion d’IA

Refusée (409) tant qu’un workflow publié la référence, sauf force=true ; refusée quand c’est la connexion par défaut et que d’autres existent. 204 même si elle n’existait plus.

Accès — Session de membre ou clé d’API portant admin:write (rôle administrateur requis).

Paramètres

NomOùTypeRequis
idcheminstringoui
forcerequêtestringnon

Réponses

204 — Retirée.

400 — La requête ne respecte pas son schéma.

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

Codes d’erreur — llm.connection_in_use, llm.connection_is_default. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.

POST /api/v1/admin/llm/connections/{id}/default ​

Faire d’une connexion d’IA la connexion par défaut

La connexion devient celle par défaut de son fournisseur ; l’ancienne cède la place. Journalisé dans l’audit.

Accès — Session de membre ou clé d’API portant admin:write (rôle administrateur requis).

Paramètres

NomOùTypeRequis
idcheminstringoui

Réponses

200 — L’état de la connexion.

ChampTypeRequis
idstring (uuid)oui
provider"anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible"oui
labelstringoui
isDefaultbooleanoui
hasApiKeybooleanoui
apiKeyHintstring | nulloui
baseUrlstring | nulloui
enabledModelsstring[] | nulloui
updatedAtstring (date-time)oui

Même schéma que POST /api/v1/admin/llm/connections.

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

Codes d’erreur — llm.connection_not_found. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.

GET /api/v1/admin/llm-providers ​

Décrire tous les fournisseurs d’IA

L’état de chaque fournisseur (configuré ou non, catalogue, connexions) et les défauts résolus, en un appel.

Accès — Session de membre ou clé d’API portant admin:read (rôle administrateur requis).

Réponses

200 — Les fournisseurs.

ChampTypeRequis
providersobject[]oui
defaultsobjectoui
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "providers": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "provider": {
            "type": "string",
            "enum": [
              "anthropic",
              "openai",
              "mistral",
              "openrouter",
              "ollama",
              "openai_compatible"
            ]
          },
          "configured": { "type": "boolean" },
          "hasApiKey": { "type": "boolean" },
          "apiKeyHint": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "baseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "preset": {
            "type": "object",
            "properties": {
              "provider": {
                "type": "string",
                "enum": [
                  "anthropic",
                  "openai",
                  "mistral",
                  "openrouter",
                  "ollama",
                  "openai_compatible"
                ]
              },
              "defaultBaseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "baseUrlPolicy": { "type": "string", "enum": [ "fixed", "preset", "required" ] },
              "apiKey": { "type": "string", "enum": [ "required", "optional" ] },
              "jsonMode": {
                "type": "string",
                "enum": [ "tool", "json_schema", "json_object", "none" ]
              },
              "supportsModelDiscovery": { "type": "boolean" },
              "supportsTools": { "type": "boolean" },
              "supportsVision": { "type": "boolean" },
              "docsUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
            },
            "required": [
              "provider",
              "defaultBaseUrl",
              "baseUrlPolicy",
              "apiKey",
              "jsonMode",
              "supportsModelDiscovery",
              "supportsTools",
              "supportsVision",
              "docsUrl"
            ],
            "additionalProperties": false
          },
          "models": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string" },
                "inputUsdPerMTok": { "anyOf": [ { "type": "number" }, { "type": "null" } ] },
                "outputUsdPerMTok": { "anyOf": [ { "type": "number" }, { "type": "null" } ] },
                "retentionCovered": { "type": "boolean" },
                "supportsTemperature": { "type": "boolean" },
                "enabled": { "type": "boolean" }
              },
              "required": [
                "id",
                "inputUsdPerMTok",
                "outputUsdPerMTok",
                "retentionCovered",
                "supportsTemperature",
                "enabled"
              ],
              "additionalProperties": false
            }
          },
          "enabledModels": {
            "anyOf": [
              { "type": "array", "items": { "type": "string" } },
              { "type": "null" }
            ]
          },
          "defaults": {
            "type": "object",
            "properties": {
              "classify": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "compose": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "extract": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "general": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
              "analyzer": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
            },
            "required": [ "classify", "compose", "extract", "general", "analyzer" ],
            "additionalProperties": false
          },
          "updatedAt": {
            "anyOf": [
              {
                "type": "string",
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
              },
              { "type": "null" }
            ]
          },
          "connections": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": {
                  "type": "string",
                  "format": "uuid",
                  "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
                },
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "label": { "type": "string" },
                "isDefault": { "type": "boolean" },
                "hasApiKey": { "type": "boolean" },
                "apiKeyHint": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                "baseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
                "enabledModels": {
                  "anyOf": [
                    { "type": "array", "items": { "type": "string" } },
                    { "type": "null" }
                  ]
                },
                "updatedAt": {
                  "type": "string",
                  "format": "date-time",
                  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
                }
              },
              "required": [
                "id",
                "provider",
                "label",
                "isDefault",
                "hasApiKey",
                "apiKeyHint",
                "baseUrl",
                "enabledModels",
                "updatedAt"
              ],
              "additionalProperties": false
            }
          }
        },
        "required": [
          "provider",
          "configured",
          "hasApiKey",
          "apiKeyHint",
          "baseUrl",
          "preset",
          "models",
          "enabledModels",
          "defaults",
          "updatedAt",
          "connections"
        ],
        "additionalProperties": false
      }
    },
    "defaults": {
      "type": "object",
      "properties": {
        "classify": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "model": { "type": "string" }
              },
              "required": [ "provider", "model" ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        },
        "compose": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "model": { "type": "string" }
              },
              "required": [ "provider", "model" ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        },
        "extract": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "model": { "type": "string" }
              },
              "required": [ "provider", "model" ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        },
        "general": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "model": { "type": "string" }
              },
              "required": [ "provider", "model" ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        },
        "analyzer": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "model": { "type": "string" }
              },
              "required": [ "provider", "model" ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        },
        "assistant": {
          "anyOf": [
            {
              "type": "object",
              "properties": {
                "provider": {
                  "type": "string",
                  "enum": [
                    "anthropic",
                    "openai",
                    "mistral",
                    "openrouter",
                    "ollama",
                    "openai_compatible"
                  ]
                },
                "model": { "type": "string" }
              },
              "required": [ "provider", "model" ],
              "additionalProperties": false
            },
            { "type": "null" }
          ]
        }
      },
      "required": [ "classify", "compose", "extract", "general", "analyzer", "assistant" ],
      "additionalProperties": false
    }
  },
  "required": [ "providers", "defaults" ],
  "additionalProperties": false
}

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

GET /api/v1/admin/llm-providers/{provider} ​

Décrire un fournisseur d’IA

Rendu même sans clé enregistrée (configured: false), catalogue et défauts compris.

Accès — Session de membre ou clé d’API portant admin:read (rôle administrateur requis).

Paramètres

NomOùTypeRequis
providercheminstringoui

Réponses

200 — L’état du fournisseur.

ChampTypeRequis
provider"anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible"oui
configuredbooleanoui
hasApiKeybooleanoui
apiKeyHintstring | nulloui
baseUrlstring | nulloui
presetobjectoui
modelsobject[]oui
enabledModelsstring[] | nulloui
defaultsobjectoui
updatedAtstring (date-time) | nulloui
connectionsobject[]oui
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "provider": {
      "type": "string",
      "enum": [
        "anthropic",
        "openai",
        "mistral",
        "openrouter",
        "ollama",
        "openai_compatible"
      ]
    },
    "configured": { "type": "boolean" },
    "hasApiKey": { "type": "boolean" },
    "apiKeyHint": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
    "baseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
    "preset": {
      "type": "object",
      "properties": {
        "provider": {
          "type": "string",
          "enum": [
            "anthropic",
            "openai",
            "mistral",
            "openrouter",
            "ollama",
            "openai_compatible"
          ]
        },
        "defaultBaseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
        "baseUrlPolicy": { "type": "string", "enum": [ "fixed", "preset", "required" ] },
        "apiKey": { "type": "string", "enum": [ "required", "optional" ] },
        "jsonMode": {
          "type": "string",
          "enum": [ "tool", "json_schema", "json_object", "none" ]
        },
        "supportsModelDiscovery": { "type": "boolean" },
        "supportsTools": { "type": "boolean" },
        "supportsVision": { "type": "boolean" },
        "docsUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
      },
      "required": [
        "provider",
        "defaultBaseUrl",
        "baseUrlPolicy",
        "apiKey",
        "jsonMode",
        "supportsModelDiscovery",
        "supportsTools",
        "supportsVision",
        "docsUrl"
      ],
      "additionalProperties": false
    },
    "models": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "inputUsdPerMTok": { "anyOf": [ { "type": "number" }, { "type": "null" } ] },
          "outputUsdPerMTok": { "anyOf": [ { "type": "number" }, { "type": "null" } ] },
          "retentionCovered": { "type": "boolean" },
          "supportsTemperature": { "type": "boolean" },
          "enabled": { "type": "boolean" }
        },
        "required": [
          "id",
          "inputUsdPerMTok",
          "outputUsdPerMTok",
          "retentionCovered",
          "supportsTemperature",
          "enabled"
        ],
        "additionalProperties": false
      }
    },
    "enabledModels": {
      "anyOf": [ { "type": "array", "items": { "type": "string" } }, { "type": "null" } ]
    },
    "defaults": {
      "type": "object",
      "properties": {
        "classify": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
        "compose": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
        "extract": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
        "general": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
        "analyzer": { "anyOf": [ { "type": "string" }, { "type": "null" } ] }
      },
      "required": [ "classify", "compose", "extract", "general", "analyzer" ],
      "additionalProperties": false
    },
    "updatedAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
        },
        { "type": "null" }
      ]
    },
    "connections": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "format": "uuid",
            "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"
          },
          "provider": {
            "type": "string",
            "enum": [
              "anthropic",
              "openai",
              "mistral",
              "openrouter",
              "ollama",
              "openai_compatible"
            ]
          },
          "label": { "type": "string" },
          "isDefault": { "type": "boolean" },
          "hasApiKey": { "type": "boolean" },
          "apiKeyHint": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "baseUrl": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "enabledModels": {
            "anyOf": [
              { "type": "array", "items": { "type": "string" } },
              { "type": "null" }
            ]
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
          }
        },
        "required": [
          "id",
          "provider",
          "label",
          "isDefault",
          "hasApiKey",
          "apiKeyHint",
          "baseUrl",
          "enabledModels",
          "updatedAt"
        ],
        "additionalProperties": false
      }
    }
  },
  "required": [
    "provider",
    "configured",
    "hasApiKey",
    "apiKeyHint",
    "baseUrl",
    "preset",
    "models",
    "enabledModels",
    "defaults",
    "updatedAt",
    "connections"
  ],
  "additionalProperties": false
}

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

Codes d’erreur — llm.unsupported_provider. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.

PUT /api/v1/admin/llm-providers/{provider} ​

Configurer un fournisseur d’IA

Pose ou remplace la connexion par défaut du fournisseur. La clé est obligatoire à la première configuration et optionnelle ensuite (absente, elle est conservée). Journalisé dans l’audit, jamais avec la clé.

Accès — Session de membre ou clé d’API portant admin:write (rôle administrateur requis).

Paramètres

NomOùTypeRequis
providercheminstringoui

Corps de la requête (application/json)

ChampTypeRequis
apiKeystringnon
baseUrlstring (uri)non
withoutApiKeybooleannon
enabledModelsstring[]non
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "apiKey": { "type": "string", "minLength": 1, "maxLength": 512 },
    "baseUrl": { "type": "string", "maxLength": 2048, "format": "uri" },
    "withoutApiKey": { "type": "boolean" },
    "enabledModels": {
      "minItems": 1,
      "maxItems": 64,
      "type": "array",
      "items": { "type": "string", "minLength": 1, "maxLength": 128 }
    }
  }
}

Réponses

200 — L’état du fournisseur.

ChampTypeRequis
provider"anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible"oui
configuredbooleanoui
hasApiKeybooleanoui
apiKeyHintstring | nulloui
baseUrlstring | nulloui
presetobjectoui
modelsobject[]oui
enabledModelsstring[] | nulloui
defaultsobjectoui
updatedAtstring (date-time) | nulloui
connectionsobject[]oui

Même schéma que GET /api/v1/admin/llm-providers/{provider}.

400 — La requête ne respecte pas son schéma.

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

Codes d’erreur — llm.unsupported_provider, request.bad_request, llm.encryption_disabled, llm.invalid_base_url, llm.api_key_required, llm.model_not_allowed. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.

DELETE /api/v1/admin/llm-providers/{provider} ​

Retirer la clé d’un fournisseur d’IA

204 même si rien n’était enregistré. La ligne d’audit dit si quelque chose a réellement été retiré.

Accès — Session de membre ou clé d’API portant admin:write (rôle administrateur requis).

Paramètres

NomOùTypeRequis
providercheminstringoui

Réponses

204 — Retirée.

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

Codes d’erreur — llm.unsupported_provider. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.

POST /api/v1/admin/llm-providers/{provider}/test ​

Tester un fournisseur d’IA

Un vrai appel, minimal. Le verdict est dans le corps avec un 200 : une clé refusée est le résultat, pas une erreur de cette API. connectionId désigne la connexion à éprouver ; absent, celle par défaut.

Accès — Session de membre ou clé d’API portant admin:write (rôle administrateur requis).

Paramètres

NomOùTypeRequis
providercheminstringoui
connectionIdrequêtestringnon

Réponses

200 — Le verdict.

ChampTypeRequis
provider"anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible"oui
okbooleanoui
modelstringoui
latencyMsintegeroui
errorobjectnon
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "provider": {
      "type": "string",
      "enum": [
        "anthropic",
        "openai",
        "mistral",
        "openrouter",
        "ollama",
        "openai_compatible"
      ]
    },
    "ok": { "type": "boolean" },
    "model": { "type": "string" },
    "latencyMs": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
    "error": {
      "type": "object",
      "properties": { "code": { "type": "string" }, "message": { "type": "string" } },
      "required": [ "code", "message" ],
      "additionalProperties": false
    }
  },
  "required": [ "provider", "ok", "model", "latencyMs" ],
  "additionalProperties": false
}

400 — La requête ne respecte pas son schéma.

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

Codes d’erreur — llm.unsupported_provider, llm.provider_not_configured, llm.connection_not_found, llm.connection_provider_mismatch. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.

GET /api/v1/admin/llm/providers/{provider}/models ​

Découvrir les modèles qu’une clé voit

Le demande au fournisseur lui-même, avec la clé de l’instance (cache de dix minutes). 200 même en échec, comme le test. Restent en erreur HTTP un fournisseur inconnu, un qui ne liste pas ses modèles, l’absence de clé, et une connexion d’un autre fournisseur.

Accès — Session de membre ou clé d’API portant admin:read (rôle administrateur requis).

Paramètres

NomOùTypeRequis
providercheminstringoui
connectionIdrequêtestringnon

Réponses

200 — Les modèles découverts.

ChampTypeRequis
provider"anthropic" | "openai" | "mistral" | "openrouter" | "ollama" | "openai_compatible"oui
okbooleanoui
modelsobject[]oui
cachedbooleanoui
fetchedAtstring (date-time) | nulloui
errorobjectnon
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "provider": {
      "type": "string",
      "enum": [
        "anthropic",
        "openai",
        "mistral",
        "openrouter",
        "ollama",
        "openai_compatible"
      ]
    },
    "ok": { "type": "boolean" },
    "models": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "id": { "type": "string" },
          "label": { "anyOf": [ { "type": "string" }, { "type": "null" } ] },
          "contextLength": {
            "anyOf": [
              {
                "type": "integer",
                "exclusiveMinimum": 0,
                "maximum": 9007199254740991
              },
              { "type": "null" }
            ]
          },
          "inputUsdPerMTok": { "anyOf": [ { "type": "number" }, { "type": "null" } ] },
          "outputUsdPerMTok": { "anyOf": [ { "type": "number" }, { "type": "null" } ] },
          "known": { "type": "boolean" }
        },
        "required": [
          "id",
          "label",
          "contextLength",
          "inputUsdPerMTok",
          "outputUsdPerMTok",
          "known"
        ],
        "additionalProperties": false
      }
    },
    "cached": { "type": "boolean" },
    "fetchedAt": {
      "anyOf": [
        {
          "type": "string",
          "format": "date-time",
          "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
        },
        { "type": "null" }
      ]
    },
    "error": {
      "type": "object",
      "properties": { "code": { "type": "string" }, "message": { "type": "string" } },
      "required": [ "code", "message" ],
      "additionalProperties": false
    }
  },
  "required": [ "provider", "ok", "models", "cached", "fetchedAt" ],
  "additionalProperties": false
}

400 — La requête ne respecte pas son schéma.

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

Codes d’erreur — llm.unsupported_provider, llm.discovery_not_supported, llm.provider_not_configured, llm.connection_provider_mismatch. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.

GET /api/v1/admin/llm/defaults ​

Lire le routage d’IA par usage

Une ligne par créneau : le choix de l’administrateur, ce que l’usage obtiendra réellement, et si le choix est devenu inapplicable (clé retirée, modèle décoché).

Accès — Session de membre ou clé d’API portant admin:read (rôle administrateur requis).

Réponses

200 — Le routage.

ChampTypeRequis
entriesobject[]oui
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "entries": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "purpose": {
            "type": "string",
            "enum": [
              "default",
              "classify",
              "compose",
              "extract",
              "general",
              "analyzer",
              "assistant"
            ]
          },
          "override": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "purpose": {
                    "type": "string",
                    "enum": [
                      "default",
                      "classify",
                      "compose",
                      "extract",
                      "general",
                      "analyzer",
                      "assistant"
                    ]
                  },
                  "provider": {
                    "type": "string",
                    "enum": [
                      "anthropic",
                      "openai",
                      "mistral",
                      "openrouter",
                      "ollama",
                      "openai_compatible"
                    ]
                  },
                  "model": {
                    "anyOf": [
                      { "type": "string", "minLength": 1, "maxLength": 128 },
                      { "type": "null" }
                    ]
                  },
                  "temperature": {
                    "anyOf": [
                      { "type": "number", "minimum": 0, "maximum": 2 },
                      { "type": "null" }
                    ]
                  },
                  "maxOutputTokens": {
                    "anyOf": [
                      {
                        "type": "integer",
                        "exclusiveMinimum": 0,
                        "maximum": 200000
                      },
                      { "type": "null" }
                    ]
                  },
                  "updatedAt": {
                    "type": "string",
                    "format": "date-time",
                    "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
                  }
                },
                "required": [
                  "purpose",
                  "provider",
                  "model",
                  "temperature",
                  "maxOutputTokens",
                  "updatedAt"
                ],
                "additionalProperties": false
              },
              { "type": "null" }
            ]
          },
          "resolved": {
            "anyOf": [
              {
                "type": "object",
                "properties": {
                  "provider": {
                    "type": "string",
                    "enum": [
                      "anthropic",
                      "openai",
                      "mistral",
                      "openrouter",
                      "ollama",
                      "openai_compatible"
                    ]
                  },
                  "model": { "type": "string" }
                },
                "required": [ "provider", "model" ],
                "additionalProperties": false
              },
              { "type": "null" }
            ]
          },
          "overrideUnavailable": { "type": "boolean" }
        },
        "required": [ "purpose", "override", "resolved", "overrideUnavailable" ],
        "additionalProperties": false
      }
    }
  },
  "required": [ "entries" ],
  "additionalProperties": false
}

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

PUT /api/v1/admin/llm/defaults ​

Régler le routage d’IA par usage

Partiel : les créneaux absents ne bougent pas ; provider: null efface la ligne. Un choix sur un fournisseur sans clé est accepté et signalé comme pas encore applicable. Journalisé dans l’audit.

Accès — Session de membre ou clé d’API portant admin:write (rôle administrateur requis).

Corps de la requête (application/json)

ChampTypeRequis
defaultsobject[]oui
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "defaults": {
      "minItems": 1,
      "maxItems": 7,
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "purpose": {
            "type": "string",
            "enum": [
              "default",
              "classify",
              "compose",
              "extract",
              "general",
              "analyzer",
              "assistant"
            ]
          },
          "provider": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "anthropic",
                  "openai",
                  "mistral",
                  "openrouter",
                  "ollama",
                  "openai_compatible"
                ]
              },
              { "type": "null" }
            ]
          },
          "model": {
            "anyOf": [
              { "type": "string", "minLength": 1, "maxLength": 128 },
              { "type": "null" }
            ]
          },
          "temperature": {
            "anyOf": [
              { "type": "number", "minimum": 0, "maximum": 2 },
              { "type": "null" }
            ]
          },
          "maxOutputTokens": {
            "anyOf": [
              { "type": "integer", "exclusiveMinimum": 0, "maximum": 200000 },
              { "type": "null" }
            ]
          }
        },
        "required": [ "purpose", "provider", "model" ]
      }
    }
  },
  "required": [ "defaults" ]
}

Réponses

200 — Le routage, après écriture.

ChampTypeRequis
entriesobject[]oui

Même schéma que GET /api/v1/admin/llm/defaults.

400 — La requête ne respecte pas son schéma.

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

Codes d’erreur — request.bad_request. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.

GET /api/v1/admin/llm-usage ​

Lire le rapport d’usage de l’IA

Agrégats par jour, fournisseur, modèle et contexte sur [from, to). to vaut maintenant par défaut, from trente jours plus tôt ; la fenêtre ne dépasse pas un an. total.costUsd est null quand aucune ligne n’avait de tarif.

Accès — Session de membre ou clé d’API portant admin:read (rôle administrateur requis).

Paramètres

NomOùTypeRequis
fromrequêtestringnon
torequêtestringnon

Réponses

200 — L’usage.

ChampTypeRequis
fromstring (date-time)oui
tostring (date-time)oui
rowsobject[]oui
totalobjectoui
Schéma JSON
json
{
  "type": "object",
  "properties": {
    "from": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
    },
    "to": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$"
    },
    "rows": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "day": { "type": "string" },
          "provider": {
            "type": "string",
            "enum": [
              "anthropic",
              "openai",
              "mistral",
              "openrouter",
              "ollama",
              "openai_compatible"
            ]
          },
          "model": { "type": "string" },
          "context": {
            "type": "string",
            "enum": [ "execution", "eval", "analyzer", "assistant" ]
          },
          "calls": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
          "inputTokens": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
          "outputTokens": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
          "costUsd": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }
        },
        "required": [
          "day",
          "provider",
          "model",
          "context",
          "calls",
          "inputTokens",
          "outputTokens",
          "costUsd"
        ],
        "additionalProperties": false
      }
    },
    "total": {
      "type": "object",
      "properties": {
        "calls": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
        "inputTokens": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
        "outputTokens": { "type": "integer", "minimum": 0, "maximum": 9007199254740991 },
        "costUsd": { "anyOf": [ { "type": "number" }, { "type": "null" } ] }
      },
      "required": [ "calls", "inputTokens", "outputTokens", "costUsd" ],
      "additionalProperties": false
    }
  },
  "required": [ "from", "to", "rows", "total" ],
  "additionalProperties": false
}

400 — La requête ne respecte pas son schéma.

401 — Aucune session ni clé d’API valide (auth.unauthenticated).

403 — Refusé : rôle insuffisant (auth.forbidden), portée manquante (api_key.scope_missing, details.required la nomme) ou route fermée aux clés (api_key.session_required).

429 — La clé d’API dépasse son débit (api_key.rate_limited) ; Retry-After dit quand réessayer.

Codes d’erreur — request.bad_request. Les codes communs (request.bad_request, auth.unauthenticated, api_key.scope_missing, api_key.rate_limited…) s’appliquent à toutes les routes ; voir Erreurs.