Upsert a user profile's traits and value score
Writes traits (and optional valueScore) to identified_users — the same table the ingest identify path uses. The response is only returned after the D1 upsert completes (at-least-once, never fire-and-forget).
projectId MUST NOT be supplied in the body — it is always derived from the authenticated API key. Traits are validated by the same sanitizeTraits pipeline as identify (≤ 8 KiB, ≤ 2 nesting levels, dangerous keys rejected).
Auth: X-API-Key with scope profile:write (x-required-scope: profile:write).
Rate limit: 60 req/min per (projectId, IP) — over → 429.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”Profile upsert body. projectId MUST NOT be included — it is derived from the API key.
object
Non-empty user/profile ID.
Trait map (same sanitizeTraits rules as identify — ≤ 8 KiB, ≤ 2 levels, dangerous keys rejected).
object
Optional value score.
Example
{ "userId": "uid_456", "traits": { "plan": "pro", "region": "apac" }, "valueScore": 87.5}Responses
Section titled “Responses”Profile upserted.
object
Invalid JSON or invalid payload (bad userId/traits/valueScore, or traits sanitizer rejection).
400 payload-validation error for the profile endpoint.
object
Human-readable reason (present for invalid_payload).
Examples
{ "error": "invalid_json"}{ "error": "invalid_payload", "detail": "userId must be a non-empty string"}Missing or unresolvable API key.
object
Human-readable error message.
Seconds to wait before retrying (present on 429/503).
Examples
{ "error": "missing_api_key"}{ "error": "unauthorized"}API key lacks the profile:write scope.
object
Human-readable error message.
Seconds to wait before retrying (present on 429/503).
Example
{ "error": "insufficient_scope"}Cross-project key (tenant isolation).
object
Human-readable error message.
Seconds to wait before retrying (present on 429/503).
Example
{ "error": "not_found"}Rate limit exceeded (60/min per project+IP).
object
Human-readable error message.
Seconds to wait before retrying (present on 429/503).
Example
{ "error": "rate_limited"}