> ## Documentation Index
> Fetch the complete documentation index at: https://docs.zelinqa.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Comprendre le tour précédent et proposer les questions suivantes

> Route principale du runtime. En un seul appel, le moteur résout la question
réellement posée et son outcome, applique les mises à jour du client, réduit
l'état, recalcule les progressions, puis classe les questions éligibles.

Le client n'a pas à savoir quelle candidate son agent a utilisée : si
`decision_id`, `question_id` ou `outcome` sont absents, Zelinqa les résout depuis
la décision en attente, le texte de l'agent, la réponse structurée et le
contexte. Une reformulation de la question par l'agent hôte reste
rattachable.

Au premier appel d'une session neuve, `previous_turn` est absent.




## OpenAPI

````yaml /openapi.fr.yaml post /v1/sessions/{session_id}/next
openapi: 3.1.0
info:
  title: Zelinqa — API V1
  version: 1.0.0
  summary: >-
    Contrat public du runtime Zelinqa et de la configuration des bases de
    questions.
  description: >-
    Contrat public de l'API Zelinqa V1 sur https://api.zelinqa.ai. La clé API
    identifie le domaine et ses droits. Les mutations utilisent Idempotency-Key
    ; celles d'une session existante utilisent aussi state_version. Les sessions
    restent attachées à leur version publiée.
  contact:
    name: Zelinqa
    url: https://docs.zelinqa.ai
  license:
    name: Proprietary — Zelinqa SAS
    identifier: LicenseRef-Zelinqa-Proprietary
servers:
  - url: https://api.zelinqa.ai
    description: API publique Zelinqa
security:
  - ApiKeyAuth: []
tags:
  - name: sessions
    description: >
      Cycle de vie d'une conversation Zelinqa : création, sélection de la
      question

      suivante, mises à jour hors tour, reprise et retour de résultat.
  - name: configuration
    description: |
      Lecture et modification de la configuration éditoriale d'un domaine, puis
      publication asynchrone de l'artefact moteur.
paths:
  /v1/sessions/{session_id}/next:
    post:
      tags:
        - sessions
      summary: Comprendre le tour précédent et proposer les questions suivantes
      description: >
        Route principale du runtime. En un seul appel, le moteur résout la
        question

        réellement posée et son outcome, applique les mises à jour du client,
        réduit

        l'état, recalcule les progressions, puis classe les questions éligibles.


        Le client n'a pas à savoir quelle candidate son agent a utilisée : si

        `decision_id`, `question_id` ou `outcome` sont absents, Zelinqa les
        résout depuis

        la décision en attente, le texte de l'agent, la réponse structurée et le

        contexte. Une reformulation de la question par l'agent hôte reste

        rattachable.


        Au premier appel d'une session neuve, `previous_turn` est absent.
      operationId: nextQuestions
      parameters:
        - $ref: '#/components/parameters/SessionId'
        - $ref: '#/components/parameters/IdempotencyKey'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/NextRequest'
            examples:
              premier_tour:
                summary: Premier appel, aucune question encore posée
                value:
                  state_version: 0
              tour_avec_reponse_libre:
                summary: L'agent a reformulé la question ; aucun identifiant fourni
                value:
                  state_version: 3
                  previous_turn:
                    assistant_text: >-
                      Et côté budget, vous vous situez plutôt dans quelle
                      fourchette ?
                    user_text: Autour de 2 000 euros, je ne veux pas dépasser 2 500.
                    message_id: msg_18
              tour_avec_reponse_structuree:
                summary: Réponse à choix, mapping déterministe sans appel LLM
                value:
                  state_version: 4
                  previous_turn:
                    decision_id: dec_7f2a
                    question_id: q_style
                    outcome: asked_answered
                    structured_answer:
                      choice_ids:
                        - choice_contemporain
              rattrapage_de_contexte:
                summary: >-
                  Messages échangés sans passer par Zelinqa, plus une donnée
                  connue du client
                value:
                  state_version: 5
                  context_update:
                    mode: messages
                    messages:
                      - role: user
                        message_id: msg_21
                        text: >-
                          En fait nous avons deux chats qui montent sur le
                          canapé.
                  client_updates:
                    data:
                      - id: has_pets
                        value: true
              selection_contrainte:
                summary: L'agent veut deux questions fermées dans une dimension précis
                value:
                  state_version: 6
                  selection:
                    candidate_count: 2
                    allowed_question_types:
                      - single_choice
                      - multiple_choice
                    dimensions:
                      ids:
                        - so_besoin
                      mode: restrict
      responses:
        '200':
          description: Décision calculée, ou arrêt explicite si aucune question n'existe.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NextResponse'
              examples:
                decision_normale:
                  $ref: '#/components/examples/DecisionNormale'
                max_turns_atteint:
                  $ref: '#/components/examples/DecisionApresMaxTurns'
                arret_sans_question:
                  $ref: '#/components/examples/ArretSansQuestion'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/InsufficientScope'
        '404':
          $ref: '#/components/responses/UnknownSession'
        '409':
          $ref: '#/components/responses/SessionMutationConflict'
        '410':
          $ref: '#/components/responses/CompiledArtifactUnavailable'
        '422':
          $ref: '#/components/responses/NextUnprocessable'
        '503':
          $ref: '#/components/responses/IdempotencyContention'
