openapi: 3.0.3
info:
  title: Repzo API - AI Object Detection Assigned Missions
  version: 1.0.0
  description: |
    **Assigned missions** — the read the MOBILE APP (and the dashboard's
    assignment SIMULATOR) uses to know which missions apply to a CLIENT and
    whether they are already done in the current interval.

    **Assignment read** (`GET ?client=<id>[&rep=<id>][&visit=<visit_id>]`,
    or `GET /<client id>` with the same optional `rep` / `visit` params).
    Resolves the enabled assignment rules against that client's attributes
    (tags, area tags, channel, assigned reps, chain, teams) and returns:
    - `rules[]` — every enabled rule with `matched` and PER-CONDITION
      verdicts (the simulator renders exactly why a rule hit or missed),
      plus its `frequency`, its `mission_sets[]` lines (set, set name,
      `requirement_mode`) and the STRICTEST `requirement_mode` of those
      lines.
    - `missions[]` — the missions of the matched rules' sets, each with its
      `requirements[]` (one per matching rule × set line: frequency,
      `requirement_mode`, current window start, completions inside the
      window, satisfied), the STRICTEST `requirement_mode` across them, and
      a rolled-up `done`. A completion = a SUCCESSFUL analysis of one of the
      client's sessions (optionally only the given rep's) whose weighted
      mission score meets the mission's `min_score` (a ZERO score never
      completes). A session STAMPED with the mission it was started from
      credits ONLY that mission — scanning one mission never completes the
      client's others.
    - `business_day` — the CURRENT business day the windows are anchored to,
      `timezone` — the zone it was resolved in, `visit_id` — the visit the
      read is scoped to (or `null`), and `rep_can_redo` — whether the rep in
      context may re-scan a DONE mission.

    **Windows are BUSINESS days, not calendar days.** `day` = the current
    business day, `week` = the current business week (first day =
    `settings.first_business_day_in_week`), `month` / `quarter` = the current
    month / quarter of business days. Business days are the platform's: the
    rep's open-day stamping context (shift schedule, `end_of_day` cut, time
    zone) when a rep is in scope (`rep` or a rep token), else the company's —
    so "per day" means the same business day the visit and its activities
    carry. A completion's business day is the one stamped on the result (from
    the scan's session), else derived from its time.

    **`every_visit` and the visit.** Pass `visit` = the DEVICE visit id
    (`visits.visit_id`) the rep is currently in — the same id every activity
    carries — and every_visit demands are scoped to THAT visit: done only when
    a completion stamped with it exists (a completion earlier today in ANOTHER
    visit to the same client leaves the mission due; whatever business day the
    visit spans). No visit lookup happens — the visit may not have synced to
    the server yet. Without `visit` the current business day is the practical
    visit proxy — what older mobiles get. The visit id reaches the results
    through the frame intake (`activity-ai-object-detection-session-frame`
    stamps `visit_id` / `route` / `business_day` on the session) and the
    evaluator (copies them onto every mission result).

    **Requirement mode is carried, not judged.** `satisfied` and `done`
    count completions regardless of the mode. The MOBILE APP applies the
    mode when deciding whether the rep may END THE VISIT: `not_required`
    blocks nothing, `submission_required` needs a scan submitted for the
    mission during the visit, `completion_required` needs `done`. A mission
    reached through several rules/sets carries the strictest mode.

    The flat RESULTS LISTING lives in its own service —
    `/ai-object-detection-mission-results` — so this read stays the lean
    per-client one the mobile hits on every visit.

    Everything is computed on read from current definitions + stored metric
    results — rule/frequency edits and recalculations reflect immediately;
    nothing is stamped on clients or sessions. NOT paginated. Read-only
    (create/update/patch/remove are rejected with 400). Rep- and admin-facing.
servers:
  - url: https://sv.api.repzo.me
security:
  - ApiKeyAuth: []
  - JwtAuth: []
paths:
  /ai-object-detection-assigned-missions:
    get:
      summary: Assigned missions for a client
      operationId: findAiObjectDetectionAssignedMissions
      parameters:
        - in: query
          name: client
          required: true
          description: "Client `_id` — required (400 when missing, invalid or not found in the caller's namespace)."
          schema: { type: string }
        - in: query
          name: rep
          description: "Optional rep `_id` — completions count only that rep's sessions, and the rep's business-day context (shift, end_of_day, time zone) and `rep_can_redo_object_detection_missions` permission apply. Defaults to the rep token's rep; with an admin token and no `rep` the company context is used and `rep_can_redo` is true."
          schema: { type: string }
        - in: query
          name: visit
          description: "Optional DEVICE visit id (`visits.visit_id`) of the visit the rep is in. Scopes every_visit demands to that visit (see the introduction). Not looked up — the visit may not have synced yet."
          schema: { type: string }
      responses:
        "200":
          description: The assignment read (a single object — not paginated).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AssignmentRead"
        "400":
          description: "`client` missing / not a valid id / not found, `rep` not a valid id, or no namespace in the token."
  /ai-object-detection-assigned-missions/{id}:
    get:
      summary: Assignment read with the id as the client
      operationId: getAiObjectDetectionAssignedMissions
      parameters:
        - in: path
          name: id
          required: true
          description: "Client `_id` — the same read as `GET ?client=<id>`."
          schema: { type: string }
        - in: query
          name: rep
          description: "Optional rep `_id` (same semantics as on the list read)."
          schema: { type: string }
        - in: query
          name: visit
          description: "Optional DEVICE visit id (same semantics as on the list read)."
          schema: { type: string }
      responses:
        "200":
          description: Same shape as the assignment read.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AssignmentRead"
        "400":
          description: "Client not found / invalid, `rep` invalid, or no namespace in the token."
