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

# End a session

> End an in-progress session. Idempotent: a session already in its terminal state is returned as-is. Tracker sessions have no end semantics and refuse with E1017.



## OpenAPI

````yaml /api-reference-v2/openapi.json post /v2/projects/{projectId}/environments/{environmentId}/sessions/{id}/end
openapi: 3.0.0
info:
  title: Usertour API v2
  description: >-
    Project-scoped v2 API. Authenticate with a personal API token — an opaque
    `utp_...` string (NOT a JWT: do not try to decode it), created in the
    Usertour app under Settings → API, sent as `Authorization: Bearer utp_...`.
  version: '2.0'
  contact: {}
servers:
  - url: https://api.usertour.io
security: []
tags: []
paths:
  /v2/projects/{projectId}/environments/{environmentId}/sessions/{id}/end:
    post:
      tags:
        - Sessions
      summary: End a session
      description: >-
        End an in-progress session. Idempotent: a session already in its
        terminal state is returned as-is. Tracker sessions have no end semantics
        and refuse with E1017.
      operationId: ApiContentSessionsController_end
      parameters:
        - name: id
          required: true
          in: path
          description: Content session ID
          schema:
            type: string
        - name: environmentId
          required: true
          in: path
          description: Environment ID
          schema:
            type: string
        - name: projectId
          required: true
          in: path
          description: Project ID
          schema:
            type: string
      responses:
        '200':
          description: Content session ended
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ContentSessionDto'
        '400':
          description: >-
            Invalid request — E1017 validation (may carry `issues`; an invalid
            orderBy/limit is also E1017), E1015 invalid scope, E0003 invalid
            against current domain state.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponseDto'
        '401':
          description: Missing or expired API key — E1010, E1020.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponseDto'
        '403':
          description: >-
            Refused — E1000 invalid key, E1011 project not in token scope, E1012
            insufficient scope, E1029 environment not in token scope, E1032
            environment creation needs a token without env-targeted capabilities
            (its allowlist cannot cover a not-yet-existing environment).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponseDto'
        '404':
          description: Content session not found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponseDto'
        '429':
          description: >-
            Rate limit exceeded — E1013. The limit follows the project's plan
            (100/500/1000/3000 requests per minute); unknown credentials share a
            per-IP bucket. Every response also carries X-RateLimit-Limit /
            -Remaining / -Reset for pacing; a 429 adds the standard Retry-After
            header (seconds to back off).
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponseDto'
      security:
        - bearer: []
