Use one record as a person, contact, and candidate
Extended objects let one record exist in multiple object contexts without duplicating
data. This guide explains how primary object and extended objects work,
how object-level extendsTo configuration drives automatic extending, and how
to model a shared person record that is visible as both contact and
candidate.
Before you begin, confirm the object names and record write scopes using the API reference. Use a sandbox record for the examples.
Why extended objects exist
Section titled “Why extended objects exist”In many Caraer setups, the same real-world entity appears in more than one object:
- A person can be a candidate and later also a contact
- A lead can become a candidate without copying
firstname,email, and other shared fields
Without extended objects, you would duplicate records or maintain fragile cross-links. Extended objects solve this by keeping one record (one UUID, one set of property values) while making it visible in multiple object contexts.
The mental model
Section titled “The mental model”Every record has:
| Concept | Property | Meaning |
|---|---|---|
| Primary object | primary_object |
The object the record was created as. This is the record’s home type. |
| Extended objects | extended_objects |
Additional object contexts where the record should also appear. Semicolon-separated object names, e.g. contact;candidate. |
| All objects | Derived | Primary object + extended objects. Used for validation and unique checks. |
Important rules:
- The primary object is never an extended object.
- Extended objects are additional visibility contexts, not separate records.
- An extended record still has one UUID and one value set.
When a record is extended, Caraer makes it available in every object listed in all objects. That is why the same record appears when you list contacts, candidates, or persons — you are querying different object views of the same underlying data.
Example record after extending:
{ "uuid": "RECORD_UUID", "primary_object": "person", "extended_objects": "contact;candidate", "properties": { "firstname": "Alex", "email": "alex@example.com" }}How extending works
Section titled “How extending works”When a record is saved or extended, Caraer:
- Resolves the record’s primary object and extended objects.
- Applies default extended objects from the primary object’s
extendsToconfiguration (see below). - Validates extended-object rules (no duplicate primary, trait limits).
- Persists
primary_objectandextended_objectson the record.
On create and update, extending runs automatically as part of the save flow. You can also extend an existing record explicitly through the API.
Object-level configuration: extendsTo
Section titled “Object-level configuration: extendsTo”Objects can define default extended targets through extendsTo. When
person has extendsTo: [contact, candidate]:
- A record created with primary object
personautomatically getscontactandcandidateas extended objects. - You do not need to call the extend endpoint for every new person record.
extendsTo is schema configuration on the object, not per-record data.
Use it when a primary type should always be visible in other object contexts.
Syncing existing records after schema changes
Section titled “Syncing existing records after schema changes”If you add or change extendsTo on an object, existing records are not
updated automatically. Call:
POST /api/v2/objects/{uuid}/syncExtendedObjects
This re-applies extended configuration for existing records that belong to that object.
Example: person → contact and candidate
Section titled “Example: person → contact and candidate”Scenario
Section titled “Scenario”You want:
personto hold shared identity fields (firstname,lastname,email)candidateto expose recruitment-specific fields and viewscontactto expose CRM-specific fields and views- One underlying record to appear in all relevant lists
Recommended setup
Section titled “Recommended setup”- Create three objects:
person,candidate,contact. - Attach shared properties (
firstname,lastname,email) to all three objects, or at least topersonand the specialized types. - Configure
person.extendsTo = [contact, candidate]. - Create records with primary object
person.
Result for a new person record:
{ "primary_object": "person", "extended_objects": "contact;candidate"}The record is visible in person, contact, and candidate list views.
Querying the same record in different contexts
Section titled “Querying the same record in different contexts”Use mainObject to choose which object view you are listing:
# Same record appears here because it is extended into candidatecurl -X POST "https://v2.api.caraer.com/api/v2/records/index" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "page": 1, "limit": 25, "mainObject": "candidate" }'# Same UUID, different object contextcurl -X POST "https://v2.api.caraer.com/api/v2/records/index" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "page": 1, "limit": 25, "mainObject": "contact" }'# List all persons, including those extended into other typescurl -X POST "https://v2.api.caraer.com/api/v2/records/index" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "page": 1, "limit": 25, "mainObject": "person" }'The property values are identical across contexts. What changes is which object schema, views, and permissions apply for that request.
Alternative: extend per record
Section titled “Alternative: extend per record”If only some people should also be contacts or candidates, skip extendsTo and
extend records individually:
curl -X POST "https://v2.api.caraer.com/api/v2/records/RECORD_UUID/extend" \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "objects": [ { "name": "contact" }, { "name": "candidate" } ] }'The extend endpoint sets extended objects for that record. It does not change the record’s primary object.
Extended objects API reference
Section titled “Extended objects API reference”Extend a record
Section titled “Extend a record”POST /api/v2/records/{uuid}/extend
Request body:
{ "objects": [ { "name": "contact" }, { "name": "candidate" } ]}Use this to:
- Add visibility in additional object contexts
- Remove visibility by sending a smaller object list (objects not in the list are removed from the extended set)
Query params:
recordReturnFormat—LEGACY,USER_FRIENDLY, orEXPANDEDparse— parse formatted values before returning the record
Sync extended objects for an object
Section titled “Sync extended objects for an object”POST /api/v2/objects/{uuid}/syncExtendedObjects
Use after changing an object’s extendsTo configuration so existing records
pick up the new default extended targets.
Validation and constraints
Section titled “Validation and constraints”Caraer enforces these extended-object rules:
- Primary object cannot appear in extended objects. If
personis primary, do not includepersonin the extended list. - At most one Page trait across primary + extended objects.
- At most one User trait across primary + extended objects.
These limits prevent conflicting trait behavior when one record spans multiple object types.
Unique-property checks consider all objects on the record. If email is
unique on both person and contact, duplicate detection spans the combined
object context.
Design guidelines
Section titled “Design guidelines”- Use a base primary object (
person) when multiple specialized types share the same identity data. - Use
extendsTofor defaults that should apply to every new record of that type. - Use the extend endpoint for exceptions and lifecycle changes (for example, promoting one person to candidate only).
- Keep shared property names aligned across extended objects so values stay consistent in every view.
- After schema changes to
extendsTo, runsyncExtendedObjects. - Query with the object context you need via
mainObject; do not assume records only exist under their primary object.
Common pitfalls
Section titled “Common pitfalls”| Pitfall | What happens | Fix |
|---|---|---|
| Expecting extend to change primary object | Primary object stays the same | Create the record under the intended primary type, or accept that primary_object reflects creation context |
Forgetting extendsTo only affects new saves + sync |
Existing records keep old extended set | Call syncExtendedObjects after schema changes |
| Including primary object in extend request | Validation error | Remove the primary object from the extended list |
| Different property sets per object | Record may lack expected fields in one view | Attach required properties to every object context users will open |