The same guest looks different depending on which API you are talking to. v1 treats a profile as a flat set of fields. v3 treats it as a nested record where contact details are collections. This is the difference that most often surprises people porting code from one API to the other, or writing profiles from two systems at once.
If you have not chosen an API yet, start with Which Bookboost API should I use?
The v1 shape
In v1, a profile is a flat JSON object. You send the fields at the top level, and that is the whole request body. One email address, one phone number, no wrapper.
{
"first_name": "Maria",
"last_name": "Lindqvist",
"email": "maria@example.com",
"phone": "+46701234567",
"language_code": "sv"
}
The v3 shape
In v3, the same guest is nested. Everything sits inside a top-level data object, and contact details become arrays.
{
"data": {
"_identifier": { "type": "profile" },
"_source": { "entity_id": "crm-88213" },
"first_name": "Maria",
"last_name": "Lindqvist",
"language_code": "sv",
"emails": [
{
"_identifier": { "type": "email" },
"is_primary": true,
"address": "maria@example.com"
}
],
"phones": [
{
"_identifier": { "type": "phone" },
"is_primary": true,
"raw": "+46701234567",
"cca2": "SE"
}
]
}
}
Three things to remember about v3
Everything is wrapped in
data. Forget the wrapper and you get a400, not a validation error telling you which field was missing. If a request is rejected and the message is unhelpful, check the wrapper first.Contact details are collections, not fields. A guest can have many email addresses and many phone numbers, and exactly one of each may be marked
is_primary._source.entity_idis your ID, not ours. It is how you refer to a record from your own system, and it is what makes re-sending the same record an update rather than a duplicate. Choose it once, from a stable field in your own database, and never change it.
Create or ingest
v3 gives you two ways to write a profile, and they behave differently on the second attempt.
POST /cdp/public/v3/profiles
POST /cdp/public/v3/profiles/ingest
The create endpoint is strict. Send the same _source.entity_id twice and the second call fails with a 409. Use it for interactive flows, where a person is waiting and you need to know the outcome immediately.
The ingest endpoint is the upsert. It accepts the same ID repeatedly, returns a 202 straight away, processes in the background, and will not overwrite newer data with older data. Use it for syncing, where volume matters more than an immediate answer.
Because ingest returns before the work is done, a 202 means accepted rather than applied. If your sync needs to confirm the result, read the profile back rather than treating the 202 as proof.
The same guest has the same ID in both APIs
A profile's uuid in v1 and its _identifier.id in v3 are the same value, and the same is true for lists. You can create a guest with v1 and read them back with v3, or the other way round, with no mapping table in between.
This is what makes a split integration practical. A website form can write through v1 so that consent is recorded, and your own app can read the same guest through v3 for the richer data.
How each write path deduplicates
There are three write paths across the two APIs, and all three apply a different rule. This is worth knowing before you build, because the failure shows up as duplicate guests months later rather than as an error at the time.
POST /api/v1/users guards on email and phone
POST /api/v1/users/store-in-list no deduplication at all
POST /cdp/public/v3/profiles keys on _source.entity_id
The v1 users endpoint refuses to create a guest whose email address or phone number already exists, returning a
409, unless you passforce_create.The v1 store-in-list endpoint does not deduplicate at all.
The v3 write endpoints deduplicate on your
_source.entity_idand ignore email entirely.
So a guest who already exists can be blocked, duplicated, or updated, depending only on which path wrote them.
Deciding which system owns creation
If more than one of your systems writes profiles, pick one to own creation and let the others only update existing records. That single decision removes most duplicate problems before they start.
A common arrangement is for the system that first meets the guest, usually the PMS or the website form, to own creation, with everything else matching on the ID it produced.
Getting help
Open Help at the bottom of the left menu and choose Talk to Us, or email support@bookboost.io. If you think duplicates have already been created, tell us roughly when and from which system, and we can look at what happened before you plan a clean-up.