Quickstart

Use cases by outcome

Real, end-to-end sequences you can copy and adapt. Start with a flagship; the same calls power use cases across the whole employee lifecycle.

One runbook your agent can run end to end.

Start

Before any call, exchange the client ID and secret Thomas gave you for a short-lived access token. Then send it as your bearer token. Thomas issues credentials to each Partner integration. There is no self-serve sign-up.

  1. 1
    Get your client credentials from Thomas

    Thomas gives you a client ID and secret for your integration. Keep them on your backend, never in the browser.

    Request
    export THOMAS_CLIENT_ID="…"
    export THOMAS_CLIENT_SECRET="…"
  2. 2
    Request a token

    The token lasts one hour. There is no refresh token, so request a new one when it expires.

    Request
    curl -X POST "$THOMAS_TOKEN_URL" \
      -u "$THOMAS_CLIENT_ID:$THOMAS_CLIENT_SECRET" \
      -d grant_type=client_credentials
  3. 3
    Send it on every call

    Pass the token as a bearer token on each request. The base URL is shown on every recipe step, and it already includes the version. It is your one source of truth. Set it once as $THOMAS_API_URL. Most calls also need one Thomas-Tenant-Id header naming the customer they act in. Your Grant carries only the scopes it needs. The token itself carries identity only, and scope authority is checked against the live Grant on every request. No credential grants bulk access to raw scoring or the item bank.

    Request
    curl "$THOMAS_API_URL/company-individuals" \
      -H "Authorization: Bearer $THOMAS_TOKEN" \
      -H "Thomas-Tenant-Id: $THOMAS_TENANT_ID"
Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

