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

# SCIM

> Automate user provisioning and deprovisioning with SCIM 2.0

## Overview

SCIM (System for Cross-domain Identity Management) automates user provisioning, ensuring your Minoa users stay in sync with your identity provider. Minoa implements SCIM 2.0, providing a standardized API for user and group management.

<Info>Please contact us to enable SCIM for your organization and receive your authentication token.</Info>

## What SCIM Does

SCIM automatically handles:

* **User Creation**: New users in your identity provider are automatically added to Minoa
* **User Updates**: Changes to user attributes (name, email, role, status) sync automatically
* **User Deactivation**: Users removed from your identity provider are suspended in Minoa
* **Group Management**: Create and manage groups, with automatic user tag updates based on group membership

## Authentication

Minoa provides a **Bearer token** for SCIM API authentication. This token is unique to your organization and must be included in all SCIM requests.

### How to Authenticate

Include the token in the `Authorization` header of every request:

```
Authorization: Bearer YOUR_TOKEN_HERE
```

<Warning>
  Keep your SCIM token secure. Treat it like a password—never commit it to version control or share it publicly.
</Warning>

### Token Management

* Tokens are tenant-specific and scoped to your organization
* Tokens are stored securely using SHA-256 hashing
* Contact support to generate a new token or deactivate an existing one

## API Base URL

All SCIM endpoints are available at:

```
https://BASE_URL/scim/v2
```

<Note>The exact base URL will be provided when SCIM is enabled for your organization.</Note>

## Supported Operations

Minoa's SCIM implementation supports the following HTTP methods and operations:

### Users

| Method   | Endpoint     | Operation    | Description                                |
| -------- | ------------ | ------------ | ------------------------------------------ |
| `GET`    | `/Users`     | List users   | Retrieve all users with optional filtering |
| `GET`    | `/Users/:id` | Get user     | Retrieve a specific user by ID             |
| `POST`   | `/Users`     | Create user  | Create a new user                          |
| `PUT`    | `/Users/:id` | Replace user | Full resource replacement                  |
| `PATCH`  | `/Users/:id` | Update user  | Partial update using SCIM PATCH operations |
| `DELETE` | `/Users/:id` | Suspend user | Suspend user (sets status to suspended)    |

### Groups

| Method   | Endpoint      | Operation     | Description                                 |
| -------- | ------------- | ------------- | ------------------------------------------- |
| `GET`    | `/Groups`     | List groups   | Retrieve all groups with optional filtering |
| `GET`    | `/Groups/:id` | Get group     | Retrieve a specific group by ID             |
| `POST`   | `/Groups`     | Create group  | Create a new group                          |
| `PUT`    | `/Groups/:id` | Replace group | Full resource replacement                   |
| `PATCH`  | `/Groups/:id` | Update group  | Partial update using SCIM PATCH operations  |
| `DELETE` | `/Groups/:id` | Delete group  | Delete a group and clear member tags        |

### Discovery Endpoints

| Method | Endpoint                 | Description                       |
| ------ | ------------------------ | --------------------------------- |
| `GET`  | `/ServiceProviderConfig` | Get service provider capabilities |
| `GET`  | `/ResourceTypes`         | List supported resource types     |
| `GET`  | `/Schemas`               | List supported schemas            |

## Service Provider Configuration

Query `/ServiceProviderConfig` to discover Minoa's SCIM capabilities:

**Supported Features:**

* ✅ **Filtering**: Filter users by `userName` (email address)
* ✅ **PATCH**: Partial updates using SCIM PATCH operations
* ✅ **OAuth Bearer Token**: Authentication method

**Not Supported:**

* ❌ Bulk operations
* ❌ Password changes
* ❌ ETags
* ❌ Sorting

**Filtering:**

* Maximum results: 100 per request
* Supported filter: `userName eq "email@example.com"`

## Resource Types

Minoa supports two resource types:

### User Resource

* **Schema**: `urn:ietf:params:scim:schemas:core:2.0:User`
* **Extension**: `urn:minoa:params:scim:schemas:extension:minoa:1.0:User`

**Standard Attributes:**

* `id` (read-only): Unique user identifier
* `userName` (required): Email address (must be valid email)
* `displayName` (recommended): User's display name
* `name`: Complex object with `givenName`, `familyName`, `formatted`
* `emails`: Array of email objects
* `photos`: Array of photo URLs
* `active`: Boolean indicating user status
* `externalId`: External identifier (e.g., Okta user ID)

