---
openapi: 3.1.1
info:
  title: SCIM API (Coming Soon)
  version: 0.0.1
  description: |
    BetterUp's SCIM 2.0 API (RFC 7643 / RFC 7644) for automated user and
    group provisioning and deprovisioning from your identity provider
    (e.g. Okta, Azure AD, OneLogin).

    Authenticate using OAuth 2.0 client credentials. Read operations
    require the `scim:user:read` / `scim:group:read` scope; write
    operations require `scim:user:write` / `scim:group:write`.

    Discovery endpoints (`ServiceProviderConfig`, `Schemas`,
    `ResourceTypes`) are unauthenticated per RFC 7644 §4.

    Download this spec: [https://api.betterup.co/docs/scim/spec](https://api.betterup.co/docs/scim/spec)
servers:
- url: https://{defaultHost}
  variables:
    defaultHost:
      default: api.betterup.co
components:
  securitySchemes:
    clientCredentials:
      type: oauth2
      description: OAuth 2.0 client credentials flow. Obtain a bearer token from `/oauth/token`
        and present it in the `Authorization` header.
      flows:
        clientCredentials:
          tokenUrl: "/oauth/token"
          scopes:
            scim:user:read: Read SCIM user resources
            scim:user:write: Create, update, and deactivate SCIM user resources
            scim:group:read: Read SCIM group resources
            scim:group:write: Create, update, and delete SCIM group resources
  schemas:
    User:
      type: object
      description: A SCIM 2.0 User resource (RFC 7643 §4.1) representing a BetterUp
        member provisioned through SCIM.
      properties:
        schemas:
          type: array
          items:
            type: string
          description: SCIM schema URIs identifying the resource type.
          example:
          - urn:ietf:params:scim:schemas:core:2.0:User
        id:
          type: string
          format: uuid
          description: Server-assigned, immutable identifier for the user. Generated
            by BetterUp.
          readOnly: true
        externalId:
          type:
          - string
          - 'null'
          description: Identifier assigned by the identity provider. Used by the IdP
            to correlate its own user identifier with the BetterUp user.
        userName:
          type: string
          format: email
          description: Unique identifier for the user. BetterUp uses the user's primary
            email address as the userName.
          example: jane.doe@example.com
        name:
          type: object
          description: The components of the user's name.
          properties:
            givenName:
              type:
              - string
              - 'null'
              description: The given (first) name of the user.
            familyName:
              type:
              - string
              - 'null'
              description: The family (last) name of the user.
        emails:
          type: array
          description: Email addresses for the user. The primary email is mirrored
            from `userName`; an optional `type=other` entry can carry an alternate
            address.
          items:
            type: object
            properties:
              value:
                type: string
                format: email
              type:
                type: string
                example: other
              primary:
                type: boolean
        active:
          type: boolean
          description: Whether the user is active. Setting this to `false` (via PATCH
            or PUT) soft-deactivates the user; setting it back to `true` reactivates
            the user. DELETE has the same soft-deactivation effect.
        title:
          type:
          - string
          - 'null'
          description: The user's job title.
        meta:
          type: object
          description: Resource metadata (RFC 7643 §3.1).
          properties:
            resourceType:
              type: string
              example: User
            created:
              type: string
              format: date-time
            lastModified:
              type: string
              format: date-time
            location:
              type: string
              description: URI of the resource being returned.
          readOnly: true
      required:
      - schemas
      - id
      - userName
    Group:
      type: object
      description: A SCIM 2.0 Group resource (RFC 7643 §4.2) representing a collection
        of BetterUp members provisioned through SCIM. Membership is the only SCIM-mutable
        surface; role bindings attached to a group are managed in the BetterUp admin
        UI, not through SCIM.
      properties:
        schemas:
          type: array
          items:
            type: string
          description: SCIM schema URIs identifying the resource type.
          example:
          - urn:ietf:params:scim:schemas:core:2.0:Group
        id:
          type: string
          format: uuid
          description: Server-assigned, immutable identifier for the group. Generated
            by BetterUp.
          readOnly: true
        externalId:
          type:
          - string
          - 'null'
          description: Identifier assigned by the identity provider. Used by the IdP
            to correlate its own group identifier with the BetterUp group, and doubles
            as the idempotency key for POST within an organization.
        displayName:
          type: string
          description: Human-readable name for the group. Not required to be unique
            within an organization (RFC 7643 §4.2).
        members:
          type: array
          description: |
            SCIM-provisioned users belonging to this group. PUT and PATCH
            `replace` operations apply full-replace semantics; PATCH `add` and
            `remove` operations apply differentially.

            Every `value` must be the `id` of a SCIM-provisioned User in the
            same organization. UUIDs that do not resolve to a SCIM-linked user
            are rejected with 400 and the unresolved values are returned in
            the error envelope.
          items:
            type: object
            properties:
              value:
                type: string
                format: uuid
                description: The `id` of a SCIM User in the same organization.
              display:
                type: string
                description: Human-readable label for the member (server-rendered).
                readOnly: true
              type:
                type: string
                description: Resource type of the member. Only `User` is supported
                  — nested groups are not supported.
                enum:
                - User
                default: User
            required:
            - value
        meta:
          type: object
          description: Resource metadata (RFC 7643 §3.1).
          properties:
            resourceType:
              type: string
              example: Group
            created:
              type: string
              format: date-time
            lastModified:
              type: string
              format: date-time
            location:
              type: string
              description: URI of the resource being returned.
          readOnly: true
      required:
      - schemas
      - id
      - displayName
    ScimError:
      type: object
      description: SCIM 2.0 error envelope (RFC 7644 §3.12). Returned for all non-2xx
        responses.
      properties:
        schemas:
          type: array
          items:
            type: string
          example:
          - urn:ietf:params:scim:api:messages:2.0:Error
        status:
          type: string
          description: HTTP status code, expressed as a string.
          example: '404'
        scimType:
          type: string
          description: SCIM detail error keyword for 400 / 409 responses (RFC 7644
            §3.12 Table 9). Absent on other status codes.
          example: uniqueness
        detail:
          type: string
          description: Human-readable explanation of the error.
      required:
      - schemas
      - status
    ListResponse:
      type: object
      description: SCIM 2.0 ListResponse envelope (RFC 7644 §3.4.2).
      properties:
        schemas:
          type: array
          items:
            type: string
          example:
          - urn:ietf:params:scim:api:messages:2.0:ListResponse
        totalResults:
          type: integer
          description: Total number of resources matching the filter, across all pages.
        startIndex:
          type: integer
          description: 1-based index of the first result in this page.
        itemsPerPage:
          type: integer
          description: Number of resources returned in this page.
        Resources:
          type: array
          description: SCIM resources matching the request. The `meta.resourceType`
            field on each entry distinguishes `User` from `Group`; a given list response
            only contains one kind, matching the queried endpoint.
          items:
            oneOf:
            - "$ref": "#/components/schemas/User"
            - "$ref": "#/components/schemas/Group"
      required:
      - schemas
      - totalResults
      - Resources
security:
- clientCredentials:
  - scim:user:read
  - scim:user:write
  - scim:group:read
  - scim:group:write
paths:
  "/scim/v2/ServiceProviderConfig":
    get:
      summary: Service provider configuration
      operationId: showScimServiceProviderConfig
      description: Returns the SCIM 2.0 features supported by BetterUp (RFC 7644 §4).
        Unauthenticated.
      tags:
      - SCIM/Discovery
      security: []
      responses:
        '200':
          description: Service provider configuration
          content:
            application/scim+json:
              schema:
                type: object
                description: SCIM 2.0 ServiceProviderConfig resource.
  "/scim/v2/Schemas":
    get:
      summary: Supported schemas
      operationId: listScimSchemas
      description: Returns the SCIM schemas supported by BetterUp (RFC 7644 §4). Unauthenticated.
      tags:
      - SCIM/Discovery
      security: []
      responses:
        '200':
          description: List of supported schemas
          content:
            application/scim+json:
              schema:
                type: object
  "/scim/v2/ResourceTypes":
    get:
      summary: Supported resource types
      operationId: listScimResourceTypes
      description: Returns the SCIM resource types supported by BetterUp (RFC 7644
        §4). Unauthenticated.
      tags:
      - SCIM/Discovery
      security: []
      responses:
        '200':
          description: List of supported resource types
          content:
            application/scim+json:
              schema:
                type: object
  "/scim/v2/Users":
    get:
      summary: List users
      operationId: listScimUsers
      description: Returns a paginated list of SCIM-provisioned users in the caller's
        organization. Supports filtering and pagination per RFC 7644 §3.4.2.
      tags:
      - SCIM/Users
      security:
      - clientCredentials:
        - scim:user:read
      parameters:
      - name: filter
        in: query
        required: false
        schema:
          type: string
        description: SCIM filter expression (e.g. `userName eq "jane@example.com"`).
          Supports `id`, `userName`, `externalId`, `name.givenName`, `name.familyName`,
          `active`, and `emails.value`.
        example: userName eq "jane@example.com"
      - name: startIndex
        in: query
        required: false
        schema:
          type: integer
          default: 1
        description: 1-based index of the first result to return.
      - name: count
        in: query
        required: false
        schema:
          type: integer
        description: Maximum number of results to return in this page.
      responses:
        '200':
          description: Users listed successfully
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ListResponse"
        '401':
          description: Missing or invalid bearer token
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '403':
          description: Token lacks the required scope
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '400':
          description: Invalid filter expression
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
    post:
      summary: Create user
      operationId: createScimUser
      description: |
        Provisions a new BetterUp user from a SCIM payload. Idempotent on
        `externalId` within the caller's organization: a repeat POST with
        the same `externalId` returns 200 with the existing resource
        instead of 201.

        If the payload's `userName` collides with a non-SCIM user already in
        the organization, the request is rejected with 409 `uniqueness`.
        Setting `active: false` provisions the user dormant (no invitation
        email, no entitlement assignment).
      tags:
      - SCIM/Users
      security:
      - clientCredentials:
        - scim:user:write
      requestBody:
        description: SCIM 2.0 User resource to provision.
        required: true
        content:
          application/scim+json:
            schema:
              "$ref": "#/components/schemas/User"
      responses:
        '201':
          description: User created
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/User"
        '200':
          description: Idempotent retry — user already exists with this externalId
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/User"
        '400':
          description: Malformed payload (e.g. missing required `userName`)
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '401':
          description: Missing or invalid bearer token
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '403':
          description: Token lacks the required scope
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '409':
          description: "`userName` already exists in the organization without a SCIM
            linkage"
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
  "/scim/v2/Users/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: BetterUp user UUID (matches the `id` returned at create time).
      schema:
        type: string
        format: uuid
    get:
      summary: Show user
      operationId: showScimUser
      description: Returns the SCIM representation of a single user. Returns 404 if
        the user is in a different organization, lacks a SCIM linked identity, or
        does not exist.
      tags:
      - SCIM/Users
      security:
      - clientCredentials:
        - scim:user:read
      responses:
        '200':
          description: User found
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/User"
        '401':
          description: Missing or invalid bearer token
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '403':
          description: Token lacks the required scope
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '404':
          description: User not found, in a different organization, or not SCIM-linked
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
    put:
      summary: Replace user
      operationId: replaceScimUser
      description: |
        Whole-resource replacement (RFC 7644 §3.5.1). Mapped attributes
        absent from the payload are cleared. The `userName` field is
        required; PUT requests missing it are rejected with 409.
      tags:
      - SCIM/Users
      security:
      - clientCredentials:
        - scim:user:write
      requestBody:
        description: Complete SCIM 2.0 User resource replacing the existing one.
        required: true
        content:
          application/scim+json:
            schema:
              "$ref": "#/components/schemas/User"
      responses:
        '200':
          description: User replaced
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/User"
        '400':
          description: Malformed payload
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '401':
          description: Missing or invalid bearer token
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '403':
          description: Token lacks the required scope
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '404':
          description: User not found
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '409':
          description: Missing `userName`, or new `userName` collides with another
            user in the organization
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
    patch:
      summary: Update user
      operationId: updateScimUser
      description: |
        Partial update (RFC 7644 §3.5.2). Accepts add / replace / remove
        operations with dotted, indexed, or URN paths, plus the Azure AD
        pathless `replace` variant. Setting `active: false` soft-deactivates
        the user; setting it back to `true` reactivates the user.
      tags:
      - SCIM/Users
      security:
      - clientCredentials:
        - scim:user:write
      requestBody:
        description: SCIM 2.0 PatchOp message describing add/replace/remove operations
          to apply.
        required: true
        content:
          application/scim+json:
            schema:
              type: object
              description: SCIM 2.0 PatchOp message (RFC 7644 §3.5.2).
              properties:
                schemas:
                  type: array
                  items:
                    type: string
                  example:
                  - urn:ietf:params:scim:api:messages:2.0:PatchOp
                Operations:
                  type: array
                  items:
                    type: object
                    properties:
                      op:
                        type: string
                        enum:
                        - add
                        - replace
                        - remove
                      path:
                        type: string
                      value: {}
                    required:
                    - op
              required:
              - schemas
              - Operations
      responses:
        '200':
          description: User updated
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/User"
        '400':
          description: Malformed patch payload or invalid `active` value
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '401':
          description: Missing or invalid bearer token
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '403':
          description: Token lacks the required scope
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '404':
          description: User not found
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '409':
          description: New `userName` collides with another user in the organization
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
    delete:
      summary: Deactivate user
      operationId: deleteScimUser
      description: |
        Soft-deactivates the user. RFC 7644 §3.6 specifies hard delete, but
        BetterUp retains user records for audit and historical reporting;
        the user's `active` attribute flips to `false` and they lose access.
        Reactivate via PATCH with `active: true`.
      tags:
      - SCIM/Users
      security:
      - clientCredentials:
        - scim:user:write
      responses:
        '204':
          description: User deactivated
        '401':
          description: Missing or invalid bearer token
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '403':
          description: Token lacks the required scope
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '404':
          description: User not found
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
  "/scim/v2/Groups":
    get:
      summary: List groups
      operationId: listScimGroups
      description: Returns a paginated list of SCIM-provisioned groups in the caller's
        organization. Supports filtering and pagination per RFC 7644 §3.4.2.
      tags:
      - SCIM/Groups
      security:
      - clientCredentials:
        - scim:group:read
      parameters:
      - name: filter
        in: query
        required: false
        schema:
          type: string
        description: SCIM filter expression. Supports `id`, `externalId`, and `displayName`.
        example: displayName eq "Engineering"
      - name: startIndex
        in: query
        required: false
        schema:
          type: integer
          default: 1
        description: 1-based index of the first result to return.
      - name: count
        in: query
        required: false
        schema:
          type: integer
        description: Maximum number of results to return in this page.
      responses:
        '200':
          description: Groups listed successfully
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ListResponse"
        '401':
          description: Missing or invalid bearer token
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '403':
          description: Token lacks the required scope
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '400':
          description: Invalid filter expression
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
    post:
      summary: Create group
      operationId: createScimGroup
      description: |
        Provisions a new BetterUp group from a SCIM payload. Idempotent on
        `externalId` within the caller's organization: a repeat POST with the
        same `externalId` returns 200 with the existing resource instead of
        201. An `externalId` belonging to a group in a different organization
        does not collide — the request is treated as a fresh provisioning.

        Members may be supplied at create time. Every `members[].value` must
        be the `id` of a SCIM-provisioned User in the same organization;
        unresolved values are rejected with 400.
      tags:
      - SCIM/Groups
      security:
      - clientCredentials:
        - scim:group:write
      requestBody:
        description: SCIM 2.0 Group resource to provision.
        required: true
        content:
          application/scim+json:
            schema:
              "$ref": "#/components/schemas/Group"
      responses:
        '201':
          description: Group created
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/Group"
        '200':
          description: Idempotent retry — group already exists with this externalId
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/Group"
        '400':
          description: Malformed payload or unresolved member UUID
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '401':
          description: Missing or invalid bearer token
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '403':
          description: Token lacks the required scope
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
  "/scim/v2/Groups/{id}":
    parameters:
    - name: id
      in: path
      required: true
      description: BetterUp group UUID (matches the `id` returned at create time).
      schema:
        type: string
        format: uuid
    get:
      summary: Show group
      operationId: showScimGroup
      description: Returns the SCIM representation of a single group, including its
        current members. Returns 404 if the group is in a different organization or
        does not exist.
      tags:
      - SCIM/Groups
      security:
      - clientCredentials:
        - scim:group:read
      responses:
        '200':
          description: Group found
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/Group"
        '401':
          description: Missing or invalid bearer token
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '403':
          description: Token lacks the required scope
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '404':
          description: Group not found or in a different organization
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
    put:
      summary: Replace group
      operationId: replaceScimGroup
      description: |
        Whole-resource replacement (RFC 7644 §3.5.1). The members list is
        diffed against current active membership and add/remove operations
        are applied to bring the group into the desired state. Mapped
        attributes absent from the payload are cleared.
      tags:
      - SCIM/Groups
      security:
      - clientCredentials:
        - scim:group:write
      requestBody:
        description: Complete SCIM 2.0 Group resource replacing the existing one.
        required: true
        content:
          application/scim+json:
            schema:
              "$ref": "#/components/schemas/Group"
      responses:
        '200':
          description: Group replaced
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/Group"
        '400':
          description: Malformed payload or unresolved member UUID
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '401':
          description: Missing or invalid bearer token
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '403':
          description: Token lacks the required scope
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '404':
          description: Group not found
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '409':
          description: New `externalId` collides with another group in the organization
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
    patch:
      summary: Update group
      operationId: updateScimGroup
      description: |
        Partial update (RFC 7644 §3.5.2). Accepts add / replace / remove
        operations on `displayName`, `externalId`, and `members`. Supports
        both the standard form (`op: add`/`remove` on `path: members`) and
        the Azure AD whole-list replace variant (`op: replace, path:
        members, value: [...]`). Member removal by filter
        (`members[value eq "..."]`) is supported per Okta's SCIM patterns.
      tags:
      - SCIM/Groups
      security:
      - clientCredentials:
        - scim:group:write
      requestBody:
        description: SCIM 2.0 PatchOp message describing add/replace/remove operations
          to apply.
        required: true
        content:
          application/scim+json:
            schema:
              type: object
              description: SCIM 2.0 PatchOp message (RFC 7644 §3.5.2).
              properties:
                schemas:
                  type: array
                  items:
                    type: string
                  example:
                  - urn:ietf:params:scim:api:messages:2.0:PatchOp
                Operations:
                  type: array
                  items:
                    type: object
                    properties:
                      op:
                        type: string
                        enum:
                        - add
                        - replace
                        - remove
                      path:
                        type: string
                      value: {}
                    required:
                    - op
              required:
              - schemas
              - Operations
      responses:
        '200':
          description: Group updated
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/Group"
        '400':
          description: Malformed patch payload or unresolved member UUID
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '401':
          description: Missing or invalid bearer token
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '403':
          description: Token lacks the required scope
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '404':
          description: Group not found
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '409':
          description: New `externalId` collides with another group in the organization
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
    delete:
      summary: Delete group
      operationId: deleteScimGroup
      description: |
        Soft-discards the group and revokes any role bindings from its
        members (role bindings the user still receives from another SCIM
        source are preserved). The underlying user_group is also discarded;
        member users themselves are not deactivated.
      tags:
      - SCIM/Groups
      security:
      - clientCredentials:
        - scim:group:write
      responses:
        '204':
          description: Group deleted
        '401':
          description: Missing or invalid bearer token
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '403':
          description: Token lacks the required scope
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
        '404':
          description: Group not found
          content:
            application/scim+json:
              schema:
                "$ref": "#/components/schemas/ScimError"
