openapi: 3.1.0
info:
  title: Agent Elite Provisioning API
  version: 1.0.0
  description: |
    Agent Elite exposes tenant-scoped user provisioning, import job status, and SCIM 2.0
    provisioning endpoints, and SAML 2.0 single sign on configured with the Agent Elite team.
    Outbound webhooks deliver recording, scorecard, agent onboarding, and test events to an
    HTTPS endpoint configured by an organization manager. There is no API for reading recordings
    or scorecards. SAML SSO is supported by the platform, and no identity provider has been
    connected in production yet.
servers:
  - url: https://www.agentelite.app
    description: Agent Elite production
security:
  - bearerAuth: []
tags:
  - name: Provisioning
  - name: SCIM
  - name: Webhooks
paths:
  /api/v1/users:
    post:
      tags: [Provisioning]
      summary: Start a bulk user import
      description: Requires the users:write scope. The import is tenant-scoped to the API key.
      operationId: createUserImport
      x-required-scope: users:write
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/ProvisionUsersRequest'
      responses:
        '202':
          description: Import accepted
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ProvisionUsersResponse'
        '400':
          $ref: '#/components/responses/JsonBadRequest'
        '401':
          $ref: '#/components/responses/JsonUnauthorized'
        '403':
          $ref: '#/components/responses/JsonForbidden'
        '429':
          $ref: '#/components/responses/JsonRateLimited'
        '500':
          $ref: '#/components/responses/JsonServerError'
  /api/v1/users/jobs/{id}:
    get:
      tags: [Provisioning]
      summary: Read a user import job
      description: A valid active API key can read a job only when it belongs to the organization and API key making the request. This route does not independently require the users:write scope.
      operationId: getUserImportJob
      parameters:
        - $ref: '#/components/parameters/JobId'
      responses:
        '200':
          description: Import job
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ImportJob'
        '401':
          $ref: '#/components/responses/JsonUnauthorized'
        '404':
          $ref: '#/components/responses/JsonNotFound'
        '429':
          $ref: '#/components/responses/JsonRateLimited'
        '500':
          $ref: '#/components/responses/JsonServerError'
  /scim/v2/ServiceProviderConfig:
    get:
      tags: [SCIM]
      summary: Read SCIM service capabilities
      operationId: getScimServiceProviderConfig
      x-required-scope: scim
      responses:
        '200':
          description: SCIM service capabilities
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ServiceProviderConfig'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
  /scim/v2/Schemas:
    get:
      tags: [SCIM]
      summary: Read supported SCIM schemas
      operationId: getScimSchemas
      x-required-scope: scim
      responses:
        '200':
          description: Supported SCIM schemas
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ScimSchemaList'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
  /scim/v2/ResourceTypes:
    get:
      tags: [SCIM]
      summary: Read supported SCIM resource types
      operationId: getScimResourceTypes
      x-required-scope: scim
      responses:
        '200':
          description: Supported SCIM resource types
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ResourceTypeList'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
  /scim/v2/Users:
    get:
      tags: [SCIM]
      summary: List SCIM users
      operationId: listScimUsers
      x-required-scope: scim
      parameters:
        - $ref: '#/components/parameters/ScimFilter'
        - $ref: '#/components/parameters/StartIndex'
        - $ref: '#/components/parameters/Count'
      responses:
        '200':
          description: SCIM user list
          content:
            application/scim+json:
              schema:
                $ref: '#/components/schemas/ScimUserList'
        '400':
          $ref: '#/components/responses/ScimBadRequest'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
    post:
      tags: [SCIM]
      summary: Create a SCIM user
      operationId: createScimUser
      x-required-scope: scim
      requestBody:
        required: true
        content:
          application/scim+json:
            schema:
              $ref: '#/components/schemas/ScimUserInput'
      responses:
        '200':
          description: SCIM user created
          content:
            application/scim+json:
              schema:
                $ref: '#/components/schemas/ScimUser'
        '400':
          $ref: '#/components/responses/ScimBadRequest'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '409':
          $ref: '#/components/responses/ScimConflict'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
  /scim/v2/Users/{id}:
    get:
      tags: [SCIM]
      summary: Read a SCIM user
      operationId: getScimUser
      x-required-scope: scim
      parameters:
        - $ref: '#/components/parameters/ResourceId'
      responses:
        '200':
          description: SCIM user
          content:
            application/scim+json:
              schema:
                $ref: '#/components/schemas/ScimUser'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '404':
          $ref: '#/components/responses/ScimNotFound'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
    put:
      tags: [SCIM]
      summary: Replace a SCIM user
      operationId: replaceScimUser
      x-required-scope: scim
      parameters:
        - $ref: '#/components/parameters/ResourceId'
      requestBody:
        required: true
        content:
          application/scim+json:
            schema:
              $ref: '#/components/schemas/ScimUserInput'
      responses:
        '200':
          description: Updated SCIM user
          content:
            application/scim+json:
              schema:
                $ref: '#/components/schemas/ScimUser'
        '400':
          $ref: '#/components/responses/ScimBadRequest'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '404':
          $ref: '#/components/responses/ScimNotFound'
        '409':
          $ref: '#/components/responses/ScimConflict'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
    patch:
      tags: [SCIM]
      summary: Update a SCIM user
      operationId: updateScimUser
      x-required-scope: scim
      parameters:
        - $ref: '#/components/parameters/ResourceId'
      requestBody:
        required: true
        content:
          application/scim+json:
            schema:
              $ref: '#/components/schemas/ScimPatchRequest'
      responses:
        '200':
          description: Updated SCIM user
          content:
            application/scim+json:
              schema:
                $ref: '#/components/schemas/ScimUser'
        '400':
          $ref: '#/components/responses/ScimBadRequest'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '404':
          $ref: '#/components/responses/ScimNotFound'
        '409':
          $ref: '#/components/responses/ScimConflict'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
    delete:
      tags: [SCIM]
      summary: Deactivate a SCIM user
      operationId: deleteScimUser
      x-required-scope: scim
      parameters:
        - $ref: '#/components/parameters/ResourceId'
      responses:
        '204':
          description: User deactivated
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '404':
          $ref: '#/components/responses/ScimNotFound'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
  /scim/v2/Groups:
    get:
      tags: [SCIM]
      summary: List SCIM groups
      operationId: listScimGroups
      x-required-scope: scim
      parameters:
        - $ref: '#/components/parameters/GroupFilter'
        - $ref: '#/components/parameters/StartIndex'
        - $ref: '#/components/parameters/Count'
      responses:
        '200':
          description: SCIM group list
          content:
            application/scim+json:
              schema:
                $ref: '#/components/schemas/ScimGroupList'
        '400':
          $ref: '#/components/responses/ScimBadRequest'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
    post:
      tags: [SCIM]
      summary: Create a SCIM group
      operationId: createScimGroup
      x-required-scope: scim
      requestBody:
        required: true
        content:
          application/scim+json:
            schema:
              $ref: '#/components/schemas/ScimGroupInput'
      responses:
        '201':
          description: SCIM group created
          content:
            application/scim+json:
              schema:
                $ref: '#/components/schemas/ScimGroup'
        '400':
          $ref: '#/components/responses/ScimBadRequest'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '409':
          $ref: '#/components/responses/ScimConflict'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
  /scim/v2/Groups/{id}:
    get:
      tags: [SCIM]
      summary: Read a SCIM group
      operationId: getScimGroup
      x-required-scope: scim
      parameters:
        - $ref: '#/components/parameters/ResourceId'
      responses:
        '200':
          description: SCIM group
          content:
            application/scim+json:
              schema:
                $ref: '#/components/schemas/ScimGroup'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '404':
          $ref: '#/components/responses/ScimNotFound'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
    put:
      tags: [SCIM]
      summary: Replace a SCIM group
      operationId: replaceScimGroup
      x-required-scope: scim
      parameters:
        - $ref: '#/components/parameters/ResourceId'
      requestBody:
        required: true
        content:
          application/scim+json:
            schema:
              $ref: '#/components/schemas/ScimGroupInput'
      responses:
        '200':
          description: Updated SCIM group
          content:
            application/scim+json:
              schema:
                $ref: '#/components/schemas/ScimGroup'
        '400':
          $ref: '#/components/responses/ScimBadRequest'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '404':
          $ref: '#/components/responses/ScimNotFound'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
    patch:
      tags: [SCIM]
      summary: Update a SCIM group
      operationId: updateScimGroup
      x-required-scope: scim
      parameters:
        - $ref: '#/components/parameters/ResourceId'
      requestBody:
        required: true
        content:
          application/scim+json:
            schema:
              $ref: '#/components/schemas/ScimPatchRequest'
      responses:
        '200':
          description: Updated SCIM group
          content:
            application/scim+json:
              schema:
                $ref: '#/components/schemas/ScimGroup'
        '400':
          $ref: '#/components/responses/ScimBadRequest'
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '404':
          $ref: '#/components/responses/ScimNotFound'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
    delete:
      tags: [SCIM]
      summary: Delete a SCIM group
      operationId: deleteScimGroup
      x-required-scope: scim
      parameters:
        - $ref: '#/components/parameters/ResourceId'
      responses:
        '204':
          description: Group deleted
        '401':
          $ref: '#/components/responses/ScimUnauthorized'
        '403':
          $ref: '#/components/responses/ScimForbidden'
        '404':
          $ref: '#/components/responses/ScimNotFound'
        '429':
          $ref: '#/components/responses/ScimRateLimited'
        '500':
          $ref: '#/components/responses/ScimServerError'