Create a customer, sync its people, and, while you are still building, withdraw a test customer and recover it. These three customer-lifecycle calls act above a tenant, so they take no Thomas-Tenant-Id header. Every call you make inside the customer does.

  1. 1
    Create the customer

    `POST /tenants` creates one customer and returns its `tenantId`. This call acts above a tenant, so it sends no Thomas-Tenant-Id header. There are three outcomes. `created` returns the new customer's `tenantId`. The other two carry no id, deliberately: they tell you nothing about a customer that is not yours. `request_access` means this customer is already on Thomas, so ask your Thomas account manager to arrange access; there is no self-serve route. `admin_resolution_required` means the details you sent match more than one customer, and your Thomas account manager will resolve it with you.

    Request
    curl -X POST "$THOMAS_API_URL/tenants" \
      -H "Authorization: Bearer $THOMAS_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"legalName":"Acme Ltd","countryCode":"GB"}'
  2. 2
    Sync its people

    Now you are working inside the customer, so every call names it in the Thomas-Tenant-Id header. Send up to 100 rows per call. Every row carries your own partnerReference for the person and the sourceVersion of your record. Re-sending a version you already sent replays it, so re-running your directory sync is safe.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues/bulk
    Request
    POST /colleagues/bulk
    { "rows": [{ "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }] }
  3. 3
    Withdraw a test customer

    `POST /tenants/{tenantId}/withdraw` withdraws a customer you created, which is the call you want while building against throwaway customers. It acts above a tenant, so it sends no Thomas-Tenant-Id header: the customer is named in the path.

    Request
    curl -X POST "$THOMAS_API_URL/tenants/$THOMAS_TENANT_ID/withdraw" \
      -H "Authorization: Bearer $THOMAS_TOKEN"
  4. 4
    Recover it within 30 days

    `POST /tenants/{tenantId}/recover` restores a customer you withdrew, within 30 days, to the exact lifecycle it held before: the customer, its Grant, its Service Membership and its source-of-record claims become live again under their original identities.

    Request
    curl -X POST "$THOMAS_API_URL/tenants/$THOMAS_TENANT_ID/recover" \
      -H "Authorization: Bearer $THOMAS_TOKEN"
Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

Day one: bring your directory into Thomas and trigger everyone's first assessment, so profiles exist before you build anything on top. About 100 batched calls for 10,000 people, not 10,000.

  1. 1
    Validate a batch first (dry run)

    Send up to 100 rows to validate, each with your partnerReference and sourceVersion. Nothing is written. You get a per-row classification (ready, reactivate or rejected) and a summary. If a row needs attention, the rest can still proceed.

    POSThttps://api.thomas.co/v1/colleagues/bulk/validate
    Request
    POST /colleagues/bulk/validate
    { "rows": [{ "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }] }
    Response
    {
      "results": [
        {
          "index": 0,
          "partnerReference": "worker-uk-004271",
          "classification": "ready"
        }
      ],
      "summary": {
        "ready": 98,
        "reactivate": 2,
        "rejected": 0
      }
    }
  2. 2
    Commit the batch

    Send the confirmed rows, each with your partnerReference and sourceVersion. Every row gets its own outcome: created, reconciled (the person was already in the organisation), replayed (you sent this version before), reactivate (an archived profile Thomas offers to restore through the reactivate route) or failed. A failed row leaves the others committed, so retry only the failed rows. Email is the persistent identity key. An HRIS id is scoped to your org. Each created, reconciled or replayed row returns its individualId. Collect those to assign assessments in the next step.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues/bulk
    Request
    POST /colleagues/bulk
    { "rows": [{ "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }] }
    Response
    {
      "results": [
        {
          "index": 0,
          "partnerReference": "worker-uk-004271",
          "outcome": "created",
          "companyIndividualId": "00000000-0000-4000-8000-000000000031",
          "individualId": "00000000-0000-4000-8000-000000000021",
          "sourceVersion": 1
        }
      ],
      "summary": {
        "created": 98,
        "reconciled": 2,
        "replayed": 0,
        "reactivate": 0,
        "failed": 0
      }
    }
  3. 3
    Trigger assessments in bulk

    Assign the behaviour assessment to up to 100 individuals per call. Already-assessed people are skipped, so it is safe to re-run.

    POSThttps://api.thomas.co/v1/assessments/assignments/bulk
    Request
    POST /assessments/assignments/bulk
    { "individualIds": ["00000000-0000-4000-8000-000000000021"], "assessmentType": "behaviour" }
    Response
    {
      "created": 1,
      "skipped": 0,
      "results": [{
        "id": "00000000-0000-4000-8000-000000000031",
        "tenantId": "00000000-0000-4000-8000-000000000001",
        "individualId": "00000000-0000-4000-8000-000000000021",
        "assessmentType": "behaviour",
        "assignedBy": "00000000-0000-4000-8000-000000000011",
        "assignedAt": "2026-10-01T00:00:00.000Z",
        "status": "PENDING",
        "createdAt": "2026-10-01T00:00:00.000Z",
        "updatedAt": "2026-10-01T00:00:00.000Z"
      }],
      "outcomes": [{
        "individualId": "00000000-0000-4000-8000-000000000021",
        "status": "created"
      }]
    }
  4. 4
    Poll an assignment until complete

    Poll each assignment until its status is COMPLETED or EXPIRED, then read the profile of each COMPLETED one.

    GEThttps://api.thomas.co/v1/assessments/assignments/{id}
    Request
    GET /assessments/assignments/00000000-0000-4000-8000-000000000031
    Response
    {
      "id": "00000000-0000-4000-8000-000000000031",
      "tenantId": "00000000-0000-4000-8000-000000000001",
      "individualId": "00000000-0000-4000-8000-000000000021",
      "assessmentType": "behaviour",
      "assignedBy": "00000000-0000-4000-8000-000000000011",
      "assignedAt": "2026-10-01T00:00:00.000Z",
      "status": "PENDING",
      "createdAt": "2026-10-01T00:00:00.000Z",
      "updatedAt": "2026-10-01T00:00:00.000Z"
    }
  5. 5
    List assignments to track progress

    List assignments to see how many have completed across the batch.

    GEThttps://api.thomas.co/v1/assessments/assignments
    Request
    GET /assessments/assignments?status=COMPLETED
    Response
    {
      "data": [{
        "id": "00000000-0000-4000-8000-000000000031",
        "tenantId": "00000000-0000-4000-8000-000000000001",
        "individualId": "00000000-0000-4000-8000-000000000021",
        "assessmentType": "behaviour",
        "assignedBy": "00000000-0000-4000-8000-000000000011",
        "assignedAt": "2026-10-01T00:00:00.000Z",
        "status": "COMPLETED",
        "createdAt": "2026-10-01T00:00:00.000Z",
        "updatedAt": "2026-10-02T09:30:00.000Z"
      }],
      "total": 87,
      "nextCursor": null
    }
  6. 6
    React to completion (webhook)
    Coming soon

    A result webhook (an assessment.completed event) is coming soon. Until then, poll the assignment status as above.

Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

People & profiles

Trigger a Thomas assessment for one colleague, then read their behaviour profile.

  1. 1
    Resolve the person

    Resolve the person to one Thomas identity: by email, the persistent case-insensitive identity key, or by your org-scoped HRIS id. Prefer not to send an email? Create and update employees with your own partner reference. Thomas matches it within your connection.

    How identity works
    POSThttps://api.thomas.co/v1/individuals/resolve
    Request
    POST /individuals/resolve
    { "email": "alex@acme.com" }
    Response
    {
      "individualId": "00000000-0000-4000-8000-000000000021",
      "created": true
    }
  2. 2
    Enrol as a colleague

    Resolve returns a global identity with no tenant membership, which is not available to org-scoped calls. Enrol the person as a colleague: createColleague resolves and enrols by email in one call, as relationshipType employee, before you assign the assessment. Send your own partnerReference for the person and the sourceVersion of your record with it.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues
    Request
    POST /colleagues
    { "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }
  3. 3
    Assign the assessment

    Assign the behaviour assessment to the resolved individual. The assignment call takes a list of individualIds, so send a list of one. A created assignment comes back in results, with the id you poll below.

    POSThttps://api.thomas.co/v1/assessments/assignments/bulk
    Request
    POST /assessments/assignments/bulk
    { "individualIds": ["00000000-0000-4000-8000-000000000021"], "assessmentType": "behaviour" }
    Response
    {
      "created": 1,
      "skipped": 0,
      "results": [{
        "id": "00000000-0000-4000-8000-000000000031",
        "tenantId": "00000000-0000-4000-8000-000000000001",
        "individualId": "00000000-0000-4000-8000-000000000021",
        "assessmentType": "behaviour",
        "assignedBy": "00000000-0000-4000-8000-000000000011",
        "assignedAt": "2026-10-01T00:00:00.000Z",
        "status": "PENDING",
        "createdAt": "2026-10-01T00:00:00.000Z",
        "updatedAt": "2026-10-01T00:00:00.000Z"
      }],
      "outcomes": [{
        "individualId": "00000000-0000-4000-8000-000000000021",
        "status": "created"
      }]
    }
  4. 4
    The person completes the assessment

    The person must complete and submit the assessment themselves in Thomas. The API cannot start, complete, or score it on their behalf. Their behaviour profile becomes available only once they have finished and it has been scored. Until then the profile read below returns 404 ("No scored assessment found for this individual").

  5. 5
    Poll the assignment

    Poll until the assignment status is COMPLETED or EXPIRED. A scored result becomes available only once the person has submitted the assessment (the previous step), not from the assignment being created.

    GEThttps://api.thomas.co/v1/assessments/assignments/{id}
    Request
    GET /assessments/assignments/00000000-0000-4000-8000-000000000031
    Response
    {
      "id": "00000000-0000-4000-8000-000000000031",
      "tenantId": "00000000-0000-4000-8000-000000000001",
      "individualId": "00000000-0000-4000-8000-000000000021",
      "assessmentType": "behaviour",
      "assignedBy": "00000000-0000-4000-8000-000000000011",
      "assignedAt": "2026-10-01T00:00:00.000Z",
      "status": "COMPLETED",
      "resultId": "00000000-0000-4000-8000-000000000041",
      "createdAt": "2026-10-01T00:00:00.000Z",
      "updatedAt": "2026-10-02T09:30:00.000Z"
    }
  6. 6
    Read the behaviour profile

    Once the person has completed the assessment and it has been scored, fetch their validated behaviour profile. Before a scored result exists this returns 404 ("No scored assessment found for this individual").

    GEThttps://api.thomas.co/v1/assessments/profile/{individualId}
    Request
    GET /assessments/profile/00000000-0000-4000-8000-000000000021
    Response
    {
      "connectionPowerType": "action",
      "traitSpectrums": [
        {
          "dimension": "D",
          "name": "Dominance",
          "leftPole": "Accommodating",
          "rightPole": "Direct",
          "position": 82,
          "band": "High"
        },
        {
          "dimension": "I",
          "name": "Influence",
          "leftPole": "Reflective",
          "rightPole": "Outgoing",
          "position": 61,
          "band": "High"
        },
        {
          "dimension": "S",
          "name": "Steadiness",
          "leftPole": "Spontaneous",
          "rightPole": "Methodical",
          "position": 34,
          "band": "Low"
        },
        {
          "dimension": "C",
          "name": "Compliance",
          "leftPole": "Pragmatist",
          "rightPole": "Perfectionist",
          "position": 47,
          "band": "Low"
        }
      ],
      "selfImageScores": {
        "d": 78,
        "i": 55,
        "s": 30,
        "c": 41
      },
      "discAdvanced": {
        "workMaskScores": {
          "d": 71,
          "i": 60,
          "s": 28,
          "c": 44
        },
        "underPressureScores": {
          "d": 66,
          "i": 52,
          "s": 35,
          "c": 49
        }
      },
      "scoredAt": "2026-10-02T09:30:00.000Z"
    }
  7. 7
    React to completion (webhook)
    Coming soon

    Result webhooks (an assessment.completed event) are coming soon. For now, poll the assignment status.

Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

Pull a person's profile into your review screen, so a manager sees how to get the best from them, not just a rating.

  1. 1
    Resolve the person

    Resolve the reviewee to one Thomas identity: by email, the persistent identity key, or by your org-scoped HRIS id.

    How identity works
    POSThttps://api.thomas.co/v1/individuals/resolve
    Request
    POST /individuals/resolve
    { "email": "alex@acme.com" }
    Response
    {
      "individualId": "00000000-0000-4000-8000-000000000021",
      "created": true
    }
  2. 2
    Enrol as a colleague

    Resolve returns a global identity with no tenant membership, so the profile read requires enrolment. Enrol the reviewee as a colleague: createColleague resolves and enrols by email in one call, as relationshipType employee, before you read their profile. Send your own partnerReference for the person and the sourceVersion of your record with it.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues
    Request
    POST /colleagues
    { "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }
  3. 3
    Read the behaviour profile

    Fetch the trait spectrums and the "how to work with this person" insight. This reads only once the person has completed a Thomas behaviour assessment. Completing it is something only they can do. It cannot be triggered on their behalf through the API, and until then it returns 404 ("No scored assessment found for this individual").

    GEThttps://api.thomas.co/v1/assessments/profile/{individualId}
    Request
    GET /assessments/profile/00000000-0000-4000-8000-000000000021
    Response
    {
      "connectionPowerType": "energy",
      "traitSpectrums": [
        {
          "dimension": "D",
          "name": "Dominance",
          "leftPole": "Accommodating",
          "rightPole": "Direct",
          "position": 58,
          "band": "High"
        },
        {
          "dimension": "I",
          "name": "Influence",
          "leftPole": "Reflective",
          "rightPole": "Outgoing",
          "position": 64,
          "band": "High"
        },
        {
          "dimension": "S",
          "name": "Steadiness",
          "leftPole": "Spontaneous",
          "rightPole": "Methodical",
          "position": 41,
          "band": "Low"
        },
        {
          "dimension": "C",
          "name": "Compliance",
          "leftPole": "Pragmatist",
          "rightPole": "Perfectionist",
          "position": 38,
          "band": "Low"
        }
      ],
      "selfImageScores": {
        "d": 62,
        "i": 70,
        "s": 44,
        "c": 38
      },
      "discAdvanced": {
        "workMaskScores": {
          "d": 58,
          "i": 66,
          "s": 49,
          "c": 41
        },
        "underPressureScores": {
          "d": 64,
          "i": 61,
          "s": 40,
          "c": 45
        }
      },
      "scoredAt": "2026-10-02T09:30:00.000Z"
    }
  4. 4
    Render the insight in your review UIyour system

    Show the trait spectrums and the "how to work with this person" insight alongside the manager's ratings in your own review screen.

Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

Surface how to communicate with a specific person, right where the conversation already happens.

  1. 1
    Resolve the person

    Resolve the person to one Thomas identity: by email, the persistent identity key, or by your org-scoped HRIS id.

    How identity works
    POSThttps://api.thomas.co/v1/individuals/resolve
    Request
    POST /individuals/resolve
    { "email": "alex@acme.com" }
    Response
    {
      "individualId": "00000000-0000-4000-8000-000000000021",
      "created": true
    }
  2. 2
    Enrol as a colleague

    Resolve returns a global identity with no tenant membership, so the profile read requires enrolment. Enrol the person as a colleague: createColleague resolves and enrols by email in one call, as relationshipType employee, before you read their profile. Send your own partnerReference for the person and the sourceVersion of your record with it.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues
    Request
    POST /colleagues
    { "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }
  3. 3
    Read the behaviour profile

    Fetch the trait spectrums and the "how to work with this person" insight. This reads only once the person has completed a Thomas behaviour assessment. Completing it is something only they can do. It cannot be triggered on their behalf through the API, and until then it returns 404 ("No scored assessment found for this individual").

    GEThttps://api.thomas.co/v1/assessments/profile/{individualId}
    Request
    GET /assessments/profile/00000000-0000-4000-8000-000000000021
    Response
    {
      "connectionPowerType": "energy",
      "traitSpectrums": [
        {
          "dimension": "D",
          "name": "Dominance",
          "leftPole": "Accommodating",
          "rightPole": "Direct",
          "position": 58,
          "band": "High"
        },
        {
          "dimension": "I",
          "name": "Influence",
          "leftPole": "Reflective",
          "rightPole": "Outgoing",
          "position": 64,
          "band": "High"
        },
        {
          "dimension": "S",
          "name": "Steadiness",
          "leftPole": "Spontaneous",
          "rightPole": "Methodical",
          "position": 41,
          "band": "Low"
        },
        {
          "dimension": "C",
          "name": "Compliance",
          "leftPole": "Pragmatist",
          "rightPole": "Perfectionist",
          "position": 38,
          "band": "Low"
        }
      ],
      "selfImageScores": {
        "d": 62,
        "i": 70,
        "s": 44,
        "c": 38
      },
      "discAdvanced": {
        "workMaskScores": {
          "d": 58,
          "i": 66,
          "s": 49,
          "c": 41
        },
        "underPressureScores": {
          "d": 64,
          "i": 61,
          "s": 40,
          "c": 45
        }
      },
      "scoredAt": "2026-10-02T09:30:00.000Z"
    }
  4. 4
    Nudge in your messaging platformyour system

    Surface the "how to work with this person" insight in your messaging platform at the right moment.

Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

Recommend the next learning step from a person's profile.

  1. 1
    Resolve the person

    Resolve the person to one Thomas identity: by email, the persistent identity key, or by your org-scoped HRIS id.

    How identity works
    POSThttps://api.thomas.co/v1/individuals/resolve
    Request
    POST /individuals/resolve
    { "email": "alex@acme.com" }
    Response
    {
      "individualId": "00000000-0000-4000-8000-000000000021",
      "created": true
    }
  2. 2
    Enrol as a colleague

    Resolve returns a global identity with no tenant membership, so the profile read requires enrolment. Enrol the person as a colleague: createColleague resolves and enrols by email in one call, as relationshipType employee, before you read their profile. Send your own partnerReference for the person and the sourceVersion of your record with it.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues
    Request
    POST /colleagues
    { "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }
  3. 3
    Read the behaviour profile

    Fetch the trait spectrums that inform the recommendation. This reads only once the person has completed a Thomas behaviour assessment. Completing it is something only they can do. It cannot be triggered on their behalf through the API, and until then it returns 404 ("No scored assessment found for this individual").

    GEThttps://api.thomas.co/v1/assessments/profile/{individualId}
    Request
    GET /assessments/profile/00000000-0000-4000-8000-000000000021
    Response
    {
      "connectionPowerType": "energy",
      "traitSpectrums": [
        {
          "dimension": "D",
          "name": "Dominance",
          "leftPole": "Accommodating",
          "rightPole": "Direct",
          "position": 58,
          "band": "High"
        },
        {
          "dimension": "I",
          "name": "Influence",
          "leftPole": "Reflective",
          "rightPole": "Outgoing",
          "position": 64,
          "band": "High"
        },
        {
          "dimension": "S",
          "name": "Steadiness",
          "leftPole": "Spontaneous",
          "rightPole": "Methodical",
          "position": 41,
          "band": "Low"
        },
        {
          "dimension": "C",
          "name": "Compliance",
          "leftPole": "Pragmatist",
          "rightPole": "Perfectionist",
          "position": 38,
          "band": "Low"
        }
      ],
      "selfImageScores": {
        "d": 62,
        "i": 70,
        "s": 44,
        "c": 38
      },
      "discAdvanced": {
        "workMaskScores": {
          "d": 58,
          "i": 66,
          "s": 49,
          "c": 41
        },
        "underPressureScores": {
          "d": 64,
          "i": 61,
          "s": 40,
          "c": 45
        }
      },
      "scoredAt": "2026-10-02T09:30:00.000Z"
    }
  4. 4
    Map to learning pathsyour system

    Map the profile to paths in your LMS: a doer gets a short video plus a hands-on exercise, not long reading.

Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

Give a manager grounded guidance on each report, on demand. One step acts on behalf of the report, when they choose to share.

  1. 1
    Resolve the report

    Resolve the report to one Thomas identity: by email, the persistent identity key, or by your org-scoped HRIS id.

    How identity works
    POSThttps://api.thomas.co/v1/individuals/resolve
    Request
    POST /individuals/resolve
    { "email": "alex@acme.com" }
    Response
    {
      "individualId": "00000000-0000-4000-8000-000000000021",
      "created": true
    }
  2. 2
    Enrol as a colleague

    Resolve returns a global identity with no tenant membership, so the profile read requires enrolment. Enrol the report as a colleague: createColleague resolves and enrols by email in one call, as relationshipType employee, before you read their profile. Send your own partnerReference for the person and the sourceVersion of your record with it.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues
    Request
    POST /colleagues
    { "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }
  3. 3
    Read the report's profile

    Fetch the trait spectrums and self-image scores that ground the manager's coaching. This reads only once the person has completed a Thomas behaviour assessment. Completing it is something only they can do. It cannot be triggered on their behalf through the API, and until then it returns 404 ("No scored assessment found for this individual").

    GEThttps://api.thomas.co/v1/assessments/profile/{individualId}
    Request
    GET /assessments/profile/00000000-0000-4000-8000-000000000021
    Response
    {
      "connectionPowerType": "energy",
      "traitSpectrums": [
        {
          "dimension": "D",
          "name": "Dominance",
          "leftPole": "Accommodating",
          "rightPole": "Direct",
          "position": 58,
          "band": "High"
        },
        {
          "dimension": "I",
          "name": "Influence",
          "leftPole": "Reflective",
          "rightPole": "Outgoing",
          "position": 64,
          "band": "High"
        },
        {
          "dimension": "S",
          "name": "Steadiness",
          "leftPole": "Spontaneous",
          "rightPole": "Methodical",
          "position": 41,
          "band": "Low"
        },
        {
          "dimension": "C",
          "name": "Compliance",
          "leftPole": "Pragmatist",
          "rightPole": "Perfectionist",
          "position": 38,
          "band": "Low"
        }
      ],
      "selfImageScores": {
        "d": 62,
        "i": 70,
        "s": 44,
        "c": 38
      },
      "discAdvanced": {
        "workMaskScores": {
          "d": 58,
          "i": 66,
          "s": 49,
          "c": 41
        },
        "underPressureScores": {
          "d": 64,
          "i": 61,
          "s": 40,
          "c": 45
        }
      },
      "scoredAt": "2026-10-02T09:30:00.000Z"
    }
  4. 4
    Enrol the manager as a colleague

    Enrol the manager in this tenant before the report shares with them. On a created or reconciled result, keep the returned tenant membership UUID for the share recipient in the next step. If Thomas returns a reactivation offer, complete that path before sharing. Send your own partnerReference for the person and the sourceVersion of your record with it.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues
    Request
    POST /colleagues
    { "email": "sam.manager@acme.com", "partnerReference": "worker-uk-004273", "sourceVersion": 1 }
  5. 5
    Let the report share their overview with their manager

    Sharing is the report's own choice, so this call is made on behalf of the report, never by your integration as itself. Make it only when the report asks to share, in your product, while you are serving them. Name the report in the Thomas-On-Behalf-Of header, as thomas: followed by their Thomas id, or partner: followed by your own reference for them. Your Grant needs users:w. Set recipientCompanyIndividualId to the manager's tenant membership UUID returned by the preceding enrol step, rather than the global identity returned by resolve. A report cannot share with themselves: that is refused 400. Show person-only data only to that person, and act on their behalf only while you are serving them.

    How identity works
    POSThttps://api.thomas.co/v1/share-grants
    Request
    POST /share-grants
    { "mode": "single", "recipientCompanyIndividualId": "00000000-0000-4000-8000-000000000041", "dataType": "behaviour_overview" }
    Response
    {
      "id": "00000000-0000-4000-8000-000000000051",
      "tenant_id": "00000000-0000-4000-8000-000000000001",
      "sharer_company_individual_id": "00000000-0000-4000-8000-000000000022",
      "recipient_company_individual_id": "00000000-0000-4000-8000-000000000041",
      "data_type": "behaviour_overview",
      "source": "voluntary",
      "recipient_role": "manager",
      "created_at": "2026-10-02T09:30:00.000Z",
      "revoked_at": null
    }
  6. 6
    Surface the insight to the manageryour system

    Only after the report chooses to share and createShareGrant succeeds, show the permitted behaviour overview and guidance to that manager in your own product.

Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

Ask Thomas for grounded coaching for one named person, in the flow of work. Two calls on behalf of that person, and the reply streams back as Server-Sent Events.

  1. 1
    Open a coaching conversation

    Coaching is always for one named person, never for your integration itself. Name them in the Thomas-On-Behalf-Of header, as thomas: followed by their Thomas id, or partner: followed by your own reference for them. Your Grant needs users:w. The conversation belongs to that person; their permissions and your Grant's users:w scope still apply. Show person-only data only to that person, and act on their behalf only while you are serving them. Pick a context: personal, manager, or org_analyst.

    How identity works
    POSThttps://api.thomas.co/v1/ai-coaching/conversations
    Request
    POST /ai-coaching/conversations
    { "contextTag": "personal", "entryPoint": "prompt_card" }
    Response
    {
      "id": "00000000-0000-4000-8000-000000000091",
      "contextTag": "personal",
      "entryPoint": "prompt_card",
      "pageId": "individual",
      "title": "Coaching conversation",
      "lastMessageAt": "2026-10-02T09:30:00.000Z",
      "createdAt": "2026-10-02T09:30:00.000Z",
      "updatedAt": "2026-10-02T09:30:00.000Z"
    }
  2. 2
    Send a message, stream the reply

    POST the person's intent to the conversation, on behalf of the same person. The answer streams back as Server-Sent Events (tokens), not a single JSON body, so render it as it arrives. A stream cannot be replayed, so do not send an Idempotency-Key here: retry with the same clientMessageId instead. Coaching calls are limited per person and per integration, and a 429 tells you when to try again.

    How identity works
    POSThttps://api.thomas.co/v1/ai-coaching/conversations/{conversationId}/messages
    Request
    POST /ai-coaching/conversations/00000000-0000-4000-8000-000000000091/messages
    { "intent": "How should I prepare for my own performance review?", "clientMessageId": "00000000-0000-4000-8000-000000000031", "pageId": "home", "pageParams": {}, "entryPoint": "prompt_card" }
    Response
    event: metadata
    data: {"kind":"metadata","mlflowTraceId":"tr-00000000000000000000000000000091","conversationId":"00000000-0000-4000-8000-000000000091","messageId":"00000000-0000-4000-8000-000000000092"}
    
    event: token
    data: {"kind":"token","text":"Start by naming what you want out of the review..."}
    
    event: done
    data: {"kind":"done","outputTokens":42}
  3. 3
    Surface it where work happensyour system

    Drop the streamed coaching into your review screen, messaging platform, or wherever work already happens, for the person you named.

Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

Teams & connection

Group resolved people into a team and read its dynamic: the spread of connection powers and where to watch out. No single team score is available here. That is the tracking recipe.

  1. 1
    Enrol the team members

    Only enrolled colleagues can be grouped into a team. A resolved-but-unenrolled member is rejected. Enrol every member you will reference in the team in one bulk call (resolves and enrols by email, idempotent): send one row per member, each carrying its email, your partnerReference for them and the sourceVersion of your record, then reference each enrolled individual by individualId when you create the team below.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues/bulk
    Request
    POST /colleagues/bulk
    { "rows": [{ "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }, { "email": "jordan@acme.com", "partnerReference": "worker-uk-004272", "sourceVersion": 1 }] }
    Response
    {
      "results": [
        {
          "index": 0,
          "partnerReference": "worker-uk-004271",
          "outcome": "created",
          "companyIndividualId": "00000000-0000-4000-8000-000000000031",
          "individualId": "00000000-0000-4000-8000-000000000021",
          "sourceVersion": 1
        },
        {
          "index": 1,
          "partnerReference": "worker-uk-004272",
          "outcome": "created",
          "companyIndividualId": "00000000-0000-4000-8000-000000000032",
          "individualId": "00000000-0000-4000-8000-000000000022",
          "sourceVersion": 1
        }
      ],
      "summary": {
        "created": 2,
        "reconciled": 0,
        "replayed": 0,
        "reactivate": 0,
        "failed": 0
      }
    }
  2. 2
    Create the team

    Group the resolved individuals into a team. Send your own externalTeamReference in the body: it is recorded against your API client and read back beside the Thomas id, but it is never an address. sourceVersion records which version of your own record this write came from. It is provenance, not an ordering key. List every owner in ownerIndividualIds, because your API client is never added, and keep owners out of memberIndividualIds. Omitting visibility creates the team hidden, which is what the response below shows; send visibility "visible" to share it with its members, and firstSharedAt is stamped the first time you do. Keep the id the response returns: every later call to this team addresses that Thomas team id. Creating again with a reference you have already bound is refused 409, naming the Thomas team it belongs to.

    POSThttps://api.thomas.co/v1/teams
    Request
    POST /teams
    { "externalTeamReference": "team-revenue", "sourceVersion": 1, "name": "Revenue", "ownerIndividualIds": ["00000000-0000-4000-8000-000000000021"], "memberIndividualIds": ["00000000-0000-4000-8000-000000000022"] }
    Response
    {
      "id": "00000000-0000-4000-8000-000000000041",
      "externalTeamReference": "team-revenue",
      "tenantId": "00000000-0000-4000-8000-000000000001",
      "name": "Revenue",
      "teamType": "explicit",
      "sourceManagerIndividualId": null,
      "visibility": "hidden",
      "firstSharedAt": null,
      "needsOwnerAssignment": false,
      "deletedAt": null,
      "inactiveAt": null,
      "createdAt": "2026-07-02T10:00:00.000Z",
      "updatedAt": "2026-07-02T10:00:00.000Z",
      "members": [{
        "id": "00000000-0000-4000-8000-000000000061",
        "teamId": "00000000-0000-4000-8000-000000000041",
        "individualId": "00000000-0000-4000-8000-000000000021",
        "companyIndividualId": "00000000-0000-4000-8000-000000000031",
        "tenantId": "00000000-0000-4000-8000-000000000001",
        "role": "owner",
        "status": "active",
        "createdAt": "2026-07-02T10:00:00.000Z",
        "updatedAt": "2026-07-02T10:00:00.000Z"
      }, {
        "id": "00000000-0000-4000-8000-000000000062",
        "teamId": "00000000-0000-4000-8000-000000000041",
        "individualId": "00000000-0000-4000-8000-000000000022",
        "companyIndividualId": "00000000-0000-4000-8000-000000000032",
        "tenantId": "00000000-0000-4000-8000-000000000001",
        "role": "member",
        "status": "active",
        "createdAt": "2026-07-02T10:00:00.000Z",
        "updatedAt": "2026-07-02T10:00:00.000Z"
      }]
    }
  3. 3
    Read the dynamic

    Get the power distribution, the dominant power, and strengths/watch-outs. Privacy-safe: a minimum group of three. Address the team by the Thomas team id the create returned.

    GEThttps://api.thomas.co/v1/teams/{teamId}/dynamics
    Request
    GET /teams/00000000-0000-4000-8000-000000000041/dynamics
    Response
    {
      "teamId": "00000000-0000-4000-8000-000000000041",
      "members": [{
        "individualId": "00000000-0000-4000-8000-000000000021",
        "displayName": "Alex",
        "connectionPower": "action",
        "behaviourScales": {
          "accommodatingDirect": 72,
          "reflectiveOutgoing": 45,
          "spontaneousMethodical": 88,
          "pragmaticPerfectionist": 33
        },
        "assessmentStatus": "assessed"
      }],
      "powerDistribution": { "action": 1, "energy": 0, "stability": 0, "precision": 0 },
      "dominantPower": "action",
      "strengthsAndWatchouts": [{
        "type": "strength",
        "title": "Fast execution",
        "description": "The team moves quickly once aligned.",
        "narrativeContent": null
      }],
      "teamProfileSummary": null
    }
Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

Read a team's connection score and how it is trending across runs.

  1. 1
    Enrol the team members

    Only enrolled colleagues can be grouped into a team. A resolved-but-unenrolled member is rejected. Enrol every member you will reference in the team in one bulk call (resolves and enrols by email, idempotent): send one row per member, each carrying its email, your partnerReference for them and the sourceVersion of your record, then reference each enrolled individual by individualId when you create the team below.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues/bulk
    Request
    POST /colleagues/bulk
    { "rows": [{ "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }, { "email": "jordan@acme.com", "partnerReference": "worker-uk-004272", "sourceVersion": 1 }] }
    Response
    {
      "results": [
        {
          "index": 0,
          "partnerReference": "worker-uk-004271",
          "outcome": "created",
          "companyIndividualId": "00000000-0000-4000-8000-000000000031",
          "individualId": "00000000-0000-4000-8000-000000000021",
          "sourceVersion": 1
        },
        {
          "index": 1,
          "partnerReference": "worker-uk-004272",
          "outcome": "created",
          "companyIndividualId": "00000000-0000-4000-8000-000000000032",
          "individualId": "00000000-0000-4000-8000-000000000022",
          "sourceVersion": 1
        }
      ],
      "summary": {
        "created": 2,
        "reconciled": 0,
        "replayed": 0,
        "reactivate": 0,
        "failed": 0
      }
    }
  2. 2
    Create or reuse the team

    Group the individuals whose connection you want to track over time. Send your own externalTeamReference in the body: it is recorded against your API client and read back beside the Thomas id, but it is never an address. sourceVersion records which version of your own record this write came from. It is provenance, not an ordering key. List every owner in ownerIndividualIds, because your API client is never added, and keep owners out of memberIndividualIds. Omitting visibility creates the team hidden, which is what the response below shows; send visibility "visible" to share it with its members, and firstSharedAt is stamped the first time you do. Keep the id it returns: the results and recurrence calls below address the team by that Thomas id. If you have lost which Thomas id your reference maps to, list your teams with listConnectionTeams, following nextCursor until it is null, rather than creating again: a repeat create on a bound reference is refused 409.

    POSThttps://api.thomas.co/v1/teams
    Request
    POST /teams
    { "externalTeamReference": "team-revenue", "sourceVersion": 1, "name": "Revenue", "ownerIndividualIds": ["00000000-0000-4000-8000-000000000021"], "memberIndividualIds": ["00000000-0000-4000-8000-000000000022"] }
    Response
    {
      "id": "00000000-0000-4000-8000-000000000051",
      "externalTeamReference": "team-revenue",
      "tenantId": "00000000-0000-4000-8000-000000000001",
      "name": "Revenue",
      "teamType": "explicit",
      "sourceManagerIndividualId": null,
      "visibility": "hidden",
      "firstSharedAt": null,
      "needsOwnerAssignment": false,
      "deletedAt": null,
      "inactiveAt": null,
      "createdAt": "2026-07-02T10:00:00.000Z",
      "updatedAt": "2026-07-02T10:00:00.000Z",
      "members": [{
        "id": "00000000-0000-4000-8000-000000000071",
        "teamId": "00000000-0000-4000-8000-000000000051",
        "individualId": "00000000-0000-4000-8000-000000000021",
        "companyIndividualId": "00000000-0000-4000-8000-000000000031",
        "tenantId": "00000000-0000-4000-8000-000000000001",
        "role": "owner",
        "status": "active",
        "createdAt": "2026-07-02T10:00:00.000Z",
        "updatedAt": "2026-07-02T10:00:00.000Z"
      }, {
        "id": "00000000-0000-4000-8000-000000000072",
        "teamId": "00000000-0000-4000-8000-000000000051",
        "individualId": "00000000-0000-4000-8000-000000000022",
        "companyIndividualId": "00000000-0000-4000-8000-000000000032",
        "tenantId": "00000000-0000-4000-8000-000000000001",
        "role": "member",
        "status": "active",
        "createdAt": "2026-07-02T10:00:00.000Z",
        "updatedAt": "2026-07-02T10:00:00.000Z"
      }]
    }
  3. 3
    Read the team results page

    Get the team results page: the aggregate score, its six-dimension breakdown, and the trend versus prior runs.

    GEThttps://api.thomas.co/v1/teams/{teamId}/runs/{runId}/team-results-page
    Request
    GET /teams/00000000-0000-4000-8000-000000000051/runs/00000000-0000-4000-8000-000000000052/team-results-page
    Response
    {
      "teamResult": { "runId": "00000000-0000-4000-8000-000000000052", "teamId": "00000000-0000-4000-8000-000000000051", "dimensions": [{ "dimension": "appreciation", "score": 78, "banding": "MODERATE" }, { "dimension": "belonging", "score": 74, "banding": "MODERATE" }, { "dimension": "cohesion", "score": 64, "banding": "LOW" }, { "dimension": "contribution", "score": 76, "banding": "MODERATE" }, { "dimension": "trust", "score": 82, "banding": "MODERATE" }, { "dimension": "wellbeing", "score": 70, "banding": "MODERATE" }], "overallScore": 72, "overallBanding": "MODERATE", "participantCount": 8, "scoresAvailable": true, "participantsNeeded": 0, "trendDeltas": null, "overallTrend": null, "focusArea": "cohesion" },
      "teamName": "Revenue",
      "teamId": "00000000-0000-4000-8000-000000000051",
      "teamType": "explicit",
      "runPeriod": "2026-07",
      "closedAt": "2026-07-02T10:15:00Z",
      "availablePeriods": ["2026-07", "2026-04"],
      "previousResult": null,
      "narratives": null,
      "trailingSeries": { "overall": [68, 72], "dimensions": { "appreciation": [74, 78], "belonging": [70, 74], "cohesion": [60, 64], "contribution": [72, 76], "trust": [80, 82], "wellbeing": [68, 70] } },
      "viewerHasCompleted": true
    }
  4. 4
    Schedule recurring runs (optional)

    Optionally configure a recurring cadence so the trend keeps updating on its own.

    PUThttps://api.thomas.co/v1/organisation/teams/{teamId}/recurrence
    Request
    PUT /organisation/teams/00000000-0000-4000-8000-000000000051/recurrence
    { "interval": "quarterly", "nextRunDate": "2030-01-01T00:00:00.000Z", "enabled": true }
    Response
    {
      "interval": "quarterly",
      "nextRunDate": "2030-01-01T00:00:00.000Z",
      "enabled": true,
      "measureId": "00000000-0000-4000-8000-000000000071",
      "teamId": "00000000-0000-4000-8000-000000000051",
      "recurrencePersisted": true
    }
Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

Compose or rebalance a team using its connection dynamic.

  1. 1
    Enrol the team members

    Only enrolled colleagues can be grouped into a team. A resolved-but-unenrolled member is rejected. Enrol every member you will reference in the team in one bulk call (resolves and enrols by email, idempotent): send one row per member, each carrying its email, your partnerReference for them and the sourceVersion of your record, then reference each enrolled individual by individualId when you create the team below.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues/bulk
    Request
    POST /colleagues/bulk
    { "rows": [{ "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }, { "email": "jordan@acme.com", "partnerReference": "worker-uk-004272", "sourceVersion": 1 }] }
    Response
    {
      "results": [
        {
          "index": 0,
          "partnerReference": "worker-uk-004271",
          "outcome": "created",
          "companyIndividualId": "00000000-0000-4000-8000-000000000031",
          "individualId": "00000000-0000-4000-8000-000000000021",
          "sourceVersion": 1
        },
        {
          "index": 1,
          "partnerReference": "worker-uk-004272",
          "outcome": "created",
          "companyIndividualId": "00000000-0000-4000-8000-000000000032",
          "individualId": "00000000-0000-4000-8000-000000000022",
          "sourceVersion": 1
        }
      ],
      "summary": {
        "created": 2,
        "reconciled": 0,
        "replayed": 0,
        "reactivate": 0,
        "failed": 0
      }
    }
  2. 2
    Create the team

    Group the people you are considering into a team. Send your own externalTeamReference in the body: it is recorded against your API client and read back beside the Thomas id, but it is never an address. sourceVersion records which version of your own record this write came from. It is provenance, not an ordering key. List every owner in ownerIndividualIds, because your API client is never added, and keep owners out of memberIndividualIds. Omitting visibility creates the team hidden, which is what the response below shows; send visibility "visible" to share it with its members, and firstSharedAt is stamped the first time you do. Keep the id the response returns: every later call to this team addresses that Thomas team id.

    POSThttps://api.thomas.co/v1/teams
    Request
    POST /teams
    { "externalTeamReference": "team-revenue", "sourceVersion": 1, "name": "Revenue", "ownerIndividualIds": ["00000000-0000-4000-8000-000000000021"], "memberIndividualIds": ["00000000-0000-4000-8000-000000000022"] }
    Response
    {
      "id": "00000000-0000-4000-8000-000000000041",
      "externalTeamReference": "team-revenue",
      "tenantId": "00000000-0000-4000-8000-000000000001",
      "name": "Revenue",
      "teamType": "explicit",
      "sourceManagerIndividualId": null,
      "visibility": "hidden",
      "firstSharedAt": null,
      "needsOwnerAssignment": false,
      "deletedAt": null,
      "inactiveAt": null,
      "createdAt": "2026-07-02T10:00:00.000Z",
      "updatedAt": "2026-07-02T10:00:00.000Z",
      "members": [{
        "id": "00000000-0000-4000-8000-000000000081",
        "teamId": "00000000-0000-4000-8000-000000000041",
        "individualId": "00000000-0000-4000-8000-000000000021",
        "companyIndividualId": "00000000-0000-4000-8000-000000000031",
        "tenantId": "00000000-0000-4000-8000-000000000001",
        "role": "owner",
        "status": "active",
        "createdAt": "2026-07-02T10:00:00.000Z",
        "updatedAt": "2026-07-02T10:00:00.000Z"
      }, {
        "id": "00000000-0000-4000-8000-000000000082",
        "teamId": "00000000-0000-4000-8000-000000000041",
        "individualId": "00000000-0000-4000-8000-000000000022",
        "companyIndividualId": "00000000-0000-4000-8000-000000000032",
        "tenantId": "00000000-0000-4000-8000-000000000001",
        "role": "member",
        "status": "active",
        "createdAt": "2026-07-02T10:00:00.000Z",
        "updatedAt": "2026-07-02T10:00:00.000Z"
      }]
    }
  3. 3
    Read the balance

    See the power spread and where to add or rebalance. Privacy-safe: a minimum group of three. Address the team by the Thomas team id the create returned.

    GEThttps://api.thomas.co/v1/teams/{teamId}/dynamics
    Request
    GET /teams/00000000-0000-4000-8000-000000000041/dynamics
    Response
    {
      "teamId": "00000000-0000-4000-8000-000000000041",
      "members": [{
        "individualId": "00000000-0000-4000-8000-000000000021",
        "displayName": "Alex",
        "connectionPower": "action",
        "behaviourScales": {
          "accommodatingDirect": 72,
          "reflectiveOutgoing": 45,
          "spontaneousMethodical": 88,
          "pragmaticPerfectionist": 33
        },
        "assessmentStatus": "assessed"
      }],
      "powerDistribution": { "action": 1, "energy": 0, "stability": 0, "precision": 0 },
      "dominantPower": "action",
      "strengthsAndWatchouts": [{
        "type": "strength",
        "title": "Fast execution",
        "description": "The team moves quickly once aligned.",
        "narrativeContent": null
      }],
      "teamProfileSummary": null
    }
Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

Lifecycle

Weave Thomas across the systems a new joiner touches, personalised to how they work. The one multi-system example: Thomas calls sit alongside your own HRIS, LMS and calendar.

  1. 1
    Create the joiner in your HRISyour system

    Your HRIS creates the new-hire record. This is the source event that kicks everything off.

  2. 2
    Enrol the joiner as a colleague

    Resolve returns a global identity with no tenant membership, so day-one org-scoped calls would reject the joiner. Enrol them as a colleague instead: createColleague resolves and enrols by email in one call, as relationshipType employee, as soon as their record exists. Email is the persistent identity key. Send your own partnerReference for the person and the sourceVersion of your record with it.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues
    Request
    POST /colleagues
    { "email": "jonah.pierce@acme.com", "partnerReference": "worker-uk-004274", "sourceVersion": 1 }
  3. 3
    Assign the assessment on day one

    Assign the behaviour assessment straight away, so a profile exists before their first week. Send the joiner as a one-element individualIds list.

    POSThttps://api.thomas.co/v1/assessments/assignments/bulk
    Request
    POST /assessments/assignments/bulk
    { "individualIds": ["00000000-0000-4000-8000-000000000022"], "assessmentType": "behaviour" }
    Response
    {
      "created": 1,
      "skipped": 0,
      "results": [{
        "id": "00000000-0000-4000-8000-000000000032",
        "tenantId": "00000000-0000-4000-8000-000000000001",
        "individualId": "00000000-0000-4000-8000-000000000022",
        "assessmentType": "behaviour",
        "assignedBy": "00000000-0000-4000-8000-000000000011",
        "assignedAt": "2026-10-01T00:00:00.000Z",
        "status": "PENDING",
        "createdAt": "2026-10-01T00:00:00.000Z",
        "updatedAt": "2026-10-01T00:00:00.000Z"
      }],
      "outcomes": [{
        "individualId": "00000000-0000-4000-8000-000000000022",
        "status": "created"
      }]
    }
  4. 4
    Read their profile

    Once completed, pull the profile to personalise everything downstream. This reads only once the person has completed a Thomas behaviour assessment. Completing it is something only they can do. It cannot be triggered on their behalf through the API, and until then it returns 404 ("No scored assessment found for this individual").

    GEThttps://api.thomas.co/v1/assessments/profile/{individualId}
    Request
    GET /assessments/profile/00000000-0000-4000-8000-000000000022
    Response
    {
      "connectionPowerType": "action",
      "traitSpectrums": [
        {
          "dimension": "D",
          "name": "Dominance",
          "leftPole": "Accommodating",
          "rightPole": "Direct",
          "position": 71,
          "band": "High"
        },
        {
          "dimension": "I",
          "name": "Influence",
          "leftPole": "Reflective",
          "rightPole": "Outgoing",
          "position": 68,
          "band": "High"
        },
        {
          "dimension": "S",
          "name": "Steadiness",
          "leftPole": "Spontaneous",
          "rightPole": "Methodical",
          "position": 39,
          "band": "Low"
        },
        {
          "dimension": "C",
          "name": "Compliance",
          "leftPole": "Pragmatist",
          "rightPole": "Perfectionist",
          "position": 43,
          "band": "Low"
        }
      ],
      "selfImageScores": {
        "d": 74,
        "i": 58,
        "s": 36,
        "c": 40
      },
      "discAdvanced": {
        "workMaskScores": {
          "d": 67,
          "i": 63,
          "s": 34,
          "c": 43
        },
        "underPressureScores": {
          "d": 78,
          "i": 52,
          "s": 41,
          "c": 48
        }
      },
      "scoredAt": "2026-10-02T09:30:00.000Z"
    }
  5. 5
    Add them to their team

    Optionally add the joiner to a team you already created, to read the dynamic they are joining. Address it by the Thomas team id you kept from the create. Your own externalTeamReference is not an address, and it comes back on the read. memberIndividualIds is a full replacement set, so send the whole roster including the joiner. Omit ownerIndividualIds to leave the owners unchanged. There is no version to send and no staleness refusal: the last write wins, so serialise your own updates to a team.

    PATCHhttps://api.thomas.co/v1/teams/{teamId}
    Request
    PATCH /teams/00000000-0000-4000-8000-000000000054
    { "memberIndividualIds": ["00000000-0000-4000-8000-000000000021", "00000000-0000-4000-8000-000000000022"] }
    Response
    {
      "id": "00000000-0000-4000-8000-000000000054",
      "externalTeamReference": "team-sales",
      "tenantId": "00000000-0000-4000-8000-000000000001",
      "name": "Sales",
      "teamType": "explicit",
      "sourceManagerIndividualId": null,
      "visibility": "hidden",
      "firstSharedAt": null,
      "needsOwnerAssignment": false,
      "deletedAt": null,
      "inactiveAt": null,
      "createdAt": "2026-07-02T10:00:00.000Z",
      "updatedAt": "2026-07-02T10:00:00.000Z",
      "members": [{
        "id": "00000000-0000-4000-8000-000000000091",
        "teamId": "00000000-0000-4000-8000-000000000054",
        "individualId": "00000000-0000-4000-8000-000000000021",
        "companyIndividualId": "00000000-0000-4000-8000-000000000031",
        "tenantId": "00000000-0000-4000-8000-000000000001",
        "role": "member",
        "status": "active",
        "createdAt": "2026-07-02T10:00:00.000Z",
        "updatedAt": "2026-07-02T10:00:00.000Z"
      }, {
        "id": "00000000-0000-4000-8000-000000000092",
        "teamId": "00000000-0000-4000-8000-000000000054",
        "individualId": "00000000-0000-4000-8000-000000000022",
        "companyIndividualId": "00000000-0000-4000-8000-000000000032",
        "tenantId": "00000000-0000-4000-8000-000000000001",
        "role": "member",
        "status": "active",
        "createdAt": "2026-07-02T10:00:00.000Z",
        "updatedAt": "2026-07-02T10:00:00.000Z"
      }]
    }
  6. 6
    Recommend learning in your LMSyour system

    Map the profile to onboarding paths in your LMS: an Action-led joiner gets short video plus a hands-on exercise, not long written courses.

  7. 7
    Book intros in your calendaryour system

    Schedule week-one 1:1s in your calendar and surface "how to work with your new teammate" in chat.

  8. 8
    React to completion (webhook)
    Coming soon

    Result webhooks (an assessment.completed event) are coming soon. For now, poll the assignment status.

Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

When someone changes role or team, refresh how they fit.

  1. 1
    Resolve the person

    Resolve the person to one Thomas identity: by email, the persistent identity key, or by your org-scoped HRIS id.

    How identity works
    POSThttps://api.thomas.co/v1/individuals/resolve
    Request
    POST /individuals/resolve
    { "email": "alex@acme.com" }
    Response
    {
      "individualId": "00000000-0000-4000-8000-000000000021",
      "created": false
    }
  2. 2
    Enrol as a colleague

    Resolve returns a global identity with no tenant membership, so the profile read requires enrolment. Enrol the person as a colleague: createColleague resolves and enrols by email, as relationshipType employee, before you re-read their profile. The move preserves the same identity. Send your own partnerReference for the person and the sourceVersion of your record with it.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues
    Request
    POST /colleagues
    { "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }
  3. 3
    Re-read the profile

    The profile persists across the move: same identity, by email or HRIS id. This reads only once the person has completed a Thomas behaviour assessment. Completing it is something only they can do. It cannot be triggered on their behalf through the API, and until then it returns 404 ("No scored assessment found for this individual").

    How identity works
    GEThttps://api.thomas.co/v1/assessments/profile/{individualId}
    Request
    GET /assessments/profile/00000000-0000-4000-8000-000000000021
    Response
    {
      "connectionPowerType": "energy",
      "traitSpectrums": [
        {
          "dimension": "D",
          "name": "Dominance",
          "leftPole": "Accommodating",
          "rightPole": "Direct",
          "position": 58,
          "band": "High"
        },
        {
          "dimension": "I",
          "name": "Influence",
          "leftPole": "Reflective",
          "rightPole": "Outgoing",
          "position": 64,
          "band": "High"
        },
        {
          "dimension": "S",
          "name": "Steadiness",
          "leftPole": "Spontaneous",
          "rightPole": "Methodical",
          "position": 41,
          "band": "Low"
        },
        {
          "dimension": "C",
          "name": "Compliance",
          "leftPole": "Pragmatist",
          "rightPole": "Perfectionist",
          "position": 38,
          "band": "Low"
        }
      ],
      "selfImageScores": {
        "d": 62,
        "i": 70,
        "s": 44,
        "c": 38
      },
      "discAdvanced": {
        "workMaskScores": {
          "d": 58,
          "i": 66,
          "s": 49,
          "c": 41
        },
        "underPressureScores": {
          "d": 64,
          "i": 61,
          "s": 40,
          "c": 45
        }
      },
      "scoredAt": "2026-10-02T09:30:00.000Z"
    }
  4. 4
    Read the new team's dynamic

    Check the fit with the team they are joining. Address the team by its Thomas team id; if you only hold your own partner reference for it, list your teams first with listConnectionTeams and read the Thomas id beside it.

    GEThttps://api.thomas.co/v1/teams/{teamId}/dynamics
    Request
    GET /teams/00000000-0000-4000-8000-000000000041/dynamics
    Response
    {
      "teamId": "00000000-0000-4000-8000-000000000041",
      "members": [{
        "individualId": "00000000-0000-4000-8000-000000000021",
        "displayName": "Alex",
        "connectionPower": "action",
        "behaviourScales": {
          "accommodatingDirect": 72,
          "reflectiveOutgoing": 45,
          "spontaneousMethodical": 88,
          "pragmaticPerfectionist": 33
        },
        "assessmentStatus": "assessed"
      }],
      "powerDistribution": { "action": 1, "energy": 0, "stability": 0, "precision": 0 },
      "dominantPower": "action",
      "strengthsAndWatchouts": [{
        "type": "strength",
        "title": "Fast execution",
        "description": "The team moves quickly once aligned.",
        "narrativeContent": null
      }],
      "teamProfileSummary": null
    }
Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

When someone leaves, capture how to work with them for the handover and gauge the team impact.

  1. 1
    Resolve the person

    Resolve the person to one Thomas identity: by email, the persistent identity key, or by your org-scoped HRIS id.

    How identity works
    POSThttps://api.thomas.co/v1/individuals/resolve
    Request
    POST /individuals/resolve
    { "email": "alex@acme.com" }
    Response
    {
      "individualId": "00000000-0000-4000-8000-000000000021",
      "created": false
    }
  2. 2
    Enrol as a colleague

    Resolve returns a global identity with no tenant membership, so the profile read requires enrolment. Enrol the person as a colleague: createColleague resolves and enrols by email, as relationshipType employee, before you read their profile for the handover. Send your own partnerReference for the person and the sourceVersion of your record with it.

    How identity works
    POSThttps://api.thomas.co/v1/colleagues
    Request
    POST /colleagues
    { "email": "alex@acme.com", "partnerReference": "worker-uk-004271", "sourceVersion": 1 }
  3. 3
    Read their profile

    Hand over "how to work with them" to whoever picks up their work. This reads only once the person has completed a Thomas behaviour assessment. Completing it is something only they can do. It cannot be triggered on their behalf through the API, and until then it returns 404 ("No scored assessment found for this individual").

    How identity works
    GEThttps://api.thomas.co/v1/assessments/profile/{individualId}
    Request
    GET /assessments/profile/00000000-0000-4000-8000-000000000021
    Response
    {
      "connectionPowerType": "energy",
      "traitSpectrums": [
        {
          "dimension": "D",
          "name": "Dominance",
          "leftPole": "Accommodating",
          "rightPole": "Direct",
          "position": 58,
          "band": "High"
        },
        {
          "dimension": "I",
          "name": "Influence",
          "leftPole": "Reflective",
          "rightPole": "Outgoing",
          "position": 64,
          "band": "High"
        },
        {
          "dimension": "S",
          "name": "Steadiness",
          "leftPole": "Spontaneous",
          "rightPole": "Methodical",
          "position": 41,
          "band": "Low"
        },
        {
          "dimension": "C",
          "name": "Compliance",
          "leftPole": "Pragmatist",
          "rightPole": "Perfectionist",
          "position": 38,
          "band": "Low"
        }
      ],
      "selfImageScores": {
        "d": 62,
        "i": 70,
        "s": 44,
        "c": 38
      },
      "discAdvanced": {
        "workMaskScores": {
          "d": 58,
          "i": 66,
          "s": 49,
          "c": 41
        },
        "underPressureScores": {
          "d": 64,
          "i": 61,
          "s": 40,
          "c": 45
        }
      },
      "scoredAt": "2026-10-02T09:30:00.000Z"
    }
  4. 4
    Read the team dynamic

    See how the team's balance shifts once they leave. Address the team by its Thomas team id; if you only hold your own partner reference for it, list your teams first with listConnectionTeams and read the Thomas id beside it.

    GEThttps://api.thomas.co/v1/teams/{teamId}/dynamics
    Request
    GET /teams/00000000-0000-4000-8000-000000000041/dynamics
    Response
    {
      "teamId": "00000000-0000-4000-8000-000000000041",
      "members": [{
        "individualId": "00000000-0000-4000-8000-000000000021",
        "displayName": "Alex",
        "connectionPower": "action",
        "behaviourScales": {
          "accommodatingDirect": 72,
          "reflectiveOutgoing": 45,
          "spontaneousMethodical": 88,
          "pragmaticPerfectionist": 33
        },
        "assessmentStatus": "assessed"
      }],
      "powerDistribution": { "action": 1, "energy": 0, "stability": 0, "precision": 0 },
      "dominantPower": "action",
      "strengthsAndWatchouts": [{
        "type": "strength",
        "title": "Fast execution",
        "description": "The team moves quickly once aligned.",
        "narrativeContent": null
      }],
      "teamProfileSummary": null
    }
Build this with your agent
Open with ClaudeOpen with ChatGPT
Open these calls in the API reference

These are starting points, not the full set.