HBDI® Profile Search API - Integration Reference
This document describes the HBDI® Profile Search API endpoint and the fields your integration will receive, based on the access level provisioned for your organization. Share it freely with the developers on your team who are building against this endpoint.
Overview
The Profile Search endpoint looks up HBDI® profile results for people in your organization by email address, up to 100 addresses per call. Results are organized around the four thinking-preference quadrants defined by the Whole Brain® Thinking model: A (Analytical), B (Practical), C (Relational), and D (Experimental).
One request returns a profile for every address you have permission to see. The fields below reflect exactly what your organization's access level returns — scores plus the interpretive fields (descriptors, work elements, energy level). It does not include account-level data such as sharing settings or internal identifiers.
POST
{API_BASE_URL}/api/v1/hbdi_profiles/search
Authentication
Authentication is a two-step exchange: get a token, then use it on every search request. Your Herrmann integration contact will provide your client ID, client key, and the two base URLs referenced below.
Step 1 — Request a token
Call the token endpoint with your client ID and key as headers. The response body is the token itself, returned as plain text — not JSON — so read the raw body and trim any surrounding whitespace.
curl -X POST "{AUTH_BASE_URL}/api/v1/get-token" \
-H "X-Client-Id: $CLIENT_ID" \
-H "X-Key: $CLIENT_KEY"
# Response (text/plain):
# eyJhbGciOiJIUzI1NiJ9.eyJvcmcuLi5.9Xk2QO5r0tW8m1c...
Step 2 — Use the token as a bearer credential
Every search request carries the token in the Authorization header. The header must begin with Bearer followed by a single space.
Authorization: Bearer <token>
Reuse your token
A token is valid until it expires. Reuse it across requests rather than fetching a new one each time. When it expires, the search endpoint returns a 401 response - fetch a new token and retry once.
Making a Request
Headers

Body Parameters

Example Request
curl -X POST "{API_BASE_URL}/api/v1/hbdi_profiles/search" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Accept: application/json" \
-d '{
"emails": ["jamie@clientcompany.com", "taylor@clientcompany.com"],
"all_assessments": false
}'
The Response
A successful call always returns 200 with three keys. Every address you requested is returned as an object[] in the data or not_found key.

Profile Fields
Each object in the data key contains the following fields.

Example Response
{
"success": true,
"data": [
{
"email": "jamie@clientcompany.com",
"preference_codes": { "A": 2, "B": 2, "C": 1, "D": 1 },
"profile_scores": { "A": 63, "B": 55, "C": 67, "D": 69 },
"stress_scores": { "A": 63, "B": 63, "C": 63, "D": 63 },
"intro_extro_score": null,
"energy_level": "3",
"descriptors": {
"A": ["factual", "critical"],
"B": ["reader", "conservative"],
"C": ["reader", "symbolic", "emotional", "spiritual"],
"D": ["synthesizer"]
},
"work_elements": {
"A": {
"analytical": "3",
"problem solving": "5",
"financial aspects": "5"
},
"B": {
"planning": "3",
"organization": "3",
"administrative": "2"
},
"C": {
"writing": "2",
"expressing ideas": "2",
"teaching/training": "1"
},
"D": {
"innovating": "2",
"integration": "4",
"creative aspects": "4"
}
},
"key_descriptor": "critical"
}
],
"not_found": ["taylor@clientcompany.com"]
}
Current Behavior Notes
Two fields don't yet behave as described in the field descriptions above. Both are on our roadmap; in the meantime, build against the behavior described here so your integration isn't surprised when we implement them.
work_elements returns scored data, not a filtered list
The intended shape of work_elements is a quadrant-keyed list of just the work elements the person prefers (rated 4 or higher), with no scores attached — for example:
"work_elements": {
"A": ["problem solving", "financial aspects"],
"B": [],
"C": [],
"D": ["integration", "creative aspects"]
}
Today, it returns the full scored object shown in the example response above—every element with its rating —rather than just the preferred ones. If you only want the person's top elements, filter for a rating of 4 or higher yourself (casting the rating to a number first, since it arrives as a string). Expect this field to change from an object to an array of strings once this is corrected — we'll give advance notice before that ships.
intro_extro_score is currently always null
This field returns null for every profile today. Treat it as unavailable rather than as “not measured for this person,” and don't build logic that depends on it having a value until we let you know it's been corrected.
What You Can and Cannot See
Only your organization's profiles
The endpoint only returns profiles belonging to people in your organization. You will never see profiles belonging to another Herrmann client.
Completed assessments only
Someone who started an HBDI® assessment but never finished it will not match — they come back in not_found, the same as an address with no assessment on file at all.
How to read not_found
not_found means only “no result was available to your integration” — it's not confirmation that a person has never completed an assessment, or that the address doesn't exist anywhere in Herrmann's systems. Design your integration around that distinction rather than treating a miss as proof of anything beyond “nothing to show you here.”
Errors
Failed requests return the same four keys, so a single handler can cover all of them.
{
"success": false,
"code": "BATCH_LIMIT_EXCEEDED",
"message": "Cannot request more than 100 emails per search",
"errors": []
}

Both authentication failures return a 401 status, so branch on code rather than status — only one of the two is worth retrying.
Integration Best Practices
-
Match results by email, not position. Data is not ordered and is not aligned with your input array. Key your lookup on the lowercased address you sent. With all_assessments: true, expect more than one entry for the same address.
-
Batch large lists, then merge. For lists over 100 addresses, split into chunks of 100, then concatenate every data array and every not_found array you get back. Chunking never changes any individual person's result.
-
Expect nulls on optional fields. Only email is guaranteed. Every other field can be null or absent depending on how much of the assessment the person completed — treat missing narrative data as normal, not as an error.
-
Cast numeric strings before you compute. preference_codes, profile_scores and stress_scores are numbers, but energy_level and the ratings inside work_elements are strings. Cast before you sort, total, or chart them — otherwise "5" sorts below "10" and totals silently concatenate instead of adding.
-
Parse defensively. Descriptors and work_elements come from stored assessment data whose inner shape has varied across assessment versions over time. The quadrant keys (A–D) are stable; what sits under them is not guaranteed, so code that walks these fields should tolerate missing quadrants or unexpected value types.
Need Additional Help? Submit a Ticket