Stitchflow
Thought Industries logo

Thought Industries User Management API Guide

API workflow

How to automate user lifecycle operations through APIs with caveats that matter in production.

UpdatedMar 16, 2026

Summary and recommendation

Thought Industries exposes both a REST API (versioned at v1 under /incoming/api/v1/) and a GraphQL API at /incoming/api/graphql, with authentication via API key passed as the apiKey query parameter or X-API-Key header.

The API key is tenant-scoped and tied to the customer subdomain - there is no shared multi-tenant hostname.

Core user lifecycle operations (create, read, update, delete/deactivate) are available over REST;

some operations may only be available in one interface, so confirm coverage against the GraphQL schema via introspection before committing to an implementation path.

This API surface integrates cleanly into an identity graph that maps users across HR systems, SSO providers, and downstream SaaS tools, enabling authoritative lifecycle events to propagate to Thought Industries without manual intervention.

API quick reference

Has user APIYes
Auth methodAPI Key (passed as a query parameter or HTTP header)
Base URLOfficial docs
SCIM availableNo
SCIM plan requiredEnterprise

Authentication

Auth method: API Key (passed as a query parameter or HTTP header)

Setup steps

  1. Log in to your Thought Industries admin portal.
  2. Navigate to Settings > Integrations > API.
  3. Generate or copy your API key.
  4. Include the key in requests as the query parameter apiKey or in the X-API-Key header.

User object / data model

Field Type Description On create On update Notes
id string Unique internal user identifier system-generated immutable
email string User's email address (login identifier) required supported Must be unique per tenant
firstName string User's first name optional supported
lastName string User's last name optional supported
password string User's password (write-only) optional supported Never returned in responses
role string User role within the platform (e.g., learner, admin) optional supported
groups array Group memberships for the user optional supported Array of group identifiers
customFields object Key-value pairs for custom user profile attributes optional supported Field keys defined in admin portal
createdAt datetime Timestamp of user creation system-generated immutable ISO 8601 format
updatedAt datetime Timestamp of last user update system-generated system-generated ISO 8601 format
active boolean Whether the user account is active optional supported Deactivating prevents login
ssoId string External SSO identifier for the user optional supported Used for SSO-linked accounts
avatarUrl string URL to user's profile avatar optional supported
language string Preferred language/locale for the user optional supported BCP 47 language tag

Core endpoints

List Users

  • Method: GET
  • URL: https://{subdomain}.thoughtindustries.com/incoming/api/v1/users
  • Watch out for: Pagination is required for large user sets; default page size is 25.

Request example

GET /incoming/api/v1/users?apiKey=YOUR_KEY&page=1&per_page=25

Response example

{
  "users": [
    {"id": "abc123", "email": "user@example.com", "firstName": "Jane", "lastName": "Doe", "active": true}
  ],
  "total": 150
}

Get User by ID

  • Method: GET
  • URL: https://{subdomain}.thoughtindustries.com/incoming/api/v1/users/{id}
  • Watch out for: Returns 404 if user ID does not exist in the tenant.

Request example

GET /incoming/api/v1/users/abc123?apiKey=YOUR_KEY

Response example

{
  "user": {
    "id": "abc123",
    "email": "user@example.com",
    "firstName": "Jane",
    "active": true
  }
}

Create User

  • Method: POST
  • URL: https://{subdomain}.thoughtindustries.com/incoming/api/v1/users
  • Watch out for: Email must be unique; duplicate email returns an error. Password is optional; if omitted, user may need to set one via email invite.

Request example

POST /incoming/api/v1/users?apiKey=YOUR_KEY
Content-Type: application/json
{
  "email": "newuser@example.com",
  "firstName": "John",
  "lastName": "Smith"
}

Response example

{
  "user": {
    "id": "xyz789",
    "email": "newuser@example.com",
    "firstName": "John",
    "active": true
  }
}