webhooks:
  recordingCompleted:
    post:
      tags: [Webhooks]
      summary: Recording completed delivery
      description: |
        Agent Elite sends this signed event when a recording receives its transcript.
        Agent Elite attempts delivery once and then retries five times at 1 minute, 5 minutes,
        30 minutes, 2 hours, and 6 hours, six attempts in total. An endpoint is disabled after
        20 consecutive failures.
      operationId: receiveRecordingCompleted
      security: []
      parameters:
        - $ref: '#/components/parameters/WebhookEvent'
        - $ref: '#/components/parameters/WebhookDelivery'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
        - $ref: '#/components/parameters/WebhookUserAgent'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEnvelope'
            example:
              id: 2f6a1c67-5d4b-4b8f-a1d6-5d5bf2a91f44
              event: recording.completed
              created_at: '2026-01-01T12:00:00.000Z'
              org_id: 7b2a8f2e-9e41-4d5d-a5d5-0f3e0d15f111
              data:
                recording_id: 9b7b11a4-4a75-4d6a-a2e3-6fca3f7ab222
                org_id: 7b2a8f2e-9e41-4d5d-a5d5-0f3e0d15f111
                agent_id: 4d3b4d55-c0ae-47fc-9cb3-a5a4b64c7333
                title: First appointment
                source: phone
                duration_seconds: 420
                category: life
                location_state: KY
                created_at: '2026-01-01T11:58:00.000Z'
      responses:
        '2XX':
          description: Any 2xx response marks delivery complete.
  scorecardGenerated:
    post:
      tags: [Webhooks]
      summary: Scorecard generated delivery
      description: Delivery retries follow the schedule documented for recording.completed.
      operationId: receiveScorecardGenerated
      security: []
      parameters:
        - $ref: '#/components/parameters/WebhookEvent'
        - $ref: '#/components/parameters/WebhookDelivery'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
        - $ref: '#/components/parameters/WebhookUserAgent'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEnvelope'
            example:
              id: 72d2eb2f-eaaa-45c0-b6c9-8e3f8c3a1dd1
              event: scorecard.generated
              created_at: '2026-01-01T12:00:00.000Z'
              org_id: 7b2a8f2e-9e41-4d5d-a5d5-0f3e0d15f111
              data:
                scorecard_id: 3c5d1c88-3a2c-4bc6-9f2d-cd9a3c0ef222
                recording_id: 9b7b11a4-4a75-4d6a-a2e3-6fca3f7ab222
                org_id: 7b2a8f2e-9e41-4d5d-a5d5-0f3e0d15f111
                agent_id: 4d3b4d55-c0ae-47fc-9cb3-a5a4b64c7333
                overall_score: 8.5
                call_stage_reached: close
                stage_index: 4
                scoring_version: applicability_v2
                source: field
                created_at: '2026-01-01T12:00:00.000Z'
      responses:
        '2XX':
          description: Any 2xx response marks delivery complete.
  agentOnboarded:
    post:
      tags: [Webhooks]
      summary: Agent onboarded delivery
      description: Delivery retries follow the schedule documented for recording.completed.
      operationId: receiveAgentOnboarded
      security: []
      parameters:
        - $ref: '#/components/parameters/WebhookEvent'
        - $ref: '#/components/parameters/WebhookDelivery'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
        - $ref: '#/components/parameters/WebhookUserAgent'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEnvelope'
            example:
              id: 9c8e6b71-9e44-4e24-9bc6-7fbc2f4ce333
              event: agent.onboarded
              created_at: '2026-01-01T12:00:00.000Z'
              org_id: 7b2a8f2e-9e41-4d5d-a5d5-0f3e0d15f111
              data:
                profile_id: 4d3b4d55-c0ae-47fc-9cb3-a5a4b64c7333
                org_id: 7b2a8f2e-9e41-4d5d-a5d5-0f3e0d15f111
                email: agent@example.com
                full_name: New Agent
                role: agent
                supervisor_id: 1f4b1a8c-5d8d-4c1d-a0d2-f5bd6a8a4444
                created_at: '2026-01-01T12:00:00.000Z'
      responses:
        '2XX':
          description: Any 2xx response marks delivery complete.
  webhookTest:
    post:
      tags: [Webhooks]
      summary: Webhook test delivery
      description: A test delivery is sent when an organization manager selects Send test event.
      operationId: receiveWebhookTest
      security: []
      parameters:
        - $ref: '#/components/parameters/WebhookEvent'
        - $ref: '#/components/parameters/WebhookDelivery'
        - $ref: '#/components/parameters/WebhookTimestamp'
        - $ref: '#/components/parameters/WebhookSignature'
        - $ref: '#/components/parameters/WebhookUserAgent'
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WebhookEnvelope'
            example:
              id: a4f14d77-6d5a-47d2-9ebf-5c8e7d5b5555
              event: webhook.test
              created_at: '2026-01-01T12:00:00.000Z'
              org_id: 7b2a8f2e-9e41-4d5d-a5d5-0f3e0d15f111
              data:
                message: Test event from Agent Elite
                endpoint_id: 6e2e7e0c-5d93-4dfd-8c6d-3e4a1e4c6666
      responses:
        '2XX':
          description: Any 2xx response marks delivery complete.
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: ae_live_
      description: Tenant API key beginning with ae_live_. Create it in Settings as an organization owner or corporate admin. The key is hashed with SHA-256 before tenant lookup.
  parameters:
    JobId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    ResourceId:
      name: id
      in: path
      required: true
      schema:
        type: string
        format: uuid
    ScimFilter:
      name: filter
      in: query
      required: false
      description: Exact equality filter. Supported forms are userName eq "value" and externalId eq "value".
      schema:
        type: string
    GroupFilter:
      name: filter
      in: query
      required: false
      description: Exact equality filter in the form displayName eq "value".
      schema:
        type: string
    StartIndex:
      name: startIndex
      in: query
      required: false
      description: One-based result offset. Values below 1 become 1.
      schema:
        type: integer
        minimum: 1
        default: 1
    Count:
      name: count
      in: query
      required: false
      description: Page size. The route defaults to 100 and caps the value at 200.
      schema:
        type: integer
        minimum: 0
        maximum: 200
        default: 100
    WebhookEvent:
      name: X-AgentElite-Event
      in: header
      required: true
      schema:
        type: string
      description: Event name such as recording.completed, scorecard.generated, agent.onboarded, or webhook.test.
    WebhookDelivery:
      name: X-AgentElite-Delivery
      in: header
      required: true
      schema:
        type: string
        format: uuid
      description: Delivery UUID.
    WebhookTimestamp:
      name: X-AgentElite-Timestamp
      in: header
      required: true
      schema:
        type: string
      description: Unix timestamp in seconds used in the signature input.
    WebhookSignature:
      name: X-AgentElite-Signature
      in: header
      required: true
      schema:
        type: string
        pattern: '^v1=[0-9a-f]+$'
      description: HMAC-SHA256 signature in the form v1=hex over timestamp.body.
    WebhookUserAgent:
      name: User-Agent
      in: header
      required: true
      schema:
        type: string
        enum: [AgentElite-Webhooks/1]
  responses:
    JsonBadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/JsonError'
    JsonUnauthorized:
      description: Invalid API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/JsonError'
    JsonForbidden:
      description: Missing users:write scope
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/JsonError'
    JsonRateLimited:
      description: Rate limit exceeded
      headers:
        Retry-After:
          description: Retry after 60 seconds.
          schema:
            type: string
            example: '60'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/JsonError'
    JsonNotFound:
      description: Job not found for this API key
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/JsonError'
    JsonServerError:
      description: Server error
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/JsonError'
    ScimBadRequest:
      description: Invalid SCIM request
      content:
        application/scim+json:
          schema:
            $ref: '#/components/schemas/ScimError'
    ScimUnauthorized:
      description: Invalid SCIM bearer token
      content:
        application/scim+json:
          schema:
            $ref: '#/components/schemas/ScimError'
    ScimForbidden:
      description: Missing scim scope
      content:
        application/scim+json:
          schema:
            $ref: '#/components/schemas/ScimError'
    ScimRateLimited:
      description: Rate limit exceeded. The current SCIM helper does not include a Retry-After header.
      content:
        application/scim+json:
          schema:
            $ref: '#/components/schemas/ScimError'
    ScimNotFound:
      description: Resource not found in this organization
      content:
        application/scim+json:
          schema:
            $ref: '#/components/schemas/ScimError'
    ScimConflict:
      description: Resource conflict
      content:
        application/scim+json:
          schema:
            $ref: '#/components/schemas/ScimError'
    ScimServerError:
      description: Server error
      content:
        application/scim+json:
          schema:
            $ref: '#/components/schemas/ScimError'
  schemas:
    JsonError:
      type: object
      required: [error]
      properties:
        error:
          type: string
    WebhookEnvelope:
      type: object
      required: [id, event, created_at, org_id, data]
      properties:
        id:
          type: string
          format: uuid
        event:
          type: string
          enum: [recording.completed, scorecard.generated, agent.onboarded, webhook.test]
        created_at:
          type: string
          format: date-time
        org_id:
          type: string
          format: uuid
        data:
          type: object
          additionalProperties: true
    ProvisionUsersRequest:
      type: object
      required: [users]
      properties:
        users:
          type: array
          minItems: 1
          maxItems: 1000
          items:
            $ref: '#/components/schemas/ProvisionUserInput'
        welcome_mode:
          type: string
          enum: [invite_email, none]
          default: invite_email
        wave_size:
          type: integer
          minimum: 1
          maximum: 1000
          default: 100
        wave_interval_minutes:
          type: integer
          minimum: 0
          maximum: 1440
          default: 60
    ProvisionUserInput:
      type: object
      properties:
        email:
          type: string
          format: email
        full_name:
          type: string
        role:
          type: string
          enum: [owner, regional_director, agency_director, general_agent, supervising_agent, agent, individual]
          default: agent
        supervisor_email:
          type: string
          format: email
        division:
          type: string
        title:
          type: string
    ProvisionUsersResponse:
      type: object
      required: [job_id, status, total, ready, skipped, invalid, invalid_rows]
      properties:
        job_id:
          type: string
          format: uuid
        status:
          type: string
          enum: [processing, completed]
        total:
          type: integer
        ready:
          type: integer
        skipped:
          type: integer
        invalid:
          type: integer
        invalid_rows:
          type: array
          items:
            $ref: '#/components/schemas/InvalidRow'
    InvalidRow:
      type: object
      required: [row_number, email, error]
      properties:
        row_number:
          type: integer
        email:
          type: string
        error:
          type: string
    ImportJob:
      type: object
      required: [id, status, total_rows, ready_rows, invalid_rows, created_count, skipped_count, failed_count, error, created_at, completed_at]
      properties:
        id:
          type: string
          format: uuid
        status:
          type: string
          enum: [validating, awaiting_confirm, processing, completed, failed, cancelled]
        total_rows:
          type: integer
        ready_rows:
          type: integer
        invalid_rows:
          type: integer
        created_count:
          type: integer
        skipped_count:
          type: integer
        failed_count:
          type: integer
        error:
          type: [string, 'null']
        created_at:
          type: string
          format: date-time
        completed_at:
          type: [string, 'null']
          format: date-time
    ScimError:
      type: object
      required: [schemas, status, detail]
      properties:
        schemas:
          type: array
          items:
            type: string
          example: [urn:ietf:params:scim:api:messages:2.0:Error]
        status:
          type: string
        detail:
          type: string
    ServiceProviderConfig:
      type: object
      properties:
        schemas:
          type: array
          items: { type: string }
        patch:
          type: object
          properties:
            supported: { type: boolean }
        bulk:
          type: object
          properties:
            supported: { type: boolean }
            maxOperations: { type: integer }
            maxPayloadSize: { type: integer }
        filter:
          type: object
          properties:
            supported: { type: boolean }
            maxResults: { type: integer }
        changePassword:
          type: object
          properties:
            supported: { type: boolean }
        sort:
          type: object
          properties:
            supported: { type: boolean }
        etag:
          type: object
          properties:
            supported: { type: boolean }
        authenticationSchemes:
          type: array
          items:
            type: object
            properties:
              type: { type: string }
              name: { type: string }
              description: { type: string }
    ScimSchemaList:
      type: object
      properties:
        schemas:
          type: array
          items: { type: string }
        totalResults:
          type: integer
        Resources:
          type: array
          items:
            type: object
            properties:
              id: { type: string }
              name: { type: string }
              description: { type: string }
              attributes:
                type: array
                items:
                  $ref: '#/components/schemas/ScimSchemaAttribute'
    ScimSchemaAttribute:
      type: object
      properties:
        name: { type: string }
        type: { type: string }
        multiValued: { type: boolean }
        required: { type: boolean }
        mutability: { type: string }
        returned: { type: string }
        uniqueness: { type: string }
    ResourceTypeList:
      type: object
      properties:
        schemas:
          type: array
          items: { type: string }
        totalResults:
          type: integer
        Resources:
          type: array
          items:
            type: object
            required: [id, name, endpoint, schema]
            properties:
              id: { type: string }
              name: { type: string }
              endpoint: { type: string }
              schema: { type: string }
    ScimUserInput:
      type: object
      required: [userName]
      properties:
        schemas:
          type: array
          items: { type: string }
        userName:
          type: string
          format: email
        externalId:
          type: [string, 'null']
        displayName:
          type: string
        name:
          type: object
          properties:
            formatted: { type: string }
            givenName: { type: string }
            familyName: { type: string }
        title:
          type: [string, 'null']
        active:
          type: boolean
          default: true
        urn:ietf:params:scim:schemas:extension:enterprise:2.0:User:
          $ref: '#/components/schemas/EnterpriseUser'
    EnterpriseUser:
      type: object
      properties:
        manager:
          $ref: '#/components/schemas/ScimManager'
        division:
          type: string
        department:
          type: string
    ScimManager:
      type: object
      properties:
        value:
          type: string
        displayName:
          type: string
    ScimUser:
      allOf:
        - $ref: '#/components/schemas/ScimUserInput'
        - type: object
          required: [id, schemas, userName, active, groups, meta]
          properties:
            id: { type: string, format: uuid }
            emails:
              type: array
              items:
                type: object
                properties:
                  value: { type: string, format: email }
                  primary: { type: boolean }
                  type: { type: string }
            groups:
              type: array
              items:
                $ref: '#/components/schemas/ScimGroupMember'
            enterprise:
              $ref: '#/components/schemas/EnterpriseUser'
            meta:
              $ref: '#/components/schemas/ScimMeta'
    ScimMeta:
      type: object
      properties:
        resourceType: { type: string }
        created: { type: string, format: date-time }
        lastModified: { type: string, format: date-time }
        location: { type: string, format: uri }
    ScimGroupMember:
      type: object
      required: [value, display]
      properties:
        value: { type: string, format: uuid }
        display: { type: string }
    ScimUserList:
      type: object
      required: [schemas, totalResults, startIndex, itemsPerPage, Resources]
      properties:
        schemas:
          type: array
          items: { type: string }
        totalResults: { type: integer }
        startIndex: { type: integer }
        itemsPerPage: { type: integer }
        Resources:
          type: array
          items:
            $ref: '#/components/schemas/ScimUser'
    ScimGroupInput:
      type: object
      required: [displayName]
      properties:
        schemas:
          type: array
          items: { type: string }
        displayName: { type: string }
        externalId:
          type: [string, 'null']
        members:
          type: array
          items:
            $ref: '#/components/schemas/ScimGroupMember'
    ScimGroup:
      allOf:
        - $ref: '#/components/schemas/ScimGroupInput'
        - type: object
          required: [id, schemas, displayName, members, meta]
          properties:
            id: { type: string, format: uuid }
            members:
              type: array
              items:
                $ref: '#/components/schemas/ScimGroupMember'
            meta:
              $ref: '#/components/schemas/ScimMeta'
    ScimGroupList:
      type: object
      required: [schemas, totalResults, startIndex, itemsPerPage, Resources]
      properties:
        schemas:
          type: array
          items: { type: string }
        totalResults: { type: integer }
        startIndex: { type: integer }
        itemsPerPage: { type: integer }
        Resources:
          type: array
          items:
            $ref: '#/components/schemas/ScimGroup'
    ScimPatchRequest:
      type: object
      description: |
        User PATCH paths handled by the route are active, name.givenName, name.familyName,
        displayName, title, externalId, userName, manager,
        urn:ietf:params:scim:schemas:extension:enterprise:2.0:User.manager,
        urn:ietf:params:scim:schemas:extension:enterprise:2.0:User.division, and
        urn:ietf:params:scim:schemas:extension:enterprise:2.0:User.department. Group PATCH
        handles displayName, members, and members[value eq "user-id"] removal paths.
      properties:
        schemas:
          type: array
          items: { type: string }
        Operations:
          type: array
          items:
            $ref: '#/components/schemas/ScimPatchOperation'
    ScimPatchOperation:
      type: object
      required: [op]
      properties:
        op:
          type: string
          enum: [add, replace, remove]
        path:
          type: string
        value: {}
