Requests and records
Read, filter, write and search records with the envelope, the sixteen operators and the lifecycle every object shares.
An object is a table you declare: a name, a label expression, and a list of attributes. A record is one row of that object. Every object gets the same REST surface under /records/:object, the same envelope, the same filters and the same lifecycle, so what you learn on one object applies to all of them.
Objects
An attribute has one of sixteen types. Each is declared with a builder from @standards/client; see Data model / Attributes for options and inferred types.
| Type | Holds |
|---|---|
text | A short string. |
richtext | Formatted text. Not filterable, except for emptiness. |
number | A number, optionally with a unit and precision. |
checkbox | true or false. |
date | A calendar date or a timestamp. |
phone | A phone number, written as { countryCode, phoneNumber }, filtered as digits. |
currency | An amount and a currency code. |
status | One value from a declared list, with a colour. |
select | One value from a declared list. |
multiselect | Several values from a declared list. |
location | A postal address; filter on city, postalCode or country. |
user | A workspace user. |
relation | A link to records of another object, single or .many(). |
formula | A value computed from the record. |
rollup | A value aggregated across a relation. |
document | A pack of files attached to the record. |
Relations link records across objects, and a bilateral relation keeps both sides in step. Documents hold files under a record. Views describe how the app lists and opens records. Data model / Relations and documents and Interfaces / Views cover them.
Records
Every record carries these fields beside its attribute values:
| Field | Meaning |
|---|---|
id | UUID of the record. |
objectName | Name of the owning object. |
label | Display name computed from the object's label expression. |
values | Attribute values, keyed by attribute name. The SDK flattens values onto the record. |
visibility | workspace or private. |
ownedBy | The owning actor of a private record, null otherwise. |
viewerAccess | What you may do with the record: owner, editor, reader or workspace. |
appearance | The record's own icon or image, never both. image is { fileId, source }: a public file id, and where it came from. Absent when it has neither: the record then shows its object's. See Appearance. |
metadata | Free-form object you can write, for UI state or your own flags. |
createdAt, updatedAt | Timestamps. |
createdBy, lastUpdatedBy | Actor ids. |
deletedAt, deletedBy | Set while the record sits in the trash. |
version | Monotonic version of the record. It moves on every write, including computed attributes recalculated in the background, so the higher of two copies of a record is the newer. |
schemaVersion | Legacy schema version stamp; nothing migrates records by it any more. |
These names are reserved. An attribute cannot be called id, label, values, metadata or appearance.
Reading
Three routes read a collection. All three answer the same envelope. A fourth, POST /records/:object/aggregate, measures the same records, optionally grouped (see Aggregating).
| Route | Use it for |
|---|---|
GET /records/:object | Quick reads with limit, offset, countMode and deleted=true in the query string. |
POST /records/:object/list | Filters, sorts, a visibility scope, fields and a timezone in the body. |
POST /records/:object/search | Full-text search. q is required; the body takes every list option on top. |
curl https://api.standards.new/v1/records/contacts/list \
-H "Authorization: Bearer $STANDARDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "limit": 20, "sorts": [{ "attribute": "lastName", "direction": "asc" }] }'{
"data": [{ "id": "…", "objectName": "contacts", "label": "Ada Lovelace", "values": { "lastName": "Lovelace" } }],
"page": { "hasMore": true, "total": null, "countMode": "none", "nextOffset": 20 }
}GET /records/:object/:id reads one record. Add ?includeDeleted=true to read one from the trash.
Pagination and counting
limitdefaults to20. Anything above1000is clamped to1000. Onlistandsearchit must be an integer of0or more:-1,1.5,"20"ornullis a400SCHEMA_VALIDATION_FAILEDnaminglimit.limit: 0returns no rows. With acountMode, it is the cheapest way to count.offsetdefaults to0. It must stay below2147483647 - limit, or the request is a400.countModeisnone(default),estimatedorexact. Withnone,totalisnulland the database skips the count. Sendexactonly when a number is displayed.hasMoretells you whether a next page exists. Followpage.nextOffsetwhen present rather than computing it fromdata.length: search can return an empty page that still advances the cursor.
Aggregating
POST /records/:object/aggregate measures the records a typed query selects, in one request: a count, or the sum, average, minimum or maximum of one attribute, optionally grouped. Reads are the same as list: object read permission and the viewer's visibility. Only records you can read are measured. The body is strict: a search term (q) or any unknown key is a 400.
curl https://api.standards.new/v1/records/deals/aggregate \
-H "Authorization: Bearer $STANDARDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "query": { "source": { "object": "deals" } }, "measure": "sum", "attribute": "amount", "groupBy": "stage" }'{
"groups": [{ "key": "open", "value": 4200 }, { "key": "won", "value": 9000 }, { "key": null, "value": 300 }],
"total": 13500,
"truncated": false,
"currency": "EUR"
}| Field | Notes |
|---|---|
query | Required. The records measured, as query(...).toDefinition() builds it. Its sorts and limit are ignored. |
measure | Required. count, sum, avg, min or max. |
attribute | The number or currency attribute measured. Required unless measure is count. |
groupBy | A status, select, single user or single relation attribute by name, or { "attribute": "createdAt", "bucket": "month" } to cut a date attribute into day, week, month, quarter or year buckets; a date attribute named alone is cut into months. |
groupBy2 | A second grouping, for stacked or funnel charts. Answers key2 on each group. |
compare | { "attribute", "period" }: restricts the measure to the current period of a date attribute so far, up to now, and answers in previous the same stretch of the period before: on the 10th, the 1st to the 10th of last month, ending at that period's own end when it is shorter. 7d and 30d are the last 7 or 30 days against the ones before. period is a bucket, 7d or 30d. |
userFilters | The reader's own filters, ANDed with the query's. |
timezone | IANA zone of the buckets and comparison windows. Defaults to UTC. |
hostRecordId | The record a relation query reads from, on a record page. |
keyis the stored option value, the id of the user or related record, or the bucket start.nullgroups the records with no value, and a value outside the attribute's options is its own group.avg,minandmaxanswernullfor a group with no values. Summing amounts in several currencies is refused withAGGREGATE_MIXED_CURRENCY.currencyis the code the amounts were measured in, which is the records' own currency and not the attribute's default: records all in USD on an attribute that defaults to EUR answer"USD". It is present only forsum,avg,minormaxover acurrencyattribute that met at least one record, in the current or the previous period.- A multi-valued attribute cannot be grouped on, and a related record you cannot read counts in the
nullgroup. - At most
100groups. A field grouping comes largest first; when more groups exist,truncatedistrue.totalstill measures every matching record, the cut groups included. - A date grouping comes oldest bucket first and is never cut: when it would answer more than
100groups (counted with the second grouping and thenullgroup), the next coarser bucket that fits answers (day, thenweek,month,quarter,year), andappliedBucketnames the one used (appliedBucket2forgroupBy2). A bucket with no record is absent, not0.
From the SDK, the same call is the aggregate() terminal of a records query (Records).
Filtering
A filter is one combinator, and or or, over a flat list of rules. There is no nesting.
{
"combinator": "and",
"rules": [
{ "attribute": "status", "operator": "any_of", "value": ["lead", "active"] },
{ "attribute": "createdAt", "operator": "greater_than", "value": "2026-01-01" }
]
}Sixteen operators exist. Which ones apply depends on the attribute type.
| Operator | Types | Meaning |
|---|---|---|
is | text, phone, number, currency, checkbox, status, select, date, user, relation, formula | Equals. On a checkbox, value is a boolean. |
is_not | text, phone, number, currency, status, select, date, user, relation, formula | Does not equal. |
contains | text, phone, formula | Case-insensitive substring. |
not_contains | text, phone, formula | Substring absent. |
starts_with | text, phone | Case-insensitive prefix. |
ends_with | text | Case-insensitive suffix. |
greater_than | number, currency, date, formula | Strictly after or above. For dates, see Dates below. |
greater_or_equal | number, currency, date, formula | On or after, at or above. |
less_than | number, currency, date, formula | Strictly before or below. |
less_or_equal | number, currency, date, formula | On or before, at or below. |
any_of | status, select, multiselect, user, relation | At least one stored value is in the list. |
none_of | status, select, multiselect, user, relation | No stored value is in the list. |
is_empty | every type but checkbox and rollup | No value. value is null. |
is_not_empty | every type but checkbox and rollup | Has a value. value is null. |
is_within | date | Relative range: { "amount": 7, "unit": "days", "direction": "past" }. |
on_day_month | date | Anniversary, any year: { "day": 14, "month": 7 }. |
System fields filter too: id, label, createdAt, updatedAt, createdBy, lastUpdatedBy and the others take the operators of their kind.
Dates. A date attribute without a time compares by UTC calendar day: greater_than "2026-09-18" starts on the 19th, and a value with a time counts as the day it is written on (2026-09-18T23:30:00-05:00 is the 18th). An attribute declared with .includeTime() compares instants, and the value sets the granularity. A bare day YYYY-MM-DD stands for that whole UTC day: is "2026-09-18" matches any time that day and greater_than starts at 2026-09-19T00:00:00Z. A value with a time is one exact instant, so is matches that instant only. A value without an offset is read as UTC. createdAt, updatedAt and deletedAt do not follow this rule yet; filter them with a bare day, which every operator reads as a UTC calendar day. For an absolute comparison (is, is_not, less_than, less_or_equal, greater_than, or greater_or_equal), the comparand must name a real calendar day: 2026-09-18, optionally followed by a time — 2026-09-18T14:30, 2026-09-18T14:30:00Z, 2026-09-18T14:30:00+02:00, 2026-09-18 14:30:00. Seconds and their fraction are optional, and the offset may be written Z, ±HH or ±HH:MM up to 14 hours. This also applies to date-valued relation or document properties and date-returning formulas. Anything else — text that is not an ISO date ("Sept 18 2026", "22-09-18"), an impossible day ("2026-02-30"), or a number — is a validation error: /records/<object>/list and /search both answer 400, and the filter never runs. Relative is_within and on_day_month rules keep their object values.
Aliases. On input, the API also accepts eq, neq, lt, gt, lte, gte, in, notIn, not_in, and the camelCase forms (startsWith, isEmpty, doesNotContain …). They are normalized to the canonical name above before validation and never stored. The former before, after, on_or_before, on_or_after, is_checked, is_not_checked and day_month_eq are rejected with a message naming the replacement.
Dynamic values. Three values resolve at query time:
{ "attribute": "owner", "operator": "is", "value": { "dynamic": "actor", "ref": "current" } }
{ "attribute": "dueDate", "operator": "is", "value": { "dynamic": "date", "anchor": "today" } }
{ "attribute": "updatedAt", "operator": "less_than", "value": { "dynamic": "date", "anchor": "now" } }The actor token (@me) matches the caller, on a user attribute as on createdBy. In an agent turn it also matches the person the agent is working for, so "my deals" asked of an agent means the asker's deals. A scheduled run's @me also names the routine's creator when a person created it; a trigger-fired run has no asker, and its @me is the agent alone. @me names the same people the read is scoped to, so it only narrows: records the caller cannot see stay hidden. today resolves to a calendar day in the request's timezone, which defaults to UTC; send timezone in the body or an x-timezone header (Europe/Paris) so today means the caller's today. now is the current UTC instant, so is with now on an attribute with a time matches almost nothing; bound it with greater_or_equal and less_than, or use is_within. today is a bare day, whose bounds are UTC. When @me cannot resolve, the rule is dropped, not rejected.
Relation and document properties. When a relation carries qualified properties, add property to filter on the property and quantifier (any or none) to say how many linked records must match:
{ "attribute": "members", "property": "role", "operator": "is", "value": "admin", "quantifier": "any" }A location filters the same way, on property: "city", "postalCode" or "country" (an ISO3 code), with the text operators and no quantifier.
Sorting
sorts is an ordered list of { attribute, direction }, asc or desc. Sorts apply in the order given.
Reference hydration
By default every relation, document and user attribute comes back as the linked records. fields narrows that:
- omit
fields: every reference attribute is hydrated; "fields": ["company"]: only those attributes are hydrated;"fields": []: nothing is hydrated and the keys are absent.
Skipping hydration is the cheapest read for exports.
Writing
Create
POST /records/:object takes the values under data and options beside them:
curl https://api.standards.new/v1/records/contacts \
-H "Authorization: Bearer $STANDARDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "data": { "firstName": "Ada", "lastName": "Lovelace" }, "options": { "visibility": "private" } }'options.allowDraftskips required-attribute validation. The record is created and nothing marks it incomplete.options.visibilitysetsworkspace(default) orprivate. Avisibilitybesidedatais refused with400, so a record meant to be private is never created workspace-visible.options.linkTo({ objectName, recordId, attribute, base }) creates the record and links it to an existing one in one transaction.baseis the value of the owner'sattributeas you last read it (an id, a list of ids for a many relation,nullwhen empty): if the owner's value changed since, the create answers409SCHEMA_CONFLICTand nothing is created. Withoutbase, the link is written over whatever the owner holds. Any other key inlinkTois refused with400.
The answer is 201 with the record.
Update
PUT /records/:object/:id is a partial update: send only the attributes that change. The body is the values object itself, with no data wrapper. An empty body is a 400.
curl -X PUT https://api.standards.new/v1/records/contacts/$RECORD_ID \
-H "Authorization: Bearer $STANDARDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "email": "[email protected]" }'PUT writes unconditionally: the last write wins. An If-Match header is refused with 400 SCHEMA_VALIDATION_FAILED. To write against what you read, send the change as an operation, where a step's base holds, per attribute, the value you based the change on. For a scalar it is a precondition: if the stored value moved since, that attribute is not written and the step answers conflict with the current value (null, "" and an absent value count as the same empty). For a relation or a document it is the base of a three-way merge, so members others added or removed meanwhile are kept. Without base, the last write wins. Every operation returns the inverse computed by the server, so a client can undo it. Sending null, "" or [] clears an attribute, required or not: required() gates creation, not later writes.
Delete, restore, purge
| Action | Route | Permission | Effect |
|---|---|---|---|
| Delete | DELETE /records/:object/:id | delete | Moves the record to the trash. 204. |
| Restore | POST /records/:object/:id/restore | delete | Brings it back. |
| Purge | POST /records/:object/:id/purge | manage | Removes it for good. 409 unless it is already in the trash. |
GET /records/:object?deleted=true lists the trash.
Bulk
| Route | Body | Max items | Result |
|---|---|---|---|
POST /records/:object/bulk-create | { "records": [ … ], "options"? } | 100 | { created, errors: [{ index, error }] } |
POST /records/:object/bulk-update | { "updates": [{ "id", "data" }] } | 100 | { updated, errors: [{ id, error, code }] } |
POST /records/:object/bulk-delete | { "ids": [ … ] } | 100 | { deleted, errors: [{ id, error, code }] } |
A batch above its cap is refused whole with 400 SCHEMA_VALIDATION_FAILED naming the array; nothing is written. Where the API's rate limiter is on, the bulk routes also allow 10 calls a minute per client IP, so the effective ceiling is 1,000 items a minute: an 11th call inside the minute answers 429 with a Retry-After header in seconds — wait that long, then retry. Each item is validated before the batch runs. A failed item lands in errors while its siblings proceed; the batch is not transactional, so a bulk update that reports one error has still written the others.
Bulk create makes workspace records only. A visibility, in options or beside records, is refused with 400 rather than ignored; create private records one by one with options.visibility.
A bulk update or bulk delete error always carries a code: SCHEMA_FORBIDDEN for a record you can read but not change (one shared with you), SCHEMA_RECORD_NOT_FOUND for a missing or hidden id, SCHEMA_SYSTEM_ENTITY_IMMUTABLE for a record the platform owns, such as a memory page, SCHEMA_REPOSITORY_DELETE_FAILED for a delete the database rejected.
Operations
POST /operations writes several records in one request and answers each step with the record as it now stands, plus the operation that undoes it.
curl https://api.standards.new/v1/operations \
-H "Authorization: Bearer $STANDARDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"opId": "5b1f0c9e-3a4d-4e8b-9c2f-7d6e5a4b3c21",
"steps": [
{ "kind": "record.set", "objectId": "'$CONTACTS_ID'", "recordId": "'$RECORD_ID'",
"values": { "email": "[email protected]" }, "base": { "email": "[email protected]" } }
]
}'The body holds an opId (a UUID you generate) and steps: 1 to 100, at most one per record. objectId is the object's UUID, the id that GET /schema/objects/:name returns. Each step is one of five kinds:
kind | Fields | Effect |
|---|---|---|
record.create | objectId, recordId, values, options? | Creates the record under the recordId you chose. options takes visibility, allowDraft and linkTo, as on create. |
record.set | objectId, recordId, values, base? | A partial update. base holds, per attribute, the value you based the change on, checked as under update. |
record.delete | objectId, recordId | Moves the record to the trash. |
record.restore | objectId, recordId | Brings it back from the trash. |
record.patch | objectId, recordId, attribute, edit | A partial edit of one rich text attribute (any other attribute answers SCHEMA_VALIDATION_FAILED). edit is { "command": "replace", "oldText", "newText", "replaceAll"? } or { "command": "insert", "text", "after"? }: oldText (and after) must occur exactly once in the current value unless replaceAll is set; without after, text is appended. oldText, text and after are never empty. The edit applies to the value as stored when the write lands, even if it changed since you read it, and the result is checked as a record.set of that value would be. An edit that changes nothing answers applied and writes nothing. |
Each step is checked against your permissions on its object, as the matching REST route checks it, and answers on its own: one refused step does not undo the others. The answer is 201 with { opId, steps, inverse }, steps in the order you sent them:
status | Fields | Meaning |
|---|---|---|
applied | record | The step was written. |
conflict | record, attrs: [{ attr, current, by }] | A record.set whose base no longer held, or a record.patch whose anchor is no longer in the value or now occurs more than once, for the attributes listed: those were not written, the others were. current is the stored value, by the record's last writer (not necessarily who changed that attribute). |
error | recordId, code, message? | The step was refused with an error code. Only SCHEMA_VALIDATION_FAILED carries a message; any other reason travels as the code alone. A step on an unknown object, or on one closed to API writes, answers SCHEMA_OBJECT_NOT_FOUND. |
inverse is an operation that undoes what applied, ready to send back to POST /operations: a create is undone by a delete, a delete by a restore, a restore by a delete, a set by a set of the previous values whose base is what this operation wrote, so undoing never overwrites a later change, and a patch by a set of the previous value on the same terms. It is null when nothing was written.
A malformed body, more than 100 steps or two steps on one record is refused whole with 400 SCHEMA_VALIDATION_FAILED, and nothing is written.
Attribution of agent writes
A defined agent writes as its own actor: the audit trail records actorType: "agent" with the human principal as rootActorId, and the activity feed shows the agent's face beside the person it acted for, read as “Lily for Karl”. A routine or an API key acting for someone reads the same way. A general chat without an agent definition writes as the person who asked, so that person stays the author in the activity feed, shown as “Karl via General chat”.
Every audit entry written during either kind of agent turn carries metadata.agentSessionId, identifying the conversation it was written in. On those rows, for both single and batch writes, the name of the agent, the routine or the general chat links to the conversation or the routine it was written in, when you can open it. A manual edit outside an agent turn carries no such key, names only its author and links nowhere. This metadata belongs to the audit entry, not the record's writable metadata field.
Visibility
A record is workspace or private. A private record is returned only to its owner and to the members it is shared with (Record access); anyone else gets the same 404 as for a missing id. List and search accept a visibility scope to read one side only; a list also accepts shared, the private records other members shared with you. Hydration and totals never leak private records.
A relation write keeps the members you cannot see: the stored membership is the value you sent plus the private records hidden from you, and the response never names them. A single relation whose target is hidden from you keeps it when you clear the field, and gives way to the value when you choose one.
PATCH /records/:object/:id/visibility with { "visibility": "private" } flips a record. Only the creator can make a workspace record private, and only the owner can open it back up. ownedBy is set by the server and cannot be assigned. A memory page takes the visibility of what it is about, so the route refuses it with 403 SCHEMA_SYSTEM_ENTITY_IMMUTABLE; change the visibility of the record or agent the page belongs to instead.
Appearance
A record can carry its own icon or its own image, never both: setting one removes the other. Each route needs update permission on the object and write access to the record. A reader the record is shared with gets 403 SCHEMA_FORBIDDEN; anyone who cannot see it gets the same 404 as for a missing id. Each answers the record as written, with its new appearance; reference attributes are not hydrated in that answer.
| Route | Body | Effect |
|---|---|---|
PUT /records/:object/:id/appearance | { "icon": "rocket" } | Sets the icon. Any other key, a file id included, is a 400. |
PUT /records/:object/:id/image | multipart, one file | Uploads the image as a public file of your workspace and sets it. |
PUT /records/:object/:id/image/logo | { "domain": "acme.com" } | Sets a company's logo as the image. |
DELETE /records/:object/:id/appearance | none | Removes the icon or the image. On an object declaring .image({ from }), its image is then looked up again from its data. |
curl -X PUT https://api.standards.new/v1/records/companies/$RECORD_ID/image \
-H "Authorization: Bearer $STANDARDS_API_KEY" \
-F "[email protected]"The image is a PNG, JPEG, GIF, WebP or AVIF of at most 5 MiB, checked against its bytes: an SVG, or a file whose bytes are not the type it is declared as, is a 400 SCHEMA_VALIDATION_FAILED, and a larger file a 413. The record keeps its previous appearance. The image you replace or remove is deleted. A deployment without public file links answers 503 CONFIGURATION_REQUIRED.
The image is a public file: {publicFilesUrl}/{fileId} opens it without signing in. {publicFilesUrl}/{fileId}?variant=mark serves it resized to a 128 px square, the size to use for an avatar or a list mark.
appearance.image.source says where the image came from. Every image you set through these routes, a file or a logo picked with the route below, is upload. website, name and email mark an image Standards found on its own from that attribute of an object declaring .image({ from }) (see Record images). A found image never replaces an icon or an image someone set, and it follows the record's data.
Logo as image
PUT /records/:object/:id/image/logo sets a company's logo as the record's image. domain is a website or a bare domain: https://www.Acme.com/about reads as acme.com. The logo counts as chosen (source: "upload"), and like an upload it removes the icon and replaces the previous image.
curl -X PUT https://api.standards.new/v1/records/companies/$RECORD_ID/image/logo \
-H "Authorization: Bearer $STANDARDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "domain": "acme.com" }'- A value that is not a domain, a domain with no known logo, or a deployment without a logo service (
logosConfigured: false) answers400SCHEMA_VALIDATION_FAILED, and the record keeps its appearance. So does a logo the service is still preparing, or a workspace that has looked up too many new domains today: try again later. - When the logo service is unavailable the route answers
503LOGO_PROVIDER_UNAVAILABLE. - A logo is stored once per workspace: two records set to the same domain share one file, and removing it from one record leaves it on the other.
Logo search
GET /logos/:object/search?query= lists at most five company logos matching query, to set as the image of a record of :object. It needs update permission on the object; without it the route answers 403 SCHEMA_FORBIDDEN.
curl "https://api.standards.new/v1/logos/companies/search?query=acme" \
-H "Authorization: Bearer $STANDARDS_API_KEY"[{ "name": "Acme", "domain": "acme.com", "previewUrl": "https://img.logo.dev/acme.com?…" }]queryis lowercased, trimmed and its spaces collapsed before it is read. Under two characters the answer is[]; over 120 it is a400SCHEMA_VALIDATION_FAILED.previewUrlis the provider's image, for display while choosing; it is never stored. Keepdomain.- The same query is answered from cache for 24 hours, whichever workspace asked it first.
- One member may search 30 times a minute; the next search answers
429LOGO_SEARCH_RATE_LIMITED. Keep the results you have and try again a minute later. - When the logo service is unavailable the route answers
503LOGO_PROVIDER_UNAVAILABLE: try again later. - When
GET /tenant/settingsanswerslogosConfigured: false, every query answers[].
Search
A query matches a record's label and its text, richtext, phone, select, multiselect and status values. Rich text matches on its words: a mention matches the name it shows, never its link. A hidden or archived attribute is never searched.
One object. POST /records/:object/search runs a full-text query with every list option on top. q is required; an empty string means "filters only". Totals are lower bounds (totalIsExact: false) because permission checks run after ranking.
curl https://api.standards.new/v1/records/contacts/search \
-H "Authorization: Bearer $STANDARDS_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "q": "lovelace", "limit": 10 }'The whole workspace. GET /search?q= looks across every object you can read and returns hits, not records.
| Query param | Effect |
|---|---|
q | The text to search. Required. |
objects | Comma-separated object names to restrict to. |
grouped=true | One group per object instead of a flat list. |
deleted | archived for the trash only, all for both. Default: live records. |
countMode=none | Skip the exact count. Any other value keeps it. |
limit, offset | Flat mode only; default 20. |
A hit carries recordId, objectId, objectName, objectLabel, label, createdAt, updatedAt, and deletedAt when the record is in the trash. Flat responses are { results, total, totalIsExact, hasMore, nextOffset? }; grouped ones replace results with groups: [{ objectName, objectLabel, results, count }]: one group per object with a match you can see, the most matches first, each with its five best hits in results and every match it holds in count; total adds the counts up. Fetch the record by id when you need its values.
Next steps
- Records — the typed query builder over the same routes.
- CLI / Records — the same reads and writes from a terminal.
- HTTP API / Errors — the envelope every failed request shares.
- Records / Files and documents — what a
documentattribute holds. - Book a demo