Marvin SCIM 2.0 — Users API

SCIM 2.0 (RFC 7643 / 7644) · Last updated July 2026

Marvin implements SCIM 2.0 to let identity providers provision, deprovision, and update users automatically.

All endpoints are under:

text
/sso/scim/v2/{scim_key}/

Your scim_key and secret will be shared with you separately


Authentication#

Every request must include an Authorization header with a Bearer token configured in your SCIM integration settings.

text
Authorization: Bearer <your-scim-token>
Content-Type:  application/scim+json   ← required for POST / PATCH
Accept:        application/scim+json

⚠️ Sending Content-Type: application/json instead of application/scim+json will return 415 Unsupported Media Type. Most identity providers set the correct type automatically when SCIM is configured properly.


Users#

GET /sso/scim/v2/{scim_key}/Users#

Returns all active, non-suspended users on the team. Supports optional filter by userName to look up a single user.

Query parameters

ParameterTypeDescription
filterstringSCIM filter expression, e.g. userName eq "user@example.com"

Response 200

json
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
  "totalResults": 2,
  "Resources": [
    {
      "id": "alice@example.com",
      "userName": "alice@example.com",
      "name": { "givenName": "Alice", "familyName": "Smith" }
    },
    {
      "id": "bob@example.com",
      "userName": "bob@example.com",
      "name": { "givenName": "Bob", "familyName": "Jones" }
    }
  ]
}

GET /sso/scim/v2/{scim_key}/Users/{email}#

Returns the full SCIM representation of a single user, including their current Marvin role.

Response 200

json
{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "id": "alice@example.com",
  "externalId": "alice@example.com",
  "userName": "alice@example.com",
  "name": {
    "givenName": "Alice",
    "familyName": "Smith"
  },
  "roles": [
    {
      "value": "Full",
      "display": "Full",
      "primary": true
    }
  ],
  "meta": {
    "resourceType": "User",
    "created": "2024-01-15T09:00:00+00:00",
    "lastModified": "2024-01-15T09:00:00+00:00",
    "location": "https://app.heymarvin.com/sso/scim/v2/Users/alice@example.com"
  }
}

POST /sso/scim/v2/{scim_key}/Users#

Creates a new Marvin user. If a user with the same userName already exists, their profile and role are updated instead — this is an upsert.

The optional roles field sets the user's Marvin role. Omitting it leaves the team's default role in place (typically Viewer).

Request body

json
{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "userName": "alice@example.com",
  "name": {
    "givenName": "Alice",
    "familyName": "Smith"
  },
  "active": true,
  "roles": [
    {
      "value": "Full",
      "primary": true
    }
  ]
}
FieldRequiredDescription
userName✅ YesUser's email address
name.givenName✅ YesFirst name
name.familyName✅ YesLast name
activeNotrue to activate, false to suspend
rolesNoArray with one role object. If omitted, team default role is used.
roles[].valueNoRole name — see Role Reference for valid values
roles[].primaryNoShould be true

Response 201 — full User object (same shape as GET /Users/{email})


PATCH /sso/scim/v2/{scim_key}/Users/{email}#

Updates one or more fields on an existing user. Only fields present in the body are modified — omitted fields are left unchanged.

This is the primary endpoint for role changes triggered by your identity provider.

Role update: send roles[0].value with the new role name. Marvin updates the user's role and marks it as SSO-managed so it is not overwritten by future team default changes.

Request body — role change

json
{
  "roles": [
    {
      "value": "Full",
      "primary": true
    }
  ]
}

Request body — name and/or active status

json
{
  "name": {
    "familyName": "Johnson"
  },
  "active": false
}

Setting active: false suspends the user.

Response 200 — full User object with updated values


DELETE /sso/scim/v2/{scim_key}/Users/{email}#

Suspends the user — they lose access to Marvin but their data is preserved. This is a soft delete; the user can be reactivated via a subsequent POST or PATCH with "active": true.

Response 200 — User object with suspended state reflected


Roles#

GET /sso/scim/v2/{scim_key}/Roles#

Returns all valid role names. Use this endpoint when configuring attribute mappings in your identity provider — the value strings here are exactly what must be sent in roles[0].value on POST / PATCH requests.

Response 200

json
{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:ListResponse"],
  "totalResults": 4,
  "Resources": [
    { "value": "Full",         "id": 0 },
    { "value": "Viewer",       "id": 2 },
    { "value": "Admin",        "id": 3 },
    { "value": "Collaborator", "id": 4 }
  ]
}

Role Reference#

SCIM valueMarvin rolePermissions
FullContributorCan view, tag, annotate, and create insights
ViewerViewerRead-only access to shared projects
AdminTeam AdminFull access including team settings and billing
CollaboratorCollaboratorCan comment and react; limited editing

The value field is case-insensitive when received — full, Full, and FULL are all accepted. Use the canonical casing above in attribute mappings.

⚠️ Sending an unrecognised value (e.g. SuperAdmin) is silently ignored — the request succeeds and the user's current role is left unchanged. No error is returned.


Error Responses#

StatusCause
400Email domain not allowed for this team
403Email address not permitted (blocked domain)
404Invalid or unknown scim_key, or user not found
415Content-Type is not application/scim+json
401 / 403Bearer token missing or incorrect