components:
  schemas:
    ContentSessionDto:
      type: object
      properties:
        id:
          type: string
        object:
          type: string
          enum:
            - contentSession
        answers:
          type: array
          items:
            type: object
            properties:
              id:
                type: string
              object:
                type: string
                enum:
                  - contentSessionAnswer
              answerType:
                type: string
                enum:
                  - nps
                  - star-rating
                  - scale
                  - single-line-text
                  - multi-line-text
                  - multiple-choice
                description: >-
                  Kind of question answered — the same vocabulary as question
                  `type` on the version.
              answerValue:
                anyOf:
                  - type: number
                  - type: string
                  - type: array
                    items:
                      type: string
                description: >-
                  The answer's value in its real type, keyed off answerType: nps
                  / star-rating / scale → a number; single-line-text /
                  multi-line-text → a string; multiple-choice → an array of the
                  chosen option strings. null only when the stored value is
                  missing.
                nullable: true
              createdAt:
                type: string
                format: date-time
              questionCvid:
                type: string
              questionName:
                type: string
            required:
              - id
              - object
              - answerType
              - answerValue
              - createdAt
              - questionCvid
              - questionName
          description: >-
            null = not expanded (pass expand: ["answers"]). Expanded: [] means
            the session has no question answers; entries appear as the user
            answers.
          nullable: true
        completed:
          type: boolean
          description: >-
            Whether the user GENUINELY reached the goal — a flow ran to its end
            (or an explicit completion step) / every checklist task was checked.
            Independent of whether the session is still open: a completed
            checklist can still be showing, and a flow can complete at a
            mid-flow completion step and keep running. Only flows and checklists
            can be completed; banners / launchers / resource centers are
            seen-then-dismissed and are always false.
        completedAt:
          type: string
          description: When the goal was reached (null if never completed).
          nullable: true
        endedAt:
          type: string
          description: >-
            When the session closed (null while it is still open/active). A
            session can be completed but not yet ended (still open), or ended
            without being completed (dismissed).
          nullable: true
        endReason:
          type: string
          description: >-
            Why the session ended (null while open). Vocabulary — user actions:
            `close_button_dismiss` (the X), `backdrop_dismiss`,
            `dismiss_button`, `user_closed`; authored behavior: `action_dismiss`
            (a dismiss action ran), `trigger_dismiss`, `auto_dismissed`,
            `end_from_program`; displacement: `replaced`,
            `user_started_other_content`, `program_started_other_content`;
            health/diagnostic: **`tooltip_target_missing`** (the tooltip anchor
            was never found — the per-session selector-health signal),
            `step_not_found`, `content_not_found`, `store_not_found`,
            `unpublished_content`, `session_timeout`, `system_closed`,
            `url_start_closed`, `launcher_deactivated`; admin: `admin_ended`.
            Old sessions may carry legacy spellings (e.g. bare `action`) — treat
            unknown values as "ended, reason unclassified".
          nullable: true
        contentId:
          type: string
        content:
          type: object
          properties:
            id:
              type: string
            object:
              type: string
              enum:
                - content
            name:
              type: string
            type:
              type: string
              enum:
                - checklist
                - flow
                - launcher
                - banner
                - tracker
                - resource-center
                - announcement
            editedVersionId:
              type: string
            updatedAt:
              type: string
              format: date-time
            createdAt:
              type: string
              format: date-time
          required:
            - id
            - object
            - name
            - type
            - editedVersionId
            - updatedAt
            - createdAt
          description: >-
            null until expanded with expand: ["content"] (the content always
            exists).
          nullable: true
        createdAt:
          type: string
          format: date-time
        companyId:
          type: string
          description: >-
            null when the session has no company context (user not in a
            company).
          nullable: true
        company:
          type: object
          properties:
            id:
              type: string
            object:
              type: string
              enum:
                - company
            attributes:
              type: object
              additionalProperties: {}
            createdAt:
              type: string
              format: date-time
          required:
            - id
            - object
            - attributes
            - createdAt
          description: >-
            null = not expanded (pass expand: ["company"]) OR, after expanding,
            the session genuinely has no company — check `companyId` to tell the
            two apart.
          nullable: true
        lastActivityAt:
          type: string
          format: date-time
        progress:
          type: number
          description: >-
            Flow progress percentage, 0-100 (integer). 100 does not imply
            completed — see `completed`.
        userId:
          type: string
          nullable: true
        user:
          type: object
          properties:
            id:
              type: string
            object:
              type: string
              enum:
                - user
            attributes:
              type: object
              additionalProperties: {}
            createdAt:
              type: string
              format: date-time
          required:
            - id
            - object
            - attributes
            - createdAt
          description: >-
            null = not expanded (pass expand: ["user"]) OR the user was deleted
            — check `userId`.
          nullable: true
        versionId:
          type: string
        version:
          type: object
          properties:
            id:
              type: string
            object:
              type: string
              enum:
                - contentVersion
            number:
              type: number
            updatedAt:
              type: string
              format: date-time
            createdAt:
              type: string
              format: date-time
          required:
            - id
            - object
            - number
            - updatedAt
            - createdAt
          description: 'null until expanded with expand: ["version"].'
          nullable: true
      required:
        - id
        - object
        - answers
        - completed
        - completedAt
        - endedAt
        - endReason
        - contentId
        - content
        - createdAt
        - companyId
        - company
        - lastActivityAt
        - progress
        - userId
        - user
        - versionId
        - version
    ErrorResponseDto:
      type: object
      properties:
        error:
          type: object
          properties:
            code:
              type: string
              description: >-
                Stable machine-readable code (e.g. E1017). Match on this, never
                on `message`.
            message:
              type: string
              description: Human-readable summary. Wording may change between releases.
            issues:
              description: >-
                Validation errors (E1017) may carry one entry per problem so
                every field can be fixed in a single round-trip. Absent on other
                errors.
              type: array
              items:
                type: object
                properties:
                  rule:
                    type: string
                    description: >-
                      Which validation layer rejected it: schema |
                      reactive_condition | action_not_allowed | step_shape |
                      reference_target | auto_start | media_url. New values may
                      be added; treat an unknown value as a generic validation
                      failure.
                  message:
                    type: string
                  path:
                    description: >-
                      Path into the request body (e.g.
                      `steps[0].triggers[0].when[1]`).
                    type: string
                required:
                  - rule
                  - message
            doc_url:
              type: string
              description: Base URL of the API documentation.
      required:
        - error
  securitySchemes:
    bearer:
      scheme: bearer
      bearerFormat: utp_... personal API token (opaque)
      type: http

````