openapi: 3.1.0
info:
  title: Tango BU User Roles Reconcile API
  version: "0.9"
  description: |
    Async REST API: client GETs user accounts and their associated data from
    Tango for reconciliation. Same endpoint as the User Provisioning API
    (`/v1/usermgmt/batch/users`) but accessed via GET. Two-call pattern with
    `gu_id` and pagination (1000 records/page — note: smaller than other
    Tango APIs' default of 5000).

    Filter options (mutually exclusive — only one at a time):
      - No params → all users.
      - `user_name` → specific user.
      - `status` → users with that status.

    Batch refresh runs once per day at a mutually-agreed time. The data
    integration API may be invoked at most once per day; additional same-day
    calls generate a new `gu_id` but return no data. Status polling is
    unrestricted.
  x-direction: outbound
  x-source: Tango
  x-destination: Client System
servers:
  - url: https://api.tangoanalytics.com
    description: Production
  - url: https://testapi.tangoanalytics.com
    description: Test (SIT/UAT)
tags:
  - name: User Reconcile
  - name: Status
paths:
  /v1/usermgmt/batch/users:
    get:
      summary: Initiate user reconcile export (async)
      operationId: getUsersReconcileBU
      tags: [User Reconcile]
      parameters:
        - $ref: "#/components/parameters/XClient"
        - $ref: "#/components/parameters/XApiKey"
        - in: query
          name: status
          required: false
          schema:
            type: string
            enum: [A, N]
          description: Filter to users with the given status. Mutually exclusive with `user_name`.
        - in: query
          name: user_name
          required: false
          schema: { type: string }
          description: Filter to a specific user. Mutually exclusive with `status`.
      responses:
        "200":
          description: Async ack with gu_id.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/AcceptedResponse"
  /v1/batch/status:
    get:
      summary: Retrieve paginated user records by gu_id
      operationId: getUsersByGuId
      tags: [Status]
      parameters:
        - $ref: "#/components/parameters/XClient"
        - $ref: "#/components/parameters/XApiKey"
        - in: query
          name: gu_id
          required: true
          schema: { type: string, format: uuid }
        - in: query
          name: page_no
          required: false
          schema: { type: integer, minimum: 1 }
      responses:
        "200":
          description: Paginated user records.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/UserPage"
components:
  securitySchemes:
    xClientHeader: { type: apiKey, in: header, name: x-client }
    xApiKeyHeader: { type: apiKey, in: header, name: x-api-key }
  parameters:
    XClient:
      name: x-client
      in: header
      required: true
      schema: { type: string }
    XApiKey:
      name: x-api-key
      in: header
      required: true
      schema: { type: string }
  schemas:
    AcceptedResponse:
      type: object
      required: [status]
      properties:
        status: { type: string, enum: [OK, Error] }
        status_message: { type: string }
        gu_id: { type: string, format: uuid }
    UserPage:
      type: object
      required: [status, records]
      properties:
        status: { type: string, enum: [PROCESSING, COMPLETED, ERROR] }
        total_records: { type: integer }
        total_pages: { type: integer }
        page_size:
          type: integer
          default: 1000
          description: |
            NOTE: this endpoint uses 1000 records/page (smaller than other
            Tango outbound APIs' 5000). Updated in source v0.7.
        page_no: { type: integer }
        records:
          type: array
          items:
            $ref: "#/components/schemas/UserRecord"
    UserRecord:
      type: object
      description: |
        User record with full provisioning state. Mirrors the BU User
        Provisioning Inbound shape, plus audit fields (creation_date,
        created_by, last_update_date, last_updated_by, last_login_date)
        added in source v0.6.
      additionalProperties: true
      properties:
        user_name: { type: string }
        first_name: { type: string }
        last_name: { type: string }
        email: { type: string, format: email }
        user_type: { type: string, enum: [CONTRACTOR, CORPORATE, GENERAL_USER] }
        sso: { type: string, enum: [Y, N] }
        status: { type: string, enum: [A, N] }
        mobile_access: { type: string, enum: [Y, N] }
        default_brand: { type: string }
        default_bu: { type: string }
        default_country: { type: string }
        default_language:
          type: string
          description: "Concatenation of 2-letter language + 2-letter country (e.g., `en-US`)."
        data_access_level: { type: string, enum: [F, R, RR] }
        creation_date: { type: string, description: "MM/DD/YYYY." }
        created_by: { type: string }
        last_login_date: { type: string, description: "MM/DD/YYYY." }
        last_update_date: { type: string, description: "MM/DD/YYYY." }
        last_updated_by: { type: string }
        entity_security:
          type: array
          items:
            type: object
            properties:
              bu_name: { type: string }
              country: { type: string, description: "ISO 3-letter." }
              entity_type:
                type: string
                description: "Tango entity (TARGET, SITE, LEASE, etc.)."
              full_access: { type: string, enum: [Y, N] }
              restrictions:
                type: array
                items:
                  type: object
                  properties:
                    attribute: { type: string, enum: [SEC_ATTR1, SEC_ATTR2] }
                    value: { type: string }
        brands:
          type: array
          description: |
            Per-brand audit records. Structure (per source): `brands[i].brand_name`
            is an object with `name`, `creation_date`, `created_by`,
            `last_update_date`, `last_updated_by` — note the inner object key
            is the literal string `brand_name` rather than a flatter shape.
          items:
            type: object
            properties:
              brand_name:
                type: object
                properties:
                  name: { type: string, description: "Comma-separated brand names." }
                  creation_date: { type: string }
                  created_by: { type: string }
                  last_update_date: { type: string }
                  last_updated_by: { type: string }
        bu:
          type: array
          items:
            type: object
            properties:
              brand_name: { type: string }
              bu_name:
                type: object
                properties:
                  name: { type: string }
                  creation_date: { type: string }
                  created_by: { type: string }
                  last_update_date: { type: string }
                  last_updated_by: { type: string }
        country:
          type: array
          items:
            type: object
            properties:
              brand_name: { type: string }
              bu_name: { type: string }
              country_name:
                type: object
                properties:
                  name: { type: string }
                  creation_date: { type: string }
                  created_by: { type: string }
                  last_update_date: { type: string }
                  last_updated_by: { type: string }
        roles:
          type: array
          items:
            type: object
            additionalProperties: true
            description: |
              Per-role audit records. Structure mirrors brands/bu/country
              with nested name/audit objects.
security:
  - xClientHeader: []
    xApiKeyHeader: []
