Skip to content
Authors
  • Sem Tadema
    Sem TademaCTO

This guide explains how to fetch records from Caraer and how to build filter payloads that behave exactly as expected.

It is written for external developers integrating with the Records API.

  • Which endpoint to use for record fetching
  • What mainObject should be
  • How filter payloads are structured
  • How show works (including *)
  • How sort works
  • How AND and OR logic works in filter groups
  • How to filter on related records
  • How to combine filters with search, sorting, and pagination

Primary endpoint for fetching records

Use:

  • POST /api/v2/records/index

With optional query params:

  • parse=true|false
  • archived=true|false
  • relatedRecordUuid=RECORD_UUID

Basic example:

curl -X POST "https://your-company.caraer.com/api/v2/records/index?parse=true" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "page": 1,
    "limit": 25,
    "mainObject": "candidate"
  }'

mainObject: what to use and why it matters

mainObject is required context for record fetching.

Use the object name of the record type you want to list, for example:

  • candidate
  • vacancy
  • contact

Example:

{
  "mainObject": "candidate",
  "page": 1,
  "limit": 25
}

Why it matters:

  • It defines which node type is the base of the query.
  • It controls how filter, show, and sort are resolved.
  • It is also used for permission checks on property access.

If mainObject does not match a valid object name in your environment, request building and/or query execution will fail.

Filter payload structure

Filtering is sent inside the request body as filter.

High-level shape:

{
  "page": 1,
  "limit": 25,
  "mainObject": "candidate",
  "query": null,
  "filter": {
    "groups": [
      {
        "items": [
          {
            "object": null,
            "relation": null,
            "property": "status_candidate",
            "operator": "equals_exactly",
            "value": "new"
          }
        ]
      }
    ]
  }
}

Meaning of each filter item field

  • object: object context used by the filter item
  • relation: relation name if the filter traverses a relation (* is allowed for any relation in relation-existence checks)
  • property: property name to filter on; if omitted, item becomes a relation existence check
  • operator: filter operator name
  • value: value used by the operator
  • relationIncluded (optional, default true): for relation existence checks (property=null), controls include/exclude relation matches

How object + relation are interpreted in filters

Important behavior:

  • If you are filtering properties on the mainObject, you usually leave object as null.
  • If you set object to the same value as mainObject, the filter is treated as filtering through a related node of that same object (self-related records), not as a plain main-object property shortcut.
  • If object is set and relation is null, filtering targets related records of that object type regardless of relation name.
  • If object is set and relation is also set, filtering targets related records through that specific relation only.

Practical recommendation: for normal mainObject property filtering, keep object and relation as null in filter items.

Show items (show) and how * works

show controls which fields are returned for each record.

Basic shape:

{
  "show": [
    { "object": "candidate", "relation": null, "property": "firstname" },
    { "object": "candidate", "relation": null, "property": "email" }
  ]
}

Using * in show

You can request all readable properties of the main object with:

{
  "show": [{ "object": "candidate", "relation": null, "property": "*" }]
}

Important behavior:

  • * expansion is applied for the main object wildcard pattern.
  • Backend resolves it to all readable properties for that main object.
  • Access rules still apply: only properties the caller can read are returned.

If you need a stable API response contract, explicitly list the properties instead of relying on *.

Sort items (sort)

sort controls ordering of result rows.

Basic shape:

{
  "sort": [
    {
      "object": "candidate",
      "relation": null,
      "property": "updatedAt",
      "direction": "DESC"
    }
  ]
}

Rules:

  • direction supports ASC and DESC.
  • You can provide multiple sort items to create secondary/tertiary ordering.
  • Use properties that exist and are readable in the requested context.

Example with fallback ordering:

{
  "sort": [
    {
      "object": "candidate",
      "relation": null,
      "property": "status_candidate",
      "direction": "ASC"
    },
    {
      "object": "candidate",
      "relation": null,
      "property": "updatedAt",
      "direction": "DESC"
    }
  ]
}

How filter logic works (AND / OR)

Filter logic is:

  • Items inside one group are combined with AND
  • Groups inside one filter are combined with OR

So groups let you create (A AND B) OR (C AND D) patterns.

Example with two groups:

{
  "filter": {
    "groups": [
      {
        "items": [
          {
            "property": "status_candidate",
            "operator": "equals_exactly",
            "value": "new"
          },
          {
            "property": "source",
            "operator": "equals_exactly",
            "value": "linkedin"
          }
        ]
      },
      {
        "items": [
          {
            "property": "status_candidate",
            "operator": "equals_exactly",
            "value": "in_progress"
          },
          {
            "property": "source",
            "operator": "equals_exactly",
            "value": "referral"
          }
        ]
      }
    ]
  }
}

