When to call
Call identify at the following moments in your application:
-
After registrationWhen a new user completes sign-up for the first time.
-
After loginEach time a returning user successfully authenticates.
-
When profile changesWhenever a user updates their email, name, or other profile attributes.
Use your internal database ID (UUID v4 recommended) as user_id. Database IDs never change, making them more reliable than emails or usernames.
Endpoint
POST /api/identify
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 identify objects.
| Field | Type | Required | Description |
|---|---|---|---|
user_id | string | Yes | Unique identifier for the user. Use a stable database ID (UUID recommended) — never use email as the ID. |
traits | object | No | Free-form dictionary of descriptive attributes about the user. See Reserved traits below. |
context.group_id | string | No | The account or organization this user belongs to. Required to link user activity to account-level signals. |
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 of when the event occurred. Defaults to the time the server receives the request. |
Reserved traits
ThriveStack recognises the following trait names and gives them special handling across the platform. Use these exact names when sending standard user attributes.
| Trait | Type | Description |
|---|---|---|
user_email | string | User's email address. |
user_name | string | User's full display name. |
first_name | string | First name. |
last_name | string | Last name. |
phone | string | Phone number. |
avatar | string | URL of the user's avatar image. |
title | string | Job title or professional role. |
username | string | Username or handle. |
created_at | string | ISO 8601 date when the user account was created. |
plan | string | Current subscription plan (e.g., "free", "pro"). |
You can also include any custom traits alongside the reserved ones.
Example
cURL
curl --location 'https://api.app.thrivestack.ai/api/identify' \
--header 'accept: */*' \
--header 'content-type: application/json' \
--header 'x-api-key: YOUR_API_KEY' \
--data '[
{
"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": {
"user_email": "john.doe@acme.com",
"user_name": "John Doe"
},
"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([
{
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: {
user_email: "john.doe@acme.com",
user_name: "John Doe"
},
timestamp: "2024-01-15T10:30:00Z"
}
]);
fetch("https://api.app.thrivestack.ai/api/identify", {
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/identify"
payload = json.dumps([
{
"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": {
"user_email": "john.doe@acme.com",
"user_name": "John Doe"
},
"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/identify"
method := "POST"
payload := strings.NewReader(`[
{
"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": {
"user_email": "john.doe@acme.com",
"user_name": "John Doe"
},
"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 |