API details
| Property | Value |
|---|---|
| Endpoint | POST /growth-intelligence |
| Protocol | GraphQL over HTTP |
| Authentication | x-api-key header |
| Content-Type | application/json |
| Operation | query GetUsers |
| Response format | JSON |
What you send
Operation query GetUsers($input: GetUsersRequest!). Every field is optional —
send an empty input to get the first page of all users.
| Field | Type | Required | Description |
|---|---|---|---|
filters | [Filter!] | No | Conditions a user must match, all ANDed together. Details → |
segmentId | String | No | Apply a saved segment's filters (by id or system name). Details → |
sort | Sort | No | Order results by one field. Details → |
pagination | Pagination | No | Page number and size. Defaults to page 1, size 10. Details → |
What you get
A GetUsersResponse — the matching users for the requested page:
{
"data": {
"getUsers": {
"user": [ User, … ], // matching users for this page
"message": "success" // optional status note; may be omitted
}
}
}
Each User nests its fields under acquisition,
engagement, and demographics, plus an account list.
The User fields table lists every path, its type, and whether
you can filter or sort on it.
User fields
Every field on User, its type, and whether it can be used as a filters[].key or sort.key. Fields marked No (including all account.* paths) are still returned in the response — they just can't drive filtering or sorting.
Basic
| Path | Label | Filter | Sort | Type | Description | Example |
|---|---|---|---|---|---|---|
id | User ID | Yes | No | String! | Unique user identifier | "usr_abc123" |
name | Name (user) | Yes | Yes | String! | User display name | "Alice Johnson" |
email | Yes | Yes | String! | User email address | "alice@acme.com" |
Account
| Path | Label | Filter | Sort | Type | Description | Example |
|---|---|---|---|---|---|---|
account | Accounts | No | No | [UserAccount!] | CRM accounts linked to the user (empty array or null when none). Matches repeated account rows from the accounts service. | [] |
account.id | Account ID | No | No | String! | Id on each element of account[] (not a separate filter path; use User fields table for filter keys). | "acc_xyz789" |
account.name | Account name | No | No | String! | Display name on each account[] element. | "Acme Corp" |
account.source | Account source | No | No | String! | Source stream on each account[] element. | "ThriveStack" |
account.timestamp | Account timestamp | No | No | String! | Association timestamp on each account[] element. | "2024-01-10T09:00:00Z" |
Status
| Path | Label | Filter | Sort | Type | Description | Example |
|---|---|---|---|---|---|---|
enriched | Enriched | Yes | Yes | Boolean! | Whether enrichment data is available | true |
softDeleted | Soft deleted | Yes | Yes | Boolean! | Whether the user is soft-deleted | false |
source | Source | Yes | Yes | String! | User data source stream | "ThriveStack" |
timeToHardDelete | Time to hard delete | Yes | Yes | Float! | Days remaining until permanent deletion | 25.5 |
tracked | Tracked | Yes | Yes | Boolean! | Whether a creation event was received | true |
Acquisition
| Path | Label | Filter | Sort | Type | Description | Example |
|---|---|---|---|---|---|---|
acquisition.abuseRuleResult | Abuse rule result | No | No | [AbuseRuleResult!]! | Per-rule abuse evaluation (query only) | [{ "passed": true, "ruleId": "r1" }] |
acquisition.abuseStatus | Abuse status | Yes | Yes | String! | Abuse classification | "clean" |
acquisition.initialUtmCampaign | Initial UTM campaign | Yes | Yes | String! | UTM campaign from first touch | "spring-launch" |
acquisition.initialUtmContent | Initial UTM content | Yes | Yes | String! | UTM content from first touch | "hero" |
acquisition.initialUtmMedium | Initial UTM medium | Yes | Yes | String! | UTM medium from first touch | "cpc" |
acquisition.initialUtmSource | Initial UTM source | Yes | Yes | String! | UTM source from first touch | "google" |
acquisition.initialUtmTerm | Initial UTM term | Yes | Yes | String! | UTM term from first touch | "product" |
acquisition.isInvited | Is invited | Yes | Yes | Boolean! | Whether user was invited by another user | true |
acquisition.signedUpOn | Signed up on | Yes | Yes | String! | Signup date (ISO) | "2024-01-10T09:00:00Z" |
acquisition.timeToSignupDays | Time to signup (days) | Yes | Yes | Float! | Days from first touch to signup | 4.5 |
acquisition.touchpointsToSignup | Touchpoints to signup | Yes | Yes | Int! | Marketing touchpoints before signup | 3 |
Engagement
| Path | Label | Filter | Sort | Type | Description | Example |
|---|---|---|---|---|---|---|
engagement.isActive | Is active | Yes | Yes | Boolean! | Active in last 30 days | true |
engagement.lastActiveAt | Last active at | Yes | Yes | String! | Last activity timestamp | "2024-01-22T11:30:00Z" |
engagement.latestSigninTimestamp | Latest sign-in | Yes | Yes | String! | Most recent sign-in timestamp | "2024-01-22T08:45:00Z" |
engagement.totalSignIns | Total sign-ins | Yes | Yes | Int! | Cumulative sign-in count | 47 |
Demographics
| Path | Label | Filter | Sort | Type | Description | Example |
|---|---|---|---|---|---|---|
demographics.avatar | Avatar | Yes | No | String! | Avatar URL | "https://cdn.example.com/a.png" |
demographics.city | City | Yes | Yes | String! | City | "San Francisco" |
demographics.country | Country | Yes | Yes | String! | Country | "United States" |
demographics.countryCode | Country code | Yes | Yes | String! | ISO country code | "US" |
demographics.emailProvider | Email provider | Yes | Yes | String! | Email provider domain | "gmail.com" |
demographics.employmentDomain | Employment domain | Yes | Yes | String! | Employer company domain | "acme.com" |
demographics.employmentName | Employment name | Yes | Yes | String! | Company the user works at | "Acme Corp" |
demographics.employmentRole | Employment role | No | No | String! | Broad job role / function | "product" |
demographics.employmentSeniority | Employment seniority | Yes | Yes | String! | Seniority level | "senior" |
demographics.employmentSubRole | Employment sub-role | Yes | Yes | String! | Granular job sub-role | "pm" |
demographics.employmentTitle | Employment title | Yes | Yes | String! | Job title | "Head of Product" |
demographics.facebookHandle | Facebook handle | Yes | No | String! | Facebook handle | "alice.j" |
demographics.firstName | First name | Yes | Yes | String! | First name (enriched) | "Alice" |
demographics.fullName | Full name | Yes | Yes | String! | Full name from enrichment | "Alice Johnson" |
demographics.githubHandle | GitHub handle | Yes | No | String! | GitHub username | "alice" |
demographics.lastName | Last name | Yes | Yes | String! | Last name (enriched) | "Johnson" |
demographics.linkedinHandle | LinkedIn handle | Yes | No | String! | LinkedIn profile handle | "in/alice" |
demographics.location | Location | No | No | String! | Full location string | "San Francisco, CA" |
demographics.phone | Phone | Yes | No | String! | Phone number(s) | "+1-415-555-0199" |
demographics.state | State | Yes | Yes | String! | State / province | "California" |
demographics.stateCode | State code | Yes | Yes | String! | State code | "CA" |
demographics.timeZone | Time zone | Yes | Yes | String! | IANA time zone | "America/Los_Angeles" |
demographics.twitterHandle | Twitter handle | Yes | No | String! | Twitter/X handle | "@alice" |
Input fields in detail
The shape and rules for each GetUsersRequest field.
filters · optional · array
An array of filter objects. All filters are ANDed: a user must satisfy every filter. Each object has:
{
"key": "",
"operator": "equals | not_equals | greater_than | less_than | greater_than_or_equal | less_than_or_equal",
"value": ""
}
value is always a string and is coerced server-side.
Operators
| Operator | Symbol | Description | Example |
|---|---|---|---|
equals | = | Exact match | key: "acquisition.abuseStatus", value: "clean" |
not_equals | ≠ | Excludes the given value | key: "engagement.isActive", value: "false" |
greater_than | > | Numeric or date comparison (exclusive) | key: "engagement.totalSignIns", value: "10" |
less_than | < | Numeric or date comparison (exclusive) | key: "engagement.totalSignIns", value: "3" |
greater_than_or_equal | ≥ | Numeric or date comparison (inclusive) | key: "engagement.totalSignIns", value: "5" |
less_than_or_equal | ≤ | Numeric or date comparison (inclusive) | key: "engagement.totalSignIns", value: "100" |
segmentId · optional · string
Apply a saved user segment's filters instead of (or together with) inline filters — both are ANDed. Pass the segment's id or system name (list them with GetSegments). Curated segments are not supported and return an error.
{ "segmentId": "active_users" }
sort · optional · object
Sort by exactly one field per request. sort.key must be a path whose Sort column is Yes in the User fields table above.
{
"key": "",
"direction": "asc" | "desc"
}
Tip: Sort by engagement.totalSignIns desc for most active users, acquisition.signedUpOn desc for newest signups, or demographics.employmentSeniority asc by seniority.
pagination · optional · object
Page-based pagination. Defaults to page 1, size 10 if omitted. size max 100.
{
"number": 1,
"size": 25
}
Increment number until the returned user array is empty.
Example request
Active users with more than 5 sign-ins and clean abuse status, ordered by sign-in count descending.
GraphQL query
query GetUsers($input: GetUsersRequest!) {
getUsers(input: $input) {
message
user {
id
name
email
account {
id
name
source
timestamp
}
source
tracked
enriched
softDeleted
timeToHardDelete
acquisition {
signedUpOn
isInvited
abuseStatus
touchpointsToSignup
timeToSignupDays
initialUtmSource
initialUtmMedium
initialUtmCampaign
initialUtmTerm
initialUtmContent
}
engagement {
isActive
lastActiveAt
totalSignIns
latestSigninTimestamp
}
demographics {
firstName
lastName
employmentTitle
employmentRole
employmentSeniority
city
country
timeZone
}
}
}
}
Variables (JSON)
{
"input": {
"filters": [
{ "key": "engagement.isActive", "operator": "equals", "value": "true" },
{ "key": "engagement.totalSignIns", "operator": "greater_than", "value": "5" },
{ "key": "acquisition.abuseStatus", "operator": "equals", "value": "clean" }
],
"sort": { "key": "engagement.totalSignIns", "direction": "desc" },
"pagination": { "number": 1, "size": 25 }
}
}
cURL
curl --location 'https://api.app.thrivestack.ai/growth-intelligence' \\
--header 'content-type: application/json' \\
--header 'x-api-key: YOUR_API_KEY' \\
--data '{
"query": "query GetUsers($input: GetUsersRequest!) { getUsers(input: $input) { message user { id name email } } }",
"variables": {
"input": {
"filters": [
{ "key": "engagement.isActive", "operator": "equals", "value": "true" },
{ "key": "engagement.totalSignIns", "operator": "greater_than", "value": "5" },
{ "key": "acquisition.abuseStatus", "operator": "equals", "value": "clean" }
],
"sort": { "key": "engagement.totalSignIns", "direction": "desc" },
"pagination": { "number": 1, "size": 25 }
}
}
}'
Example response
Matching users are under data.getUsers.user (array). message may be "success" or omitted depending on the server.
{
"data": {
"getUsers": {
"message": "success",
"user": [
{
"id": "usr_abc123",
"name": "Alice Johnson",
"email": "alice@acme.com",
"account": [
{ "id": "acc_xyz789", "name": "Acme Corp", "source": "ThriveStack", "timestamp": "2024-01-10T09:00:00Z" }
],
"source": "ThriveStack",
"tracked": true,
"enriched": true,
"softDeleted": false,
"timeToHardDelete": 0,
"acquisition": {
"signedUpOn": "2024-01-10T09:00:00Z",
"isInvited": true,
"abuseStatus": "clean",
"touchpointsToSignup": 3,
"timeToSignupDays": 4.5,
"initialUtmSource": "google",
"initialUtmMedium": "cpc",
"initialUtmCampaign": "spring-launch"
},
"engagement": {
"isActive": true,
"lastActiveAt": "2024-01-22T11:30:00Z",
"totalSignIns": 47,
"latestSigninTimestamp": "2024-01-22T08:45:00Z"
},
"demographics": {
"firstName": "Alice",
"lastName": "Johnson",
"employmentTitle": "Head of Product",
"employmentRole": "product",
"employmentSeniority": "senior",
"city": "San Francisco",
"country": "United States",
"timeZone": "America/Los_Angeles"
}
}
]
}
}
}
Tips and best practices
- Filter values are always strings — use
"true"/"false"for booleans and numeric strings for numbers; the server coerces them. - All filters are ANDed. Use separate filter objects for each condition (e.g. active and 10+ sign-ins).
- Account on the user record: select
account { id name source timestamp }(a list ofUserAccount).account.*can't be filtered or sorted — narrow withemailordemographics.employmentDomaininstead, then refine client-side. - Organic power users: e.g.
acquisition.isInvitedequals"false"with highengagement.totalSignIns. - Paginate by incrementing
pagination.numberuntiluseris empty.