components:
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: api-key
    JwtAuth:
      type: apiKey
      in: header
      name: Authorization
  schemas:
    RuleFrequency:
      type: object
      properties:
        interval:
          type: string
          enum: [every_visit, day, week, month, quarter]
        times:
          type: integer
          minimum: 1
          description: "Completions required per interval (1 for every_visit)."
    RequirementMode:
      type: string
      enum: [not_required, submission_required, completion_required]
      description: "What a rule demands of a set's missions: not_required (optional), submission_required (a scan must be submitted, pass or fail), completion_required (the scan must complete the mission). Carried by the backend, enforced by the mobile app when the rep ends the visit."
    AssignmentRead:
      type: object
      properties:
        client:
          type: object
          properties:
            _id: { type: string }
            name: { type: string }
        rep:
          type: string
          nullable: true
          description: "The `rep` query param echoed back — null when not sent."
        rep_can_redo:
          type: boolean
          description: 'The rep''s `rep_can_redo_object_detection_missions` permission (default false) — the mobile disables "SCAN THIS MISSION" on DONE rows unless true. Always true when no rep is in context (admin simulator).'
        timezone:
          type: string
          description: IANA time zone the business day was resolved in.
        business_day:
          type: string
          description: "The CURRENT business day (`YYYY-MM-DD`) the windows are anchored to — the rep's stamping context when a rep is in scope, else the company's."
        visit_id:
          type: string
          nullable: true
          description: "The device visit id the read is scoped to — null when `visit` was not sent."
        rules:
          type: array
          description: Every enabled rule with per-condition verdicts (simulator food).
          items:
            type: object
            properties:
              _id: { type: string }
              name: { type: string }
              mission_sets:
                type: array
                description: The sets the rule assigns, each with its demand.
                items:
                  type: object
                  properties:
                    mission_set: { type: string }
                    set_name:
                      type: string
                      description: Absent when the set is disabled / deleted.
                    requirement_mode:
                      $ref: "#/components/schemas/RequirementMode"
              requirement_mode:
                allOf:
                  - $ref: "#/components/schemas/RequirementMode"
                description: "The STRICTEST of the rule's `mission_sets[]` lines."
              matched:
                type: boolean
                description: "Every condition line holds (AND); zero lines = true."
              frequency:
                $ref: "#/components/schemas/RuleFrequency"
              conditions:
                type: array
                items:
                  type: object
                  properties:
                    key:
                      type: string
                      enum:
                        [
                          client,
                          client_tag,
                          client_channel,
                          assigned_to,
                          chain,
                          area_tag,
                          team,
                        ]
                    operator:
                      type: string
                      enum: [in, nin]
                    value:
                      type: array
                      items: { type: string }
                    matched: { type: boolean }
        missions:
          type: array
          description: The ASSIGNED missions only (via matched rules' sets), sorted by name.
          items:
            type: object
            properties:
              _id: { type: string }
              name: { type: string }
              min_score:
                type: number
                description: "0..1 — the weighted mission score a scan must reach to complete the mission."
              category:
                type: string
                description: "Optional detection category — stamp it on the session started FROM this mission (`session.category`) so the pipeline auto-analyzes it."
              category_name: { type: string }
              via_sets:
                type: array
                description: Names of the matched sets that carry this mission.
                items: { type: string }
              requirement_mode:
                allOf:
                  - $ref: "#/components/schemas/RequirementMode"
                description: "The STRICTEST demand across `requirements[]` — what the mobile enforces when the rep ends the visit; not_required means the mission is optional here."
              requirements:
                type: array
                description: One demand per matching rule × set line.
                items:
                  type: object
                  properties:
                    rule: { type: string }
                    rule_name: { type: string }
                    set: { type: string }
                    set_name: { type: string }
                    frequency:
                      $ref: "#/components/schemas/RuleFrequency"
                    requirement_mode:
                      $ref: "#/components/schemas/RequirementMode"
                    window_start:
                      type: number
                      nullable: true
                      description: "Start (ms) of the first BUSINESS day of the demand's current window. null for a visit-scoped every_visit demand (matched by visit, not by time)."
                    completions:
                      type: number
                      description: "Completions whose business day falls inside the window — for a visit-scoped every_visit demand, the completions stamped with that visit id."
                    satisfied:
                      type: boolean
                      description: "completions ≥ frequency.times (a visit-scoped every_visit demand: ≥ 1) — regardless of requirement_mode."
              done:
                type: boolean
                description: "Every requirement satisfied (every_visit: completed during the scoped visit, or in the current business day when no visit is given) — mode-agnostic; the mobile combines it with requirement_mode."
