This guide explains how to create, update, and manage records through the /api/v2/records endpoints.
Records are the actual data instances for your objects (for example candidates, vacancies, companies). These endpoints gives you a complete lifecycle: create/update, search, relation handling, archive/restore, and morphing.
- Bearer token or API Token with permissions for the relevant object scopes
- Object internal name (used as
{objectName}in routes)
Core write and lifecycle endpoints:
POST /api/v2/records/{objectName}create a recordPUT /api/v2/records/{objectName}/{uuid}update a recordPOST /api/v2/records/{objectName}/createOrUpdateupsert by unique fieldsDELETE /api/v2/records/{uuid}archive, anonymize, or hard deletePOST /api/v2/records/{uuid}/restorerestore archived recordsPOST /api/v2/records/{uuid}/morphmove/add visibility across objects
List/search and projection endpoints:
POST /api/v2/records/indexPOST /api/v2/records/searchGET /api/v2/records/{uuid}GET /api/v2/records/{uuid}/previews/{name}
Relation endpoints:
POST /api/v2/records/relations/{fromUuid}/{relationName}/{toUuid}DELETE /api/v2/records/relations/{fromUuid}/{relationName}/{toUuid}
curl -X POST "https://your-company.caraer.com/api/v2/records/candidate?parse=true" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"properties": {
"firstname": "Jane",
"lastname": "Doe",
"email": "jane.doe@example.com"
}
}'What happens:
- The controller builds a
Recordfrom your DTO and object name. - Property-level validation is applied in record services.
- You get a
CreateResponsewith the created record. - When setting parse to true the values will be parsed to labels and human readable values. Leave parse out if you want the internal values when working on an integration for example. Parse is by default false.
curl -X PUT "https://your-company.caraer.com/api/v2/records/candidate/RECORD_UUID?parse=true" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"properties": {
"application_status": "in_progress"
}
}'The endpoint returns UpdateResponse<Record>.
If your integration receives repeated events, createOrUpdate is usually the safest write pattern:
curl -X POST "https://your-company.caraer.com/api/v2/records/candidate/createOrUpdate" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"values": {
"email": "jane.doe@example.com",
"firstname": "Jane",
"lastname": "Doe"
}
}'If a matching unique record exists, Caraer updates it; otherwise it creates a new one. The matching is based on all the unique properties of a record.
When you submit values, the backend validates by property type and uses property format for parsing/formatting behavior. The key is always the property name of the property, and the value shape must match the type.
Common type rules from backend validation:
string: send a stringnumber: send a numeric value (for example120000or"120000")date: send unix timestamp milliseconds in UTC (preferred). ISO date/date-time inputs are also accepted by date parsing and normalizedcheckbox: sendtrue/false(or string"true"/"false")single-select: send one option name exactly as configuredmulti-select: send option names as one semicolon-separated string or as an array of the namestag: send tags as one semicolon-separated string or as an array of the namesrange: send"min;max"(both numeric)structure: send a valid JSON string (not a raw nested JSON object)file: send a file key string (needs to be under your company files path)linked-property: do not send a value (backend rejects manual values since they're read only and managed on the related records)
Example payload covering multiple types:
{
"properties": {
"firstname": "Jane Doe",
"salary": 4200,
"start_date": 1742851200000, // "2025-04-24T00:00:00.000Z"
"available_remote": true,
"application_status": "in_progress",
"skills": "java;spring;neo4j", // ['java', 'spring', 'neo4j']
"labels": "high_priority;backend", // ['high_priority', 'backend']
"salary_range": "3500;5000",
"profile_data": "{\"github\":\"janedoe\",\"years_experience\":6}",
"resume_file": "COMPANY_UUID/files/candidates/jane-doe-cv.pdf"
}
}- Type controls what value shape is valid.
- Format controls how that value is interpreted or displayed.
Example: a string property may accept multiple string input styles, but the normalized stored value is still validated as a string type value.
- Confirm property
namekeys match the object schema exactly. - Confirm select values exist in property options.
- For
multi-select,tag, andrange, use semicolon delimiters or arrays with names of the options. - For
structure, serialize to a JSON string first. - Do not send values for
linked-propertyfields.
Use /index for generic pagination, with optional archived=true and parse=true.
Note on archived records:
The archived query parameter determines whether to fetch archived (soft-deleted) records or not. By default, only active (non-archived) records are returned (archived=false).
- Set
archived=trueto list only records that have been archived. - Omitting this parameter (or setting
archived=false) will fetch only currently active records.
Example:
To fetch only archived records, use:
curl -X GET "https://your-company.caraer.com/api/v2/records/OBJECT_NAME/index?archived=true" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Use these endpoints to link two existing records with a named relation. The route is:
POST /api/v2/records/relations/{fromUuid}/{relationName}/{toUuid}DELETE /api/v2/records/relations/{fromUuid}/{relationName}/{toUuid}
Example: link candidate FROM_UUID to vacancy TO_UUID using relation applied_for.
curl -X POST "https://your-company.caraer.com/api/v2/records/relations/FROM_UUID/applied_for/TO_UUID?primary=true" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"What happens on create:
- Backend loads both records and the relation by
relationName. - If one of them does not exist, the request fails with a not-found error.
- The relation direction is resolved against relation schema (
from/to) in backend logic, so use the relation name exactly as defined. - The relation is merged (created if missing, updated if already present).
This removes the relation edge between those record UUIDs for that relation name.
curl -X DELETE "https://your-company.caraer.com/api/v2/records/relations/FROM_UUID/applied_for/TO_UUID" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"What happens on delete:
- It checks relation delete access on both involved objects.
- It deletes matching relation edge(s) for that relation between the two UUIDs.
primary is a query parameter on relation creation:
primary=false(default): creates/updates the relation without primary selection behavior.primary=true: marks this specific relation edge as the primary one.
Important primary behavior:
- The created relation gets
primary = true. - For the same relation type and same target record (
toUuid), other incoming relation edges are set toprimary = false. - Practical result: you can enforce a single "main" linked record for a target record within that relation type.
Example:
curl -X POST "https://your-company.caraer.com/api/v2/records/relations/CANDIDATE_UUID/applied_for/VACANCY_UUID?primary=true" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"- Relation
primaryis metadata on a relation edge between two records. primary_objectis metadata on the record node itself and represents that record's main object context.
They solve different problems and are managed separately.
Delete modes:
archive(default): soft delete sets the deletedAt property on the recordanonymize: removes most data values from the record, preserving only its structure in the database (useful for keeping references to submissions or campaign aggregates while ensuring privacy)delete: Permanently removes the record from the database (hard delete). Once deleted, the record cannot be recovered.
curl -X DELETE "https://your-company.caraer.com/api/v2/records/RECORD_UUID?mode=archive" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Restore:
Note: The restore endpoint (
POST /api/v2/records/{uuid}/restore) can only fully restore records that were archived (soft deleted).While you can technically call restore for records deleted with
anonymize, most data values will not be recovered because they were irreversibly anonymized. Only records deleted with thearchive(soft delete) mode can be restored with their original data intact.
curl -X POST "https://your-company.caraer.com/api/v2/records/RECORD_UUID/restore" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Morphing lets one record be visible in other object contexts without duplicating data. See Object morph explained for primary object vs morph objects, extendsTo, and the person → contact/candidate pattern.
curl -X POST "https://your-company.caraer.com/api/v2/records/RECORD_UUID/morph" \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"objects": [
{ "name": "candidate" },
{ "name": "lead" }
]
}'create,update, andcreateOrUpdateall supportparse=truequery parsing for returned values./searchcan return either full records or previews depending on request payload.
Important: Build integrations around validated property names and object names from your environment, not hardcoded assumptions from examples.