Update the person's attributes
Writes the email, name and traits of the person x-distinct-id names, and returns the person.
A retried write with the same clientEventId is applied once.
Guide: Install an SDK
A project publishable key, which ships inside apps. Send the end user's distinct id in x-distinct-id with it.
In: header
Header parameters
The distinct id of the end user the request acts for: the anonymous vh_anon_… id the SDK generated, or the id the app identified them with. At most 255 characters.
percent when x-distinct-id holds the RFC 3986 percent-encoded UTF-8 form of an id that is not printable ASCII; leave it out when the header holds the id as is.
Value in
- "percent"
The project's publishable key, vh_pk_…. A test key, vh_pk_test_…, works with store sandbox and test purchases.
The app's bundle id on iOS or package name on Android, which picks the project's store configuration for purchases and promotional offers.
The end user's most preferred locale, such as en-US.
The app's version, such as 2.4.1.
true when the app sent the request from the background, otherwise false.
Value in
- "true"
- "false"
true when the app is a debug build, otherwise false. Only debug builds can make simulated development purchases.
Value in
- "true"
- "false"
A random id, new for every request, such as a lowercase UUID.
true when the SDK runs in observer mode: another billing integration owns purchases and the SDK only reports them. Otherwise false.
Value in
- "true"
- "false"
The platform the app runs on, such as ios, android or web. Records where a new person signed up.
The device's manufacturer, such as Apple or Google.
The device's model, such as iPhone15,2; in a browser, its navigator.platform.
native in an app, browser in a web page.
Value in
- "native"
- "browser"
The version of the app hosting the SDK; native SDKs send the app's version, the web SDK leaves it out.
The operating system's version, such as 17.4; in a browser, its user agent.
The end user's preferred locales, most preferred first, separated by commas, such as en-US,de-DE.
The SDK making the request: ios, android, react-native or web. A native SDK driven by another reports the outer one, such as react-native.
Value in
- "react-native"
- "web"
- "ios"
- "android"
The version of the SDK making the request, such as 1.4.0.
The app store storefront (country) the end user buys from, when the SDK knows it.
Request body
application/json
TypeScript definitions
Use the request body type in TypeScript.
The person's email address.
The person's name.
Traits to set; the newest write of each trait wins.
Traits to set only when the person has none of that name yet; the earliest write wins, and a traits write still wins over them.
A stable id the SDK gives this write, so a retried or echoed copy of it is applied only once.
Examples
curl -X POST "https://api.voidhash.com/api/v1/sdk/person/traits" \ -H "x-distinct-id: user_8231" \ -H "x-publishable-key: vh_pk_..." \ -H "x-client-bundle-id: com.example.app" \ -H "x-is-backgrounded: true" \ -H "x-is-debug-build: true" \ -H "x-observer-mode: true" \ -H "x-platform: ios" \ -H "x-platform-flavor: native" \ -H "x-sdk: react-native" \ -H "x-sdk-version: 0.0.1-alpha.1" \ -H "Content-Type: application/json" \ -d '{ "traits": { "plan": "pro", "seats": 3 }, "setOnce": { "signupSource": "ios" } }'{ "personId": "person_h7d2k9m4x1c6v8b3n5q0w2e7", "distinctId": "user_8231", "email": "ada@example.com", "name": "Ada Lovelace", "entitlements": { "grants": [ { "perkId": "perk_c4v7b1n9m2x5z8q3w6e0r4t7", "perkSlug": "premium", "source": "subscription", "sourceId": "sub_p9o3i7u1y5t8r2e6w0q4a8s2", "sourcePersonId": "person_h7d2k9m4x1c6v8b3n5q0w2e7", "expiresAt": "2026-04-14T09:26:53.000Z", "status": "active" } ] }, "subscriptions": { "current": { "subscriptionId": "sub_p9o3i7u1y5t8r2e6w0q4a8s2", "productId": "prod_x8k2m5q9v3n7c1z4w6e0r2t5", "status": "active", "expiresAt": "2026-04-14T09:26:53.000Z" }, "history": [ { "subscriptionId": "sub_p9o3i7u1y5t8r2e6w0q4a8s2", "productId": "prod_x8k2m5q9v3n7c1z4w6e0r2t5", "sourcePersonId": "person_h7d2k9m4x1c6v8b3n5q0w2e7", "status": "active", "isTrial": false, "startsAt": "2026-03-14T09:26:53.000Z", "expiresAt": "2026-04-14T09:26:53.000Z", "canceledAt": null } ] }, "purchases": { "history": [ { "purchaseId": "pur_a3s7d1f5g9h2j6k0l4z8x2c6", "productId": "prod_x8k2m5q9v3n7c1z4w6e0r2t5", "providerKey": "2000000123456789", "sourcePersonId": "person_h7d2k9m4x1c6v8b3n5q0w2e7", "type": "subscription", "createdAt": "2026-03-14T09:26:53.000Z" } ] }, "snapshotContext": { "includedPersonIds": [ "person_h7d2k9m4x1c6v8b3n5q0w2e7" ], "migrationJobId": null, "mode": "persisted" }}{ "_tag": "Api/SdkValidationError", "message": "string"}{ "_tag": "Api/AuthenticationError", "cause": "string", "message": "string"}{ "_tag": "Api/SdkPersonNotFoundError", "message": "string"}{ "_tag": "Api/SdkServiceError", "cause": "string"}Response body
application/json
application/json
application/json
application/json
application/json
Errors
This operation adds no error codes of its own. It can fail with the errors every operation shares, such as unauthenticated; see Errors.
More in SDK
- Retrieve the person
GET - Identify a person
POST - Sync a store transaction
POST - Sign a promotional offer
POST - Record a development purchase
POST - Evaluate feature flags
POST - Resolve a paywall
POST - Retrieve the SDK schema
GET - Register a push device
POST - Refresh a push device
POST - Unregister a push device
POST
Something wrong or missing on this page? Tell us by email or on Discord.