Update User

  • Method: PUT
  • URL: https://{subdomain}.thoughtindustries.com/incoming/api/v1/users/{id}
  • Watch out for: Full object replacement semantics may apply; confirm with official docs whether PATCH partial update is supported.

Request example

PUT /incoming/api/v1/users/xyz789?apiKey=YOUR_KEY
Content-Type: application/json
{
  "firstName": "Jonathan",
  "active": false
}

Response example

{
  "user": {
    "id": "xyz789",
    "email": "newuser@example.com",
    "firstName": "Jonathan",
    "active": false
  }
}

Delete User

  • Method: DELETE
  • URL: https://{subdomain}.thoughtindustries.com/incoming/api/v1/users/{id}
  • Watch out for: Deletion is typically permanent; deactivating (active: false) is preferred for audit trail retention.

Request example

DELETE /incoming/api/v1/users/xyz789?apiKey=YOUR_KEY

Response example

{
  "success": true
}

Add User to Group

  • Method: POST
  • URL: https://{subdomain}.thoughtindustries.com/incoming/api/v1/groups/{groupId}/users
  • Watch out for: Group ID must exist; adding a user to a non-existent group returns an error.

Request example

POST /incoming/api/v1/groups/grp001/users?apiKey=YOUR_KEY
Content-Type: application/json
{
  "userId": "xyz789"
}

Response example

{
  "success": true
}

Get User Enrollments

  • Method: GET
  • URL: https://{subdomain}.thoughtindustries.com/incoming/api/v1/users/{id}/enrollments
  • Watch out for: Enrollment data may be paginated for users with many courses.

Request example

GET /incoming/api/v1/users/xyz789/enrollments?apiKey=YOUR_KEY

Response example

{
  "enrollments": [
    {"courseId": "crs001", "status": "in_progress", "completedAt": null}
  ]
}

GraphQL User Query

  • Method: POST
  • URL: https://{subdomain}.thoughtindustries.com/incoming/api/graphql
  • Watch out for: GraphQL API is available alongside REST; schema introspection should be used to confirm current field availability.

Request example

POST /incoming/api/graphql?apiKey=YOUR_KEY
Content-Type: application/json
{
  "query": "{ users(page: 1) { id email firstName active } }"
}

Response example

{
  "data": {
    "users": [
      {"id": "abc123", "email": "user@example.com", "firstName": "Jane", "active": true}
    ]
  }
}

Rate limits, pagination, and events

  • Rate limits: Rate limit specifics are not publicly documented in official developer docs.

  • Rate-limit headers: No

  • Retry-After header: No

  • Rate-limit notes: No explicit rate limit tiers, headers, or Retry-After behavior documented publicly. Contact Thought Industries support for current limits.

  • Pagination method: offset

  • Default page size: 25

  • Max page size: 100

  • Pagination pointer: page / per_page

  • Webhooks available: Yes

  • Webhook notes: Thought Industries supports outbound webhooks that fire on platform events. Webhooks are configured in the admin portal under Integrations.

  • Alternative event strategy: Polling the REST API or GraphQL API for user and enrollment changes if webhooks are not available on a given plan.

  • Webhook events: user.created, user.updated, user.enrolled, course.completed, assessment.completed

SCIM API status

  • SCIM available: No
  • SCIM version: Not documented
  • Plan required: Enterprise
  • Endpoint: Not documented

Limitations:

  • Native SCIM provisioning is not documented in official Thought Industries developer docs.
  • Enterprise plan may be required for advanced provisioning integrations.
  • SSO-based provisioning (JIT) may be available as an alternative via SAML.

Common scenarios

Three scenarios are well-supported by the documented endpoints.

First, provisioning a new learner: POST to /v1/users, capture the returned id, then POST to /v1/courses/{courseId}/enrollments - the course must be published and the user active before enrollment succeeds.

Second, deactivating a departed employee: resolve the user id via GET /v1/users?email={email}, then PUT /v1/users/{id} with {"active": false};

this preserves completion history, whereas DELETE is permanent and removes historical records.