components:
  parameters:
    SessionId:
      name: session_id
      in: path
      required: true
      schema:
        type: string
        minLength: 1
        maxLength: 128
      description: Identifiant opaque de session attribué par Zelinqa à la création.
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema:
        type: string
        minLength: 8
        maxLength: 128
      description: >
        Clé unique par mutation logique, choisie par l'appelant.


        Même clé et même corps renvoient exactement la réponse d'origine, sans

        rejouer l'effet : aucun retry ne double `turn_count`, un outcome, un

        événement, un feedback ou une publication. Même clé avec un corps
        différent

        produit `409 idempotency_key_reused`.


        Les enregistrements sont isolés par tenant et par opération, et sont
        purgés

        après **24 heures**. Passé ce délai, la même clé est traitée comme
        neuve.
  schemas:
    NextRequest:
      type: object
      additionalProperties: false
      required:
        - state_version
      properties:
        state_version:
          type: integer
          minimum: 0
          description: >-
            Version lue par le client avant cette mutation, en contrôle
            optimiste.
        previous_turn:
          $ref: '#/components/schemas/PreviousTurn'
        context_update:
          $ref: '#/components/schemas/ContextUpdate'
        client_updates:
          $ref: '#/components/schemas/ClientUpdates'
        selection:
          $ref: '#/components/schemas/SelectionOptions'
    NextResponse:
      type: object
      additionalProperties: false
      required:
        - request_id
        - session_id
        - decision_id
        - action
        - stop_reason
        - candidates
        - progress
        - turn_count
        - turns_remaining
        - warnings
        - degraded
        - degraded_reasons
        - versions
      description: >
        Aucun score de sélection n'est exposé : le classement est porté par
        `rank`.

        Un score brut n'a pas de sens métier hors du moteur et ne doit pas
        devenir

        une dépendance des intégrations.


        **Invariants garantis par le moteur**, non exprimés en JSON Schema pour

        rester générables en SDK :


        - `action: ask` implique `decision_id` non nul, `stop_reason` nul et au
          moins un candidat ;
        - `action: stop` implique `decision_id` nul, `stop_reason` renseigné et
          `candidates` vide.
      properties:
        request_id:
          type: string
        session_id:
          type: string
        decision_id:
          type:
            - string
            - 'null'
          description: >-
            Non nul quand `action` vaut `ask`. À renvoyer dans le tour suivant
            si disponible.
        action:
          type: string
          enum:
            - ask
            - stop
          description: >
            `stop` n'est produit **que** lorsqu'aucune question identifiable

            n'existe. Une limite de tours atteinte, un objectif déjà atteint ou
            une

            éligibilité normale vide ne coupent pas la conversation : le moteur

            propose encore la meilleure question disponible et le signale dans

            `warnings`. La décision d'arrêter appartient à l'appelant.
        stop_reason:
          oneOf:
            - $ref: '#/components/schemas/StopReason'
            - type: 'null'
        candidates:
          type: array
          maxItems: 10
          items:
            $ref: '#/components/schemas/Candidate'
          description: |
            Ordonnées par pertinence décroissante, rang 1 en premier. Lorsque
            plusieurs candidats sont demandés, le moteur simule la couverture
            apportée par le premier avant de choisir le suivant : deux variantes
            demandant la même information ne sont pas retournées ensemble.
        progress:
          $ref: '#/components/schemas/ProgressView'
        turn_count:
          type: integer
          minimum: 0
        turns_remaining:
          type: integer
          minimum: 0
          description: Vaut `0` lorsque la limite souple est atteinte ou dépassée.
        warnings:
          type: array
          uniqueItems: true
          items:
            $ref: '#/components/schemas/SelectionWarning'
        degraded:
          type: boolean
          description: >-
            Vrai lorsque la compréhension du tour a été effectuée en mode
            réduit.
        degraded_reasons:
          type: array
          uniqueItems: true
          items:
            type: string
            enum:
              - missing_user_text
              - summary_only_context
              - semantic_service_unavailable
              - unresolved_previous_turn
        versions:
          $ref: '#/components/schemas/VersionInfo'
    PreviousTurn:
      type: object
      additionalProperties: false
      minProperties: 1
      description: >-
        Observation du dernier échange ; absente au premier appel d'une session
        neuve. Les identifiants de décision et de question sont facultatifs et
        ne doivent pas être inventés. Pour une question ouverte répondue,
        user_text non vide est obligatoire, sauf si outcome vaut explicitement
        asked_no_answer ou refused. Sinon, l'API renvoie 422
        invalid_previous_turn avant toute analyse par modèle. Les questions
        fermées et semi-ouvertes acceptent des choice_ids seuls ; une
        semi-ouverte peut aussi porter free_text. Ni context_update ni
        client_updates ne remplacent le texte requis.
      properties:
        decision_id:
          type: string
          minLength: 1
          maxLength: 128
        question_id:
          type: string
          minLength: 1
          maxLength: 128
        outcome:
          $ref: '#/components/schemas/QuestionOutcome'
        assistant_text:
          type: string
          minLength: 1
          maxLength: 8000
          description: >-
            Question ou message réellement envoyé par l'agent hôte,
            éventuellement reformulé.
        user_text:
          type: string
          minLength: 1
          maxLength: 8000
          description: >-
            Réponse réellement donnée par la personne. Obligatoire pour une
            question ouverte répondue, sauf outcome asked_no_answer ou refused ;
            facultative pour une question fermée ou semi-ouverte. Un texte
            fourni peut être analysé par le moteur.
        structured_answer:
          $ref: '#/components/schemas/StructuredAnswer'
        message_id:
          type: string
          minLength: 1
          maxLength: 128
    ContextUpdate:
      oneOf:
        - $ref: '#/components/schemas/ConversationSummary'
        - $ref: '#/components/schemas/ConversationMessageDelta'
      discriminator:
        propertyName: mode
        mapping:
          summary: '#/components/schemas/ConversationSummary'
          messages: '#/components/schemas/ConversationMessageDelta'
      description: >
        Contexte apparu depuis le dernier appel Zelinqa. Le client choisit un
        résumé

        compact ou le delta ordonné des messages ; il ne renvoie jamais
        l'historique

        déjà traité.
    ClientUpdates:
      type: object
      additionalProperties: false
      minProperties: 1
      description: >-
        Mises à jour explicites du système appelant, prioritaires sur
        l'inférence.
      properties:
        data:
          type: array
          maxItems: 100
          items:
            $ref: '#/components/schemas/DataClientUpdate'
        dimensions:
          type: array
          maxItems: 100
          items:
            $ref: '#/components/schemas/DimensionOverrideUpdate'
        objective:
          $ref: '#/components/schemas/ObjectiveOverrideUpdate'
    SelectionOptions:
      type: object
      additionalProperties: false
      description: >
        Contraintes valables pour cet appel uniquement. Elles sont appliquées
        **avant**

        le calcul du classement, jamais en filtrant un top 3 déjà constitué.
      properties:
        candidate_count:
          type: integer
          minimum: 1
          maximum: 10
          description: >-
            Remplace pour cet appel le nombre de propositions défini dans la
            configuration.
        dimensions:
          $ref: '#/components/schemas/DimensionSelection'
        allowed_question_types:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            $ref: '#/components/schemas/QuestionType'
        required_target_ids:
          type: array
          minItems: 1
          maxItems: 100
          uniqueItems: true
          items:
            type: string
            minLength: 1
            maxLength: 128
          description: N'accepter que des questions couvrant au moins une de ces cibles.
        excluded_question_ids:
          type: array
          maxItems: 500
          uniqueItems: true
          items:
            type: string
            minLength: 1
            maxLength: 128
    StopReason:
      type: string
      enum:
        - no_question_available
      description: >
        Unique cause d'arrêt dur : aucune question identifiable ne subsiste
        après

        application des exclusions dures — question inactive, dimension exclu
        par

        le client, contrainte stricte de l'appel. Les autres situations
        terminales

        remontent par `warnings`, la conversation restant décidée par
        l'appelant.
    Candidate:
      type: object
      additionalProperties: false
      required:
        - rank
        - question_id
        - text
        - type
        - choices
        - target_ids
      description: |
        **Invariant garanti par le moteur**, couvert par les tests de contrat :
        `type: open` implique `choices` vide ; tout autre type implique au moins
        deux choix.
      properties:
        rank:
          type: integer
          minimum: 1
          maximum: 10
        question_id:
          type: string
          minLength: 1
          maxLength: 128
          description: >-
            Toujours un identifiant valide du corpus publié, y compris pour un
            repli.
        text:
          type: string
          minLength: 1
          maxLength: 4000
          description: >
            Libellé publié de la question. L'agent hôte reste libre de le
            reformuler :

            Zelinqa sait rattacher un texte reformulé à sa question au tour
            suivant.
        type:
          $ref: '#/components/schemas/QuestionType'
        choices:
          type: array
          maxItems: 50
          items:
            $ref: '#/components/schemas/CandidateChoice'
          description: Vide pour une question ouverte.
        target_ids:
          type: array
          minItems: 1
          uniqueItems: true
          items:
            type: string
            minLength: 1
            maxLength: 128
          description: Cibles que cette question adresse, principale en premier.
        selection_mode:
          $ref: '#/components/schemas/QuestionSelectionMode'
          description: |
            Présent pour `semi_open` afin d'indiquer si l'appelant accepte un ou
            plusieurs `choice_ids`. Absent pour les autres types.
    ProgressView:
      type: object
      additionalProperties: false
      required:
        - objective
        - dimensions
      properties:
        objective:
          $ref: '#/components/schemas/ObjectiveProgress'
        dimensions:
          type: array
          items:
            $ref: '#/components/schemas/DimensionProgress'
    SelectionWarning:
      type: string
      enum:
        - max_turns_reached
        - objective_achieved
        - eligibility_exhausted_fallback
        - constraints_relaxed
      description: >
        Conditions d'arrêt réunies, sans que Zelinqa interrompe la conversation.


        - `max_turns_reached` : la limite souple est atteinte ou dépassée ;

        - `objective_achieved` : les conditions de réussite sont satisfaites ;

        - `eligibility_exhausted_fallback` : plus aucune question n'était
        éligible
          normalement, la proposition est un repli — elle porte tout de même un
          `question_id` valide et respecte les exclusions dures ;
        - `constraints_relaxed` : une préférence `prefer` a dû être élargie.
    VersionInfo:
      type: object
      additionalProperties: false
      required:
        - state_version
        - engine_version
        - api_version
      description: >
        La version de configuration épinglée par la session n'est pas exposée :
        le

        client ne la choisit pas et ne doit pas s'y adosser.
      properties:
        state_version:
          type: integer
          minimum: 0
          description: À renvoyer dans la mutation suivante de cette session.
        engine_version:
          type: string
        api_version:
          type: string
          const: '1.0'
    ErrorEnvelope:
      type: object
      additionalProperties: false
      required:
        - code
        - message
        - request_id
        - details
      description: Enveloppe uniforme de toutes les erreurs métier.
      properties:
        code:
          $ref: '#/components/schemas/ErrorCode'
        message:
          type: string
          minLength: 1
        request_id:
          type: string
          minLength: 1
        details:
          type: object
          additionalProperties: true
    QuestionOutcome:
      type: string
      enum:
        - asked_answered
        - asked_no_answer
        - refused
      description: >
        Résultat d'une question effectivement posée.


        - `asked_answered` : question posée et réponse exploitable reçue ;

        - `asked_no_answer` : question posée, aucune réponse exploitable ;

        - `refused` : refus explicite de répondre.


        Une question jamais posée n'a pas de résultat : elle est simplement
        absente.

        Il n'existe pas de valeur `skipped` — une proposition que l'agent n'a
        pas

        utilisée est journalisée comme décision ignorée, jamais comme un faux

        résultat sur la question.
    StructuredAnswer:
      type: object
      additionalProperties: false
      required:
        - choice_ids
      description: >
        Réponse à une question à choix. Le mapping choix vers information de
        réussite

        est déterministe et compilé à la publication : aucun appel LLM n'est
        effectué

        pour l'interpréter.
      properties:
        choice_ids:
          type: array
          minItems: 1
          maxItems: 50
          uniqueItems: true
          items:
            type: string
            minLength: 1
            maxLength: 128
        free_text:
          type: string
          minLength: 1
          maxLength: 4000
          description: Complément libre, autorisé uniquement pour une question `semi_open`.
    ConversationSummary:
      type: object
      additionalProperties: false
      required:
        - mode
        - text
      properties:
        mode:
          type: string
          const: summary
        text:
          type: string
          minLength: 1
          maxLength: 16000
    ConversationMessageDelta:
      type: object
      additionalProperties: false
      required:
        - mode
        - messages
      properties:
        mode:
          type: string
          const: messages
        messages:
          type: array
          minItems: 1
          maxItems: 100
          items:
            $ref: '#/components/schemas/ConversationMessage'
          description: Messages nouveaux, dans l'ordre conversationnel.
    DataClientUpdate:
      oneOf:
        - $ref: '#/components/schemas/SetDataUpdate'
        - $ref: '#/components/schemas/UnsetDataUpdate'
        - $ref: '#/components/schemas/NotApplicableDataUpdate'
      description: >
        Le client n'envoie jamais de statut. `set` produit une valeur confirmée
        avec

        la priorité de preuve maximale ; `unset` retire la valeur courante ;

        `not_applicable` satisfait une information de réussite sans lui donner
        de

        valeur, par exemple une donnée sans objet pour ce visiteur.
    DimensionOverrideUpdate:
      oneOf:
        - type: object
          additionalProperties: false
          required:
            - id
            - operation
            - status
          properties:
            id:
              type: string
              minLength: 1
              maxLength: 128
            operation:
              type: string
              const: set
            status:
              type: string
              enum:
                - achieved
                - not_achieved
        - type: object
          additionalProperties: false
          required:
            - id
            - operation
          properties:
            id:
              type: string
              minLength: 1
              maxLength: 128
            operation:
              type: string
              const: exclude
        - type: object
          additionalProperties: false
          required:
            - id
            - operation
          properties:
            id:
              type: string
              minLength: 1
              maxLength: 128
            operation:
              type: string
              const: clear
    ObjectiveOverrideUpdate:
      oneOf:
        - type: object
          additionalProperties: false
          required:
            - operation
            - status
          properties:
            operation:
              type: string
              const: set
            status:
              $ref: '#/components/schemas/ObjectiveOverrideValue'
        - type: object
          additionalProperties: false
          required:
            - operation
          properties:
            operation:
              type: string
              const: clear
    DimensionSelection:
      type: object
      additionalProperties: false
      required:
        - ids
        - mode
      properties:
        ids:
          type: array
          minItems: 1
          maxItems: 100
          uniqueItems: true
          items:
            type: string
            minLength: 1
            maxLength: 128
        mode:
          type: string
          enum:
            - restrict
            - prefer
          description: >
            `restrict` interdit strictement les questions hors de ces
            dimensions.

            `prefer` les favorise mais autorise un repli si aucun candidat
            éligible

            n'y reste.
    QuestionType:
      type: string
      enum:
        - open
        - single_choice
        - multiple_choice
        - semi_open
      description: |
        - `open` : réponse libre, aucun choix ;
        - `single_choice` : un seul choix parmi la liste ;
        - `multiple_choice` : plusieurs choix possibles ;
        - `semi_open` : choix proposés, complément libre autorisé via
          `structured_answer.free_text`.

        Pour `semi_open`, `selection_mode` précise si un ou plusieurs choix sont
        permis en plus du complément libre.
    CandidateChoice:
      type: object
      additionalProperties: false
      required:
        - choice_id
        - label
      properties:
        choice_id:
          type: string
          minLength: 1
          maxLength: 128
        label:
          type: string
          minLength: 1
          maxLength: 500
    QuestionSelectionMode:
      type: string
      enum:
        - single
        - multiple
    ObjectiveProgress:
      type: object
      additionalProperties: false
      required:
        - computed_status
        - progress
        - client_override
        - effective_status
      properties:
        computed_status:
          $ref: '#/components/schemas/ProgressStatus'
        progress:
          type: number
          minimum: 0
          maximum: 1
          description: |
            Agrégation des dimensions pondérée par leur rôle de complétion. Les
            dimensions exclus sortent du dénominateur.
        client_override:
          oneOf:
            - $ref: '#/components/schemas/ObjectiveClientOverrideView'
            - type: 'null'
        effective_status:
          $ref: '#/components/schemas/ProgressStatus'
    DimensionProgress:
      type: object
      additionalProperties: false
      required:
        - id
        - order_position
        - completion_role
        - computed_status
        - progress
        - client_override
        - effective_status
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
        order_position:
          type: integer
          minimum: 0
        completion_role:
          $ref: '#/components/schemas/CompletionRole'
        computed_status:
          $ref: '#/components/schemas/ProgressStatus'
        progress:
          type: number
          minimum: 0
          maximum: 1
        client_override:
          oneOf:
            - $ref: '#/components/schemas/DimensionClientOverrideView'
            - type: 'null'
        effective_status:
          type: string
          enum:
            - not_started
            - in_progress
            - covered
            - blocked
            - excluded
          description: >
            Statut consommable par le client après application de
            `client_override`

            au `computed_status` : sans override, le statut calculé ; `achieved`

            force `covered` ; `not_achieved` conserve le statut calculé sauf que

            `covered` redevient `in_progress` ; `excluded` retire la dimension
            de

            la sélection et des agrégations de l'objectif.
    ErrorCode:
      type: string
      description: Catalogue fermé des erreurs métier V1.
      enum:
        - unauthorized
        - insufficient_scope
        - idempotency_contention
        - state_version_conflict
        - idempotency_key_reused
        - unknown_session
        - invalid_previous_turn
        - constraint_no_match
        - invalid_choice
        - compiled_artifact_unavailable
        - configuration_validation_failed
        - compilation_in_progress
        - unknown_configuration
        - unknown_compilation
    ConversationMessage:
      type: object
      additionalProperties: false
      required:
        - role
      description: |
        Message du delta de contexte. Une `structured_answer` portée par un
        message utilisateur est mappée déterministement, sans LLM, comme pour
        `previous_turn` — elle exige donc `question_id` : sans lui, la requête
        est rejetée en `invalid_previous_turn`. Un `question_id` connu du
        corpus rend les cibles de cette question vérifiables par l'extraction,
        même hors décision en attente.
      properties:
        role:
          type: string
          enum:
            - user
            - assistant
        message_id:
          type: string
          minLength: 1
          maxLength: 128
        question_id:
          type: string
          minLength: 1
          maxLength: 128
        text:
          type: string
          minLength: 1
          maxLength: 8000
        structured_answer:
          $ref: '#/components/schemas/StructuredAnswer'
          description: >
            Réponse structurée rapportée dans le delta. Obligatoirement

            accompagnée de `question_id` (le schéma ne porte pas de if/then :

            la règle est appliquée par le runtime, code
            `invalid_previous_turn`).
        occurred_at:
          type: string
          format: date-time
      anyOf:
        - required:
            - text
        - required:
            - structured_answer
    SetDataUpdate:
      type: object
      additionalProperties: false
      required:
        - id
        - value
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
        operation:
          type: string
          const: set
          default: set
        value:
          description: >
            Valeur validée contre le schéma publié de l'information de réussite.

            Une chaîne, un nombre, un booléen ou une liste de ces valeurs ;
            jamais

            un objet, forme que l'extraction ne saurait produire ni corriger.
          oneOf:
            - type: string
            - type: number
            - type: boolean
            - type: array
              items:
                oneOf:
                  - type: string
                  - type: number
                  - type: boolean
    UnsetDataUpdate:
      type: object
      additionalProperties: false
      required:
        - id
        - operation
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
        operation:
          type: string
          const: unset
    NotApplicableDataUpdate:
      type: object
      additionalProperties: false
      required:
        - id
        - operation
      properties:
        id:
          type: string
          minLength: 1
          maxLength: 128
        operation:
          type: string
          const: not_applicable
    ObjectiveOverrideValue:
      type: string
      enum:
        - achieved
        - not_achieved
    ProgressStatus:
      type: string
      enum:
        - not_started
        - in_progress
        - covered
        - blocked
    ObjectiveClientOverrideView:
      type: object
      additionalProperties: false
      required:
        - status
      properties:
        status:
          $ref: '#/components/schemas/ObjectiveOverrideValue'
        updated_at:
          type: string
          format: date-time
    CompletionRole:
      type: string
      enum:
        - blocking
        - contributing
        - optional
      description: >-
        Rôle dans la qualification globale : les données des dimensions blocking
        doivent être conclues ; une dimension blocking sans donnée ne bloque pas
        seule la réussite. Les dimensions contributing participent au seuil
        moyen du niveau. Les dimensions optional n'imposent pas de seuil de
        couverture, mais les informations de réussite configurées restent
        exigées, sauf exclusion ou décision explicite du client.
    DimensionClientOverrideView:
      type: object
      additionalProperties: false
      required:
        - status
      properties:
        status:
          $ref: '#/components/schemas/DimensionOverrideValue'
        updated_at:
          type: string
          format: date-time
    DimensionOverrideValue:
      type: string
      enum:
        - achieved
        - not_achieved
        - excluded
      description: >
        `achieved` force la dimension à couvert. `not_achieved` empêche une

        complétion calculée prématurée. `excluded` le retire de la sélection et
        des

        dénominateurs de progression de l'objectif.
  examples:
    DecisionNormale:
      summary: Le tour précédent a été compris, deux questions sont proposées
      value:
        request_id: req_1002
        session_id: ses_01J8Z
        decision_id: dec_7f2a
        action: ask
        stop_reason: null
        candidates:
          - rank: 1
            question_id: q_budget
            text: Quel budget souhaitez-vous consacrer au canapé ?
            type: open
            choices: []
            target_ids:
              - annual_budget
          - rank: 2
            question_id: q_delai
            text: À quelle période souhaitez-vous être livré ?
            type: single_choice
            choices:
              - choice_id: choice_1m
                label: Dans le mois
              - choice_id: choice_3m
                label: Dans les trois mois
              - choice_id: choice_later
                label: Plus tard
            target_ids:
              - delivery_window
        progress:
          objective:
            computed_status: in_progress
            progress: 0.34
            client_override: null
            effective_status: in_progress
          dimensions:
            - id: so_besoin
              order_position: 0
              completion_role: blocking
              computed_status: covered
              progress: 1
              client_override: null
              effective_status: covered
            - id: so_budget
              order_position: 1
              completion_role: blocking
              computed_status: in_progress
              progress: 0.15
              client_override: null
              effective_status: in_progress
            - id: so_livraison
              order_position: 2
              completion_role: contributing
              computed_status: not_started
              progress: 0
              client_override: null
              effective_status: not_started
        turn_count: 2
        turns_remaining: 8
        warnings: []
        degraded: false
        degraded_reasons: []
        versions:
          state_version: 4
          engine_version: 1.0.0
          api_version: '1.0'
    DecisionApresMaxTurns:
      summary: >-
        La limite de tours est dépassée ; Zelinqa propose encore, l'appelant
        décide
      value:
        request_id: req_1003
        session_id: ses_01J8Z
        decision_id: dec_8c11
        action: ask
        stop_reason: null
        candidates:
          - rank: 1
            question_id: q_delai
            text: À quelle période souhaitez-vous être livré ?
            type: single_choice
            choices:
              - choice_id: choice_1m
                label: Dans le mois
              - choice_id: choice_3m
                label: Dans les trois mois
              - choice_id: choice_later
                label: Plus tard
            target_ids:
              - delivery_window
        progress:
          objective:
            computed_status: in_progress
            progress: 0.86
            client_override: null
            effective_status: in_progress
          dimensions:
            - id: so_besoin
              order_position: 0
              completion_role: blocking
              computed_status: covered
              progress: 1
              client_override: null
              effective_status: covered
            - id: so_budget
              order_position: 1
              completion_role: blocking
              computed_status: covered
              progress: 1
              client_override: null
              effective_status: covered
            - id: so_livraison
              order_position: 2
              completion_role: contributing
              computed_status: in_progress
              progress: 0.4
              client_override: null
              effective_status: in_progress
        turn_count: 10
        turns_remaining: 0
        warnings:
          - max_turns_reached
        degraded: false
        degraded_reasons: []
        versions:
          state_version: 21
          engine_version: 1.0.0
          api_version: '1.0'
    ArretSansQuestion:
      summary: Aucune question identifiable ne subsiste après les exclusions dures
      value:
        request_id: req_1004
        session_id: ses_01J8Z
        decision_id: null
        action: stop
        stop_reason: no_question_available
        candidates: []
        progress:
          objective:
            computed_status: covered
            progress: 1
            client_override: null
            effective_status: covered
          dimensions:
            - id: so_besoin
              order_position: 0
              completion_role: blocking
              computed_status: covered
              progress: 1
              client_override: null
              effective_status: covered
            - id: so_budget
              order_position: 1
              completion_role: blocking
              computed_status: covered
              progress: 1
              client_override: null
              effective_status: covered
            - id: so_livraison
              order_position: 2
              completion_role: contributing
              computed_status: not_started
              progress: 0
              client_override:
                status: excluded
                updated_at: '2026-09-01T09:31:00Z'
              effective_status: excluded
        turn_count: 12
        turns_remaining: 0
        warnings:
          - objective_achieved
          - max_turns_reached
        degraded: false
        degraded_reasons: []
        versions:
          state_version: 25
          engine_version: 1.0.0
          api_version: '1.0'
  responses:
    Unauthorized:
      description: >-
        Réponse 401 possible au niveau de l'application. Une clé absente ou
        invalide est généralement refusée en 403 par la passerelle, sans
        enveloppe métier garantie.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            code: unauthorized
            message: Clé d'intégration absente ou invalide.
            request_id: req_9000
            details: {}
    InsufficientScope:
      description: >-
        Accès refusé. La passerelle peut renvoyer 403 pour une clé absente,
        invalide, expirée, révoquée ou dépourvue du scope requis, sans enveloppe
        métier garantie.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            code: insufficient_scope
            message: Cette clé ne permet pas de publier une configuration.
            request_id: req_9001
            details:
              required_scopes:
                - configuration:publish
              granted_scopes:
                - configuration:read
                - configuration:write
    UnknownSession:
      description: Session inconnue, ou appartenant à un autre tenant.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            code: unknown_session
            message: La session demandée est inconnue.
            request_id: req_9007
            details:
              session_id: ses_inconnue
    SessionMutationConflict:
      description: Conflit de version optimiste ou réutilisation d'une clé d'idempotence.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            state_version_conflict:
              summary: La session a été modifiée entre-temps
              value:
                code: state_version_conflict
                message: La session a été modifiée depuis votre dernière lecture.
                request_id: req_9003
                details:
                  supplied_state_version: 7
                  current_state_version: 8
            idempotency_key_reused:
              summary: Même clé, corps différent
              value:
                code: idempotency_key_reused
                message: Cette clé d'idempotence est déjà associée à une autre requête.
                request_id: req_9004
                details:
                  idempotency_key: next-ses01J8Z-turn-4
    CompiledArtifactUnavailable:
      description: >
        L'artefact compilé épinglé par la session est réellement inaccessible.
        Une

        configuration simplement remplacée par une version plus récente ne
        produit pas

        cette erreur : l'ancienne version reste lisible pour terminer les
        sessions qui

        l'utilisent déjà.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            code: compiled_artifact_unavailable
            message: >-
              L'artefact moteur de cette session est temporairement
              indisponible.
            request_id: req_9010
            details:
              session_id: ses_01J8Z
    NextUnprocessable:
      description: >-
        Le tour précédent est incohérent ou les contraintes strictes ne laissent
        aucune question. Une question ouverte répondue sans
        previous_turn.user_text non vide renvoie 422 invalid_previous_turn avant
        toute analyse par modèle.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          examples:
            open_question_without_text:
              summary: >-
                Une question ouverte répondue exige la réponse réelle de la
                personne
              value:
                code: invalid_previous_turn
                message: une question ouverte répondue exige user_text
                request_id: req_open_without_text
                details: {}
            invalid_previous_turn:
              summary: Les identifiants fournis contredisent la décision en attente
              value:
                code: invalid_previous_turn
                message: >-
                  Les identifiants fournis ne correspondent pas à la décision en
                  attente.
                request_id: req_9011
                details:
                  pending_decision_id: dec_7f2a
                  supplied_decision_id: dec_7f29
                  supplied_question_id: q_budget
                  candidate_question_ids:
                    - q_style
                    - q_budget
                    - q_delai
            constraint_no_match:
              summary: Aucune question ne satisfait les contraintes strictes demandées
              value:
                code: constraint_no_match
                message: >-
                  Aucune question disponible ne satisfait les contraintes
                  demandées.
                request_id: req_9012
                details:
                  allowed_question_types:
                    - multiple_choice
                  required_target_ids:
                    - annual_budget
            invalid_choice:
              summary: Un choix n'appartient pas à la question résolue
              value:
                code: invalid_choice
                message: Un choix fourni ne correspond pas à la question résolue.
                request_id: req_9013
                details:
                  question_id: q_style
                  invalid_choice_ids:
                    - choice_inconnu
    IdempotencyContention:
      description: >
        La clé d'idempotence n'a pas pu être réservée après deux tentatives, à
        cause

        d'un cycle anormal de requêtes concurrentes portant la même clé.


        Ce n'est **pas** une erreur du client et ce n'est pas un conflit :
        réessayer

        après un court délai a des chances d'aboutir. À distinguer de

        `idempotency_key_reused`, qui est définitif pour ce couple clé/corps.
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorEnvelope'
          example:
            code: idempotency_contention
            message: La clé d'idempotence n'a pas pu être réservée, réessayez.
            request_id: req_9017
            details:
              idempotency_key: next-ses01J8Z-turn-4
              retry_after_seconds: 1
  securitySchemes:
    ApiKeyAuth:
      type: http
      scheme: bearer
      bearerFormat: Zelinqa API key
      description: >-
        Envoyez la clé API dans Authorization: Bearer <clé>. Elle identifie le
        domaine et les droits autorisés. Ne transmettez pas d'en-têtes
        d'identité internes.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.