API Reference
SCIM

SCIM 2.0

PurpletGo exposes a SCIM 2.0 (System for Cross-domain Identity Management) endpoint so your identity provider can sync employee records and trigger deprovisioning the moment a user is removed there.

SCIM is the recommended integration method for organisations using Okta, Azure AD, JumpCloud, OneLogin, or Ping Identity as their source of truth for user lifecycle management.


Base URL

https://api.purpletgo.com/scim/v2

All SCIM endpoints are relative to this base URL. Note this sits outside the regular /api and /api/v1 prefixes - it's a separate router mounted specifically for SCIM traffic.


Authentication

SCIM uses Bearer token authentication. Generate a dedicated SCIM token from the PurpletGo dashboard - it's separate from regular API keys and only works against these SCIM endpoints.

Generate a SCIM token

  1. Go to Settings → Integrations → SCIM
  2. Click Generate SCIM Token
  3. Copy the token - it is shown only once

Configure your IdP

Paste the base URL and token into your IdP's SCIM provisioning settings. Each IdP uses slightly different field names:

IdPBase URL fieldToken field
OktaSCIM Connector Base URLAPI Token
Azure ADTenant URLSecret Token
JumpCloudBase URLToken
OneLoginSCIM Base URLOAuth Bearer Token
Ping IdentitySCIM Base URLBearer Token

Enable provisioning actions

Enable at minimum: Create Users, Deactivate Users. Optionally enable Update User Attributes.

⚠️

A SCIM token grants provisioning access to every employee record in the org. Rotate it immediately if it's exposed - it can be revoked from Settings → Integrations → SCIM without touching other API keys.


Supported endpoints

MethodPathDescription
GET/ServiceProviderConfigCapability advertisement - the only endpoint that doesn't require a Bearer token
GET/UsersList employees for the org
GET/Users/:idGet a single employee
POST/UsersProvision a user (creates, or updates if the email already exists)
PATCH/Users/:idUpdate attributes; active: false triggers offboarding
DELETE/Users/:idDeprovision a user

There's no /Schemas endpoint and no SCIM Groups support - department and role are synced as plain attributes on the User resource, not as group memberships.


User schema and attribute mapping

PurpletGo maps a handful of urn:ietf:params:scim:schemas:core:2.0:User attributes onto its org_employees table:

SCIM AttributePurpletGo fieldNotes
userNameemailUsed as the unique identifier if no primary email is supplied
emails[primary]emailPreferred over userName when both are present
displayNamefull_nameFalls back to name.formatted, then givenName + familyName, then the email
name.givenName / name.familyName-Only used to help derive full_name on create; PurpletGo doesn't store given/family name separately
titlejob_title
departmentdepartmentSent as a plain top-level attribute, not a namespaced extension
active-false triggers auto-offboarding; PurpletGo doesn't otherwise persist an active flag on the employee record
externalIdemployee_idYour IdP's internal ID, kept for correlation

There's no support for phoneNumbers or a manager-ID attribute - anything else in the request body outside the fields above is accepted but ignored.


Provisioning flow

Creating a user (POST /Users)

Request

POST /scim/v2/Users
Authorization: Bearer scim_live_••••••••••••••••
Content-Type: application/scim+json
{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "userName": "jordan.lee@acme.com",
  "name": { "givenName": "Jordan", "familyName": "Lee" },
  "displayName": "Jordan Lee",
  "title": "Senior Engineer",
  "department": "Engineering",
  "active": true,
  "emails": [{ "value": "jordan.lee@acme.com", "type": "work", "primary": true }],
  "externalId": "okta_00u1ab2cd3ef4gh5ij6k"
}

Response 201 Created

{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "id": "8f4c6d1a-9e2b-4a3f-8c1d-2b7e9f0a1c3d",
  "externalId": "okta_00u1ab2cd3ef4gh5ij6k",
  "userName": "jordan.lee@acme.com",
  "name": { "formatted": "Jordan Lee", "givenName": "Jordan", "familyName": "Lee" },
  "displayName": "Jordan Lee",
  "emails": [{ "value": "jordan.lee@acme.com", "primary": true, "type": "work" }],
  "title": "Senior Engineer",
  "department": "Engineering",
  "active": true,
  "meta": {
    "resourceType": "User",
    "location": "https://api.purpletgo.com/scim/v2/Users/8f4c6d1a-9e2b-4a3f-8c1d-2b7e9f0a1c3d"
  }
}

POST /Users is an upsert keyed on email, not a strict create. If a user with that email already exists in the org, PurpletGo updates their name, title, and department rather than rejecting the request - so you won't see a duplicate-user error from a re-sync or a retried request.


Deactivating a user (PATCH /Users/:id)

Setting active to false is the deprovisioning signal PurpletGo looks for. It responds by creating an offboarding for the employee, guarded so a retried or duplicate PATCH won't create a second one.

Request

PATCH /scim/v2/Users/8f4c6d1a-9e2b-4a3f-8c1d-2b7e9f0a1c3d
Authorization: Bearer scim_live_••••••••••••••••
Content-Type: application/scim+json
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [
    { "op": "replace", "path": "active", "value": false }
  ]
}

Response 200 OK - the updated user resource, with active: false.

PurpletGo also recognizes the nested form some IdPs send ({ "op": "replace", "value": { "active": false, "displayName": "...", "title": "..." } }) and will apply displayName/title updates alongside the deactivation. Other PATCH paths and add/remove operations are accepted but have no effect.

The offboarding created here starts life the same way a manually-created one does - if your org has approval chains configured, it enters pending_approval before access revocation begins.


Deleting a user (DELETE /Users/:id)

⚠️

DELETE does not remove the employee record. It runs the exact same "create an offboarding if one isn't already in progress" logic as PATCH active=false - the employee record stays in PurpletGo so its offboarding history and audit trail remain intact. If you need to fully erase a record for data-privacy reasons, that has to be done separately from the PurpletGo dashboard; there's no API for it.

Response 204 No Content


Listing users (GET /Users)

Supports SCIM-style pagination via startIndex and count (max 200, default 100).

GET /scim/v2/Users?startIndex=1&count=25
Authorization: Bearer scim_live_••••••••••••••••

Response 200 OK

{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
  "totalResults": 1,
  "startIndex": 1,
  "itemsPerPage": 25,
  "Resources": [
    {
      "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
      "id": "8f4c6d1a-9e2b-4a3f-8c1d-2b7e9f0a1c3d",
      "userName": "jordan.lee@acme.com",
      "displayName": "Jordan Lee",
      "active": true
    }
  ]
}
⚠️

GET /ServiceProviderConfig advertises filter: { supported: true } for IdP discovery purposes, but the /Users endpoint doesn't currently evaluate filter query expressions - if your IdP sends one, it's silently ignored and every employee page is returned. Rely on startIndex/count for pagination.


Error responses

{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:Error"],
  "status": "400",
  "detail": "userName / email must be a valid email address"
}
HTTP StatusMeaning
400Malformed request - missing/invalid email, or Operations isn't an array
401Missing or invalid SCIM bearer token
404User ID not found in this organisation
429Rate limit exceeded (120 requests/minute per SCIM endpoint)
500Internal error while processing the request

There's no 409 case - see the upsert note above.


Related