fields, keyed by property key. See How the schema works.
Everywhere an endpoint takes an {object} path segment you may pass the object’s
id or its key (obj_3Ab… or contacts). The display name is not accepted.
Attributes
string
Unique identifier, prefixed
rec_.string
The key of the object this record belongs to, e.g.
contacts.string
The id of that object.
string | null
The record’s display name, derived from the object’s primary property.
object
The record’s values, keyed by each property’s
key. The keys depend on the
object’s schema; a property with no value is null rather than absent, so every
record in a list has identical keys. The shape of each value depends on the
property’s type. See How the schema works.A notes-type field holds a doc_… document
id, not the text. Retrieve the record with ?expand=notes to inline it as
markdown in one call (needs documents:read), or fetch the document by its id.string | null
ISO 8601 creation timestamp.
string | null
ISO 8601 timestamp of the last change.
On create and update, a field key that matches no property doesn’t fail
the request. The record is written and the key is reported back in
ignoredFields. A typo’d key therefore looks just like a successful write, so
check ignoredFields on your first run.Writing select, status and multi-select fields
Send an option’s label, as it appears in Custral, or itsopt_… id. Labels
match case-insensitively. A multi-select takes an array:
option_not_found, and the message names the field and the label (Stage has no option "Comitment".). Nothing is written, and the API never creates an option
for you; add it in Custral first. A single-choice field sent several values fails
the same way. Send null (or []) to clear the field.
Writing the same record twice
POST /v1/records/{object}/upsert takes matchOn (the property that identifies
the record) beside fields. The record whose matchOn property holds that value
is updated, or created when none does, so a sync can send the same record on
every run without listing first:
createFields. They are written when the upsert creates the record
and ignored when it updates one, so a re-run never resets what your team changed:
"action": "created" or "updated"). The
match is exact, on a text, email, phone or website property. When two records
already hold the value, the call fails with match_ambiguous and writes
nothing; update the one you mean by its id.
Querying the list
GET /v1/records/{object} (see List records) is more than a dump. You can
look records up by any property, search, sort, and page two ways.
query
Filter by a property, this is how you fetch by your own
id property, an
email, or any field instead of the rec_ id. filter[email]=jane@acme.com
matches exactly; filter[amount][gte]=100 uses an operator (eq neq gt
lt gte lte contains notContains startsWith endsWith empty
notEmpty). Multiple properties AND together. A <key> is a property key (or
created_at / updated_at). empty / notEmpty ignore their value
(filter[email][empty]=true). A filter is never silently dropped. An unknown property answers
property_invalid, an unsupported operator filter_operator_invalid, and a
malformed or repeated filter parameter filter_invalid; each error’s message
and data.parameter name the parameter at fault, e.g. filter[nosuchproperty].query
Full-text search across the object’s records, relevance-ranked. When set,
filter / sort / cursor are ignored.query
sort=amount:desc, a property key (or created_at / updated_at) and an
optional :asc / :desc (default asc).query
Stable pagination for large sets. Each response carries
nextCursor /
prevCursor; pass one back as cursor=… for the next page. Mutually exclusive
with offset. Pick one paging mode. (Offset paging still works for small sets.)Look up by a property, newest first