This means:

  • (status=new AND source=linkedin) OR (status=in_progress AND source=referral)

All operators

OperatorTypical meaning
equals_exactlyExact equality
equals_flexiblyCase-insensitive equality
not_equals_exactlyExact inequality
not_equals_flexiblyCase-insensitive inequality
bigger_thanGreater than
smaller_thanLess than
bigger_than_or_equal_toGreater than or equal
smaller_than_or_equal_toLess than or equal
contains_exactlyContains (case-sensitive)
contains_flexiblyContains (case-insensitive)
not_contains_exactlyDoes not contain (case-sensitive)
not_contains_flexiblyDoes not contain (case-insensitive)
starts_with_exactlyStarts with (case-sensitive)
starts_with_flexiblyStarts with (case-insensitive)
ends_with_exactlyEnds with (case-sensitive)
ends_with_flexiblyEnds with (case-insensitive)
is_nullEmpty / null
is_not_nullNot empty / not null
anyAny of provided values
all_flexiblyContains all provided values
all_exactlyMatches all provided values exactly
distance_betweenDistance-based comparison
rangeIn between min/max

Which operators are usable per property type/format

In default Caraer property configuration, operator availability is primarily driven by property type, and formats of that type follow the same list.

Property typeFormatsSupported operators
stringemail, multi-line, phone, single-line, urlis_null, is_not_null, equals_exactly, not_equals_exactly, contains_flexibly, not_contains_flexibly, starts_with_flexibly, ends_with_flexibly
numbernumber, currencyis_null, is_not_null, equals_exactly, not_equals_exactly, bigger_than, smaller_than, bigger_than_or_equal_to, smaller_than_or_equal_to, range
datedateis_null, is_not_null, equals_exactly, not_equals_exactly, bigger_than, smaller_than, bigger_than_or_equal_to, smaller_than_or_equal_to, range
checkboxsingle-checkboxis_null, is_not_null, equals_exactly, not_equals_exactly
single-selectsingle-selectany, is_null, is_not_null
multi-selectmulti-selectany, all_flexibly, all_exactly, is_null, is_not_null
tagtagis_null, is_not_null, equals_exactly, not_equals_exactly, any, all_flexibly, all_exactly
rangenumber-range, currency-rangerange
structurestructurecontains_flexibly, is_null, is_not_null
filefileis_null, is_not_null
linked-propertylinked-propertyBased on the property it's linked to

Relation-only filtering note

When property is null, filter items behave as relation existence checks (relationIncluded=true/false). In that case, property-type operator tables do not apply because the filter is relation-based, not property-value-based.

Relation filters

You can filter by relation presence even without specifying a property.

Relation existence example (candidate has any relation to vacancy):

{
  "filter": {
    "groups": [
      {
        "items": [
          {
            "object": "vacancy",
            "relation": "*",
            "property": null,
            "relationIncluded": true
          }
        ]
      }
    ]
  }
}

Relation non-existence example (candidate has no applied_for relation to vacancy):

{
  "filter": {
    "groups": [
      {
        "items": [
          {
            "object": "vacancy",
            "relation": "applied_for",
            "property": null,
            "relationIncluded": false
          }
        ]
      }
    ]
  }
}

relatedRecordUuid shortcut (relation-aware filtering)

When you pass relatedRecordUuid as a query parameter, Caraer can help shape relation-aware filtering:

  • If no filter is provided, it defaults to records related to that record
  • If filter values contain smarten placeholders, they are resolved against that related record context

Example:

curl -X POST "https://your-company.caraer.com/api/v2/records/index?relatedRecordUuid=RELATED_UUID" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "page": 1,
    "limit": 20,
    "mainObject": "candidate",
    "filter": { "groups": [] }
  }'

Combining filter + search + sort + pagination

You can combine all of these in one request:

{
  "page": 1,
  "limit": 20,
  "mainObject": "candidate",
  "query": "jane",
  "filter": {
    "groups": [
      {
        "items": [
          {
            "property": "status_candidate",
            "operator": "equals_exactly",
            "value": "in_progress"
          }
        ]
      }
    ]
  },
  "sort": [
    {
      "object": "candidate",
      "relation": null,
      "property": "updatedAt",
      "direction": "DESC"
    }
  ]
}

Practical checklist

  • Always set mainObject to the object you are fetching.
  • Keep mainObject aligned with the object name used in show/sort where possible.
  • Use exact property names from your Caraer schema.
  • Start with one simple group, then add complexity.
  • Validate operator spelling (equals_exactly, not EQUALS).
  • Use explicit show fields for stable integrations; use * mainly for exploration.
  • Use parse=true only if you need human-readable values in response.