When to call
Call group whenever a user is associated with an account:
-
Account creationWhen a new workspace or organization is created for the first time.
-
User added to accountWhen an existing user joins an account via invitation or SSO.
-
Account profile changesWhen account attributes such as name, plan, or domain are updated.
Two IDs drive the Group call: user_id (who is performing the action) and group_id (which account they belong to). A single user can belong to multiple groups — each association is a separate Group call with the same user_id and a different group_id.
Endpoint
POST /api/group
Authentication
Pass your API key in the x-api-key header on every request. You can find your key in the ThriveStack dashboard under Settings → API Keys.
accept: */*
content-type: application/json
x-api-key: YOUR_API_KEY
Request body
Send an array containing one or more group objects.
| Field | Type | Required | Description |
|---|---|---|---|
group_id | string | Yes | Unique identifier for the account/group (UUID recommended). Stable across the account's lifetime. |
user_id | string | Yes | The user being associated with this account. |
traits | object | No | Free-form dictionary of account attributes. See Reserved traits below. |
context.device_id | string | No | Unique identifier for the user's device. |
context.session_id | string | No | Current session identifier. |
context.source | string | No | "product" for in-app events, "marketing" for public-facing website events. |
timestamp | string | No | ISO 8601 timestamp. Defaults to server time if omitted. |
Reserved traits
ThriveStack recognises the following account trait names and gives them special handling in dashboards, segmentation, and alerts. Use these exact names when sending standard account attributes.
| Trait | Type | Description |
|---|---|---|
account_name | string | Display name of the account or organization. |
account_domain | string | Primary domain of the account (e.g., "acme.com"). |
group_type | string | Type of group (e.g., "Account", "Team"). |
industry | string | Industry classification of the account. |
employees | number | Number of employees in the organization. |
plan | string | Current subscription plan (e.g., "free", "enterprise"). |
website | string | Account's website URL. |
phone | string | Account contact number. |
created_at | string | ISO 8601 date when the account was created. |
description | string | Short description or biography of the account. |
avatar | string | URL of the account's logo or avatar image. |
You can also include any custom traits alongside the reserved ones. Trait names are case-insensitive — use a consistent casing in your codebase.
Example
cURL
curl --location 'https://api.app.thrivestack.ai/api/group' \
--header 'accept: */*' \
--header 'content-type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data '[
{
"group_id": "ac8db7ba-5139-4911-ba6e-523fd9c4704b",
"user_id": "18f716ac-37a4-464f-adb7-3cc30032308c",
"context": {
"device_id": "7d08298f",
"session_id": "session_e2iukz3vduo",
"group_id": "ac8db7ba-5139-4911-ba6e-523fd9c4704b",
"source": "product"
},
"traits": {
"group_type": "Account",
"account_domain": "acme.com",
"account_name": "Acme Corporation",
"industry": "Technology",
"plan": "enterprise"
},
"timestamp": "2024-01-15T10:30:00Z"
}
]'
JavaScript
const myHeaders = new Headers();
myHeaders.append("accept", "*/*");
myHeaders.append("content-type", "application/json");
myHeaders.append("x-api-key", "YOUR_API_KEY");
const raw = JSON.stringify([
{
group_id: "ac8db7ba-5139-4911-ba6e-523fd9c4704b",
user_id: "18f716ac-37a4-464f-adb7-3cc30032308c",
context: {
device_id: "7d08298f",
session_id: "session_e2iukz3vduo",
group_id: "ac8db7ba-5139-4911-ba6e-523fd9c4704b",
source: "product"
},
traits: {
group_type: "Account",
account_domain: "acme.com",
account_name: "Acme Corporation",
industry: "Technology",
plan: "enterprise"
},
timestamp: "2024-01-15T10:30:00Z"
}
]);
fetch("https://api.app.thrivestack.ai/api/group", {
method: "POST",
headers: myHeaders,
body: raw,
redirect: "follow"
})
.then((response) => response.text())
.then((result) => console.log(result))
.catch((error) => console.error(error));
Python
import requests
import json
url = "https://api.app.thrivestack.ai/api/group"
payload = json.dumps([
{
"group_id": "ac8db7ba-5139-4911-ba6e-523fd9c4704b",
"user_id": "18f716ac-37a4-464f-adb7-3cc30032308c",
"context": {
"device_id": "7d08298f",
"session_id": "session_e2iukz3vduo",
"group_id": "ac8db7ba-5139-4911-ba6e-523fd9c4704b",
"source": "product"
},
"traits": {
"group_type": "Account",
"account_domain": "acme.com",
"account_name": "Acme Corporation",
"industry": "Technology",
"plan": "enterprise"
},
"timestamp": "2024-01-15T10:30:00Z"
}
])
headers = {
'accept': '*/*',
'content-type': 'application/json',
'x-api-key': 'YOUR_API_KEY'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)
Go
package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.app.thrivestack.ai/api/group"
method := "POST"
payload := strings.NewReader(`[
{
"group_id": "ac8db7ba-5139-4911-ba6e-523fd9c4704b",
"user_id": "18f716ac-37a4-464f-adb7-3cc30032308c",
"context": {
"device_id": "7d08298f",
"session_id": "session_e2iukz3vduo",
"group_id": "ac8db7ba-5139-4911-ba6e-523fd9c4704b",
"source": "product"
},
"traits": {
"group_type": "Account",
"account_domain": "acme.com",
"account_name": "Acme Corporation",
"industry": "Technology",
"plan": "enterprise"
},
"timestamp": "2024-01-15T10:30:00Z"
}
]`)
client := &http.Client{}
req, err := http.NewRequest(method, url, payload)
if err != nil {
fmt.Println(err)
return
}
req.Header.Add("accept", "*/*")
req.Header.Add("content-type", "application/json")
req.Header.Add("x-api-key", "YOUR_API_KEY")
res, err := client.Do(req)
if err != nil {
fmt.Println(err)
return
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(body))
}
Response
200 OK
{
"success": true,
"eventId": "evt_abc123xyz"
}
Error codes
| Status | Meaning |
|---|---|
400 | Bad Request — invalid JSON or missing required fields |
401 | Unauthorized — invalid or missing API key |
429 | Rate Limit Exceeded |
500 | Server Error — try again later |