**Minoa Extension Attributes:**

* `role` (optional): User role (`admin` or `user`)

### Group Resource

* **Schema**: `urn:ietf:params:scim:schemas:core:2.0:Group`

**Attributes:**

* `id` (read-only): Unique group identifier
* `displayName` (required): Group name
* `externalId`: External identifier
* `members`: Array of member objects with `value`, `$ref`, and `display`

<Note>When users are added to groups, their tags are automatically updated to match the group's display name.</Note>

## User Operations

### Create User

**Request:**

```json theme={null}
POST /Users
{
  "userName": "user@example.com",
  "name": {
    "givenName": "John",
    "familyName": "Doe",
    "formatted": "John Doe"
  },
  "active": true,
  "urn:minoa:params:scim:schemas:extension:minoa:1.0:User": {
    "role": "user"
  }
}
```

**Response:** `201 Created` with full user resource

<Info>
  If a user with the same email already exists but isn't linked to SCIM, Minoa will automatically link them using the
  provided `externalId`.
</Info>

### Update User (PATCH)

**Supported Operations:**

* `replace`: Update existing attributes
* `add`: Add new attributes or array elements
* `remove`: Remove attributes or array elements

**Example:**

```json theme={null}
PATCH /Users/:id
{
  "Operations": [
    {
      "op": "replace",
      "path": "active",
      "value": false
    },
    {
      "op": "replace",
      "path": "urn:minoa:params:scim:schemas:extension:minoa:1.0:User:role",
      "value": "admin"
    }
  ]
}
```

### Suspend User (DELETE)

The `DELETE` operation suspends a user rather than permanently deleting them:

* Sets user status to `suspended`
* Disables Firebase Auth account
* Revokes all refresh tokens
* User data is preserved

**Response:** `204 No Content`

## Group Operations

### Create Group

**Request:**

```json theme={null}
POST /Groups
{
  "id": "group-id-from-identity-provider",
  "displayName": "Sales Team",
  "members": [
    {
      "value": "user-id-1",
      "display": "User One"
    }
  ]
}
```

**Response:** `201 Created` with full group resource

<Note>When a group is created, all members automatically receive a tag matching the group's `displayName`.</Note>

### Update Group (PATCH)

**Supported Operations:**

* `add` on `members`: Add members to group
* `remove` on `members`: Remove members from group
* `replace` on `displayName`: Update group name

**Example:**

```json theme={null}
PATCH /Groups/:id
{
  "Operations": [
    {
      "op": "add",
      "path": "members",
      "value": {
        "value": "new-user-id",
        "display": "New User"
      }
    }
  ]
}
```

## Filtering

### User Filtering

Filter users by email address:

```
GET /Users?filter=userName eq "user@example.com"
```

### Group Filtering

Filter groups by display name:

```
GET /Groups?filter=displayName eq "Sales Team"
```

## Pagination

List endpoints support pagination:

* `startIndex`: Starting index (default: 1)
* `count`: Number of results (default: 100, max: 100)

**Example:**

```
GET /Users?startIndex=1&count=50
```

## Rate Limiting

SCIM API requests are rate-limited to:

* **100 requests per 15 minutes** per organization

<Warning>
  Exceeding the rate limit will result in `429 Too Many Requests` responses. Implement exponential backoff in your
  identity provider configuration.
</Warning>

## Error Handling

Minoa returns standard SCIM error responses:

```json theme={null}
{
    "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
    "status": "404",
    "detail": "User not found",
    "scimType": "invalidValue"
}
```

**Common Status Codes:**

* `200 OK`: Successful GET, PUT, PATCH
* `201 Created`: Successful POST
* `204 No Content`: Successful DELETE
* `400 Bad Request`: Invalid request format or validation error
* `401 Unauthorized`: Missing or invalid Bearer token
* `404 Not Found`: Resource not found
* `409 Conflict`: Resource already exists (e.g., duplicate email)
* `413 Payload Too Large`: Request body exceeds 256KB limit
* `429 Too Many Requests`: Rate limit exceeded
* `500 Internal Server Error`: Server error

## Supported Identity Providers

Minoa's SCIM 2.0 implementation is compatible with all major identity providers:

