Docs

Subjects & identifiers

Create subjects, add identifiers, and merge anonymous → identified.

A person often starts anonymous — a cookie_id before they sign up — and becomes identified later, once they log in with an email. These endpoints let you create that identity up front and reconcile the two once they’re the same person, so the consent they already gave carries over instead of resetting.

They are `sk_` only. In the browser, POST /v1/consent already creates the subject if you pass identifiers.

Identifier types

A subject is reached by an identifier, written in a URL as <prefix>:<value> or in a body as { "type", "value" } (value up to 320 characters).

TypeReferenceTypical useWith a `pk_` key
EMAILemail:ana@example.comA person’s email addressNeeds an identity token
PHONEphone:+34600111222A phone numberNeeds an identity token
EXTERNAL_IDexternal_id:u_9241Your own user id (recommended)Needs an identity token
CUSTOMcustom:…Any other identifier from your systemNeeds an identity token
DEVICE_IDdev_… (or device_id:…)Anonymous install/browser id minted by the CMP and SDKsAnonymous — no token
COOKIE_IDcookie_id:ck_a1b2A first-party cookie for anonymous visitorsAnonymous — no token

With a `pk_` key, only the two anonymous types work on their own. Any identified type (EMAIL, PHONE, EXTERNAL_ID, CUSTOM) must match the X-Tc-Identity token your backend signs for the signed-in user — see Guide: identifying logged-in users. This covers the whole subject: reaching a subject that carries one of those identifiers by its internal id, a linked device id or a merge chain needs the token too. `sk_` keys are never identity-checked.

Lookups are case-insensitive and ignore leading and trailing spaces, for every type (Ana@Example.com and ana@example.com are the same subject). Nothing else is normalized: +34 600 111 222 and +34600111222 are different phones, so send them in one format (E.164 for phones).

Every subject needs an identifier

A subject cannot exist without at least one identifier: POST /v1/subjects and every { "identifiers": [...] } body require one. Records that belong to nobody in particular — a “controller-attested” batch with no person attached — are not supported today. If you know a contact only by a row id in your own system, send it as an external_id.

No lists or segments, by design

A subject holds identifiers, consent state and preference values, and nothing else: there is no list, segment or tag membership on it. Keep audience membership in your own system (your ESP, CRM or database) and use Croombit for the consent state that decides whether you may contact them — model “may we email them about X” as a purpose or a preference.

Create

POST /v1/subjects
{ "identifiers": [{ "type": "EMAIL", "value": "user@example.com" }] }
→ { "subjectId": "cmtr…", "created": true }

Merge (anonymous → identified)

When an anonymous visitor (identified by cookie_id or device_id) signs in, merge their identity into the identified one:

POST /v1/subjects/merge
{ "source": "cookie_id:ck_abc", "target": "email:user@example.com", "reason": "login" }
→ { "sourceSubjectId", "targetSubjectId", "movedRecords", "movedIdentifiers", "outcome" }

The source subject's identifiers and vault rows are re-pointed to the target; the source keeps a mergedIntoId pointer. Later reads by any of its identifiers resolve to the target. subject.merged is emitted only when outcome is "merged" (not for device_relinked).

The same shared-device rule applies when you send a device id together with an account identifier (POST /v1/subjects, or a consent / preference body with identifiers): account identifiers are looked up first, and a device that belongs to another signed-in account never pulls the new account into that subject. With an sk_ key the device moves to the account signing in; with a pk_ key it stays where it is, because a browser can send any device id.