Third, syncing profile updates from an HR system: resolve the user by email, PUT updated fields including customFields, and fall back to POST if the user does not yet exist

note that custom field keys must be pre-configured in the admin portal or writes may be silently ignored.

Pagination defaults to 25 records per page with a maximum of 100;

use page and per_page params for full roster traversal.

Rate limit behavior is not publicly documented - implement exponential backoff as a baseline.

Provision a new learner and enroll in a course

  1. POST /incoming/api/v1/users with email, firstName, lastName to create the user.
  2. Capture the returned user id from the response.
  3. POST /incoming/api/v1/courses/{courseId}/enrollments with the userId to enroll the learner.
  4. Optionally POST /incoming/api/v1/groups/{groupId}/users to assign the user to the appropriate group.

Watch out for: Course enrollment endpoint requires the course to be published and the user to be active; verify both before calling.

Deactivate a departed employee

  1. GET /incoming/api/v1/users?email={email} to look up the user by email and retrieve their id.
  2. PUT /incoming/api/v1/users/{id} with body {"active": false} to deactivate the account.
  3. Confirm the response returns active: false.

Watch out for: Deactivation prevents login but preserves completion records. Permanent deletion removes historical data.

Sync user profile updates from an external HR system

  1. On HR system change event, call GET /incoming/api/v1/users?email={email} to resolve the Thought Industries user id.
  2. If user exists, call PUT /incoming/api/v1/users/{id} with updated fields (firstName, lastName, customFields).
  3. If user does not exist (404), call POST /incoming/api/v1/users to create a new record.
  4. Log the API response and handle errors with exponential backoff retry.

Watch out for: Custom fields must match keys pre-configured in the admin portal; unknown keys may be silently ignored or return an error.

Why building this yourself is a trap

The absence of native SCIM is the primary integration caveat: automated provisioning requires direct API orchestration rather than a standards-based SCIM 2.0 handshake, which increases implementation surface and ongoing maintenance burden.

The PUT endpoint for user updates may apply full object replacement semantics - partial PATCH support is unconfirmed, so callers should retrieve the current user object before writing to avoid inadvertently nulling fields. Webhook payload schemas are not fully documented publicly; validate in a sandbox before wiring into production pipelines.

No rate limit headers or Retry-After signals are documented, so there is no programmatic signal to back off - conservative retry logic is mandatory. Finally, the GraphQL and REST APIs coexist but are not feature-equivalent; audit both surfaces before assuming an operation is unavailable.

Automate Thought Industries workflows without one-off scripts

Stitchflow builds and maintains end-to-end IT automation across your SaaS stack, including apps without APIs. Built for exactly how your company works, with human approvals where they matter.

Every app coverage, including apps without APIs
60+ app integrations plus browser automation for apps without APIs
IT graph reconciliation across apps and your IdP
Less than a week to launch, maintained as APIs and admin consoles change
SOC 2 Type II. ~2 hours of your team's time

UpdatedMar 16, 2026

* Details sourced from official product documentation and admin references.

Keep exploring

Related apps

Abnormal Security logo

Abnormal Security

API Only
AutomationAPI only
Last updatedMar 2026

Abnormal Security is an enterprise email security platform focused on detecting and investigating threats such as phishing, account takeover (ATO), and vendor email compromise. It does not support SCIM provisioning, which means every app in your stack

ActiveCampaign logo

ActiveCampaign

API Only
AutomationAPI only
Last updatedFeb 2026

ActiveCampaign uses a group-based permission model: every user belongs to exactly one group, and all feature-area access (Contacts, Campaigns, Automations, Deals, Reports, Templates) is configured at the group level, not per individual. The default Adm

ADP logo

ADP

API Only
AutomationAPI only
Last updatedFeb 2026

ADP Workforce Now is a mid-market to enterprise HCM platform that serves as the HR source of record for employee data — payroll, benefits, time, and talent. User access is governed by a hybrid permission model: predefined security roles (Security Maste