* **Okta** (fully tested and supported)
* **Azure AD** (Microsoft Entra ID)
* **OneLogin**
* **Google Workspace**
* Any SCIM 2.0 compliant identity provider

## Configuration Steps

1. **Contact Support**: Reach out to enable SCIM for your organization
2. **Receive Token**: You'll receive a unique Bearer token for your organization
3. **Get Base URL**: You'll receive the SCIM API base URL
4. **Configure Identity Provider**:
   * Enter the base URL in your identity provider's SCIM settings
   * Configure the Bearer token for authentication
   * Map attributes according to your needs
5. **Test Connection**: Most identity providers provide a test connection feature

## Attribute Mapping

### Standard SCIM Attributes

Minoa maps standard SCIM attributes as follows:

| SCIM Attribute    | Minoa Field  | Notes                                          |
| ----------------- | ------------ | ---------------------------------------------- |
| `userName`        | `email`      | Must be a valid email address                  |
| `displayName`     | `name`       | User's full name - recommended                 |
| `name.formatted`  | `name`       | User's full name - fallback                    |
| `name.givenName`  | -            | User's first name - fallback to construct name |
| `name.familyName` | -            | User's last name - fallback to construct name  |
| `emails[0].value` | `email`      | Primary email                                  |
| `photos[0].value` | `photoUrl`   | Profile photo URL                              |
| `active`          | `status`     | `true` → `active`, `false` → `suspended`       |
| `externalId`      | `oktaUserId` | External identifier from identity provider     |

### Minoa Extension Attributes

| Extension Attribute | Minoa Field | Required | Notes                     |
| ------------------- | ----------- | -------- | ------------------------- |
| `role`              | `role`      | Yes      | Must be `admin` or `user` |

## Troubleshooting

<Warning>
  Changes can take up to 5 minutes to sync between your identity provider and Minoa, depending on your identity
  provider's sync frequency.
</Warning>

<AccordionGroup>
  <Accordion title="Users not provisioning">
    **Check these items:**

    1. Verify the SCIM base URL is correct in your identity provider settings
    2. Confirm the Bearer token is correctly configured
    3. Check that the user's email address is valid and unique
    4. Ensure the `role` attribute is included in the Minoa extension
    5. Review identity provider logs for error messages
  </Accordion>

  <Accordion title="Deactivated users still have access">
    **Troubleshooting steps:**

    1. Verify that deprovisioning is enabled in your identity provider's SCIM settings
    2. Check that the DELETE operation is being sent (some providers require explicit configuration)
    3. Confirm the user was successfully suspended by checking the user's status in Minoa
    4. Note: Suspended users retain their data but cannot log in
  </Accordion>

  <Accordion title="Rate limit errors">
    **If you're seeing 429 errors:**

    1. The limit is 100 requests per 15 minutes per organization
    2. Implement exponential backoff in your identity provider
    3. Reduce sync frequency if possible
    4. Contact support if you need a higher rate limit
  </Accordion>

  <Accordion title="Group members not updating tags">
    **Verify:**

    1. Group operations are being sent correctly
    2. User IDs in group members match actual user IDs in Minoa
    3. Check that the group's `displayName` is being set correctly
    4. Tags are automatically updated when users are added/removed from groups
  </Accordion>

  <Accordion title="Authentication errors">
    **If you're seeing 401 errors:**

    1. Verify the Bearer token is included in the `Authorization` header
    2. Check that the token format is correct: `Bearer YOUR_TOKEN`
    3. Confirm the token hasn't been deactivated
    4. Contact support to verify your token is active
  </Accordion>
</AccordionGroup>

## Best Practices

1. **Test First**: Use your identity provider's test connection feature before enabling full sync
2. **Monitor Logs**: Check both identity provider and Minoa logs for sync issues
3. **Handle Errors**: Implement retry logic with exponential backoff for transient errors
4. **Validate Data**: Ensure user emails are valid and roles are correctly set
5. **Sync Frequency**: Balance sync frequency with rate limits—most providers default to every 5-10 minutes

## Additional Resources

* [SCIM 2.0 Specification](https://datatracker.ietf.org/doc/html/rfc7644)
* [SCIM Protocol Specification](https://datatracker.ietf.org/doc/html/rfc7643)

<Info>Need help configuring SCIM? Contact our support team at [support@minoa.io](mailto:support@minoa.io) for assistance.</Info>
