Skip to content
helmstudio
Contents

records

GET /records/{collection}

Query a collection with the closed filter language. Never SQL.

06 §6 and Q19. Each `where` is `field:op:value`, at most eight, all of which must match. - field: a top-level document key matching `^[A-Za-z_][A-Za-z0-9_]*$`, or one of `id`, `created_at`, `updated_at`. No nested paths. Any field may be filtered; `storage.collections[].index` only makes it faster. - op: `eq ne lt lte gt gte in contains exists`. - value: everything after the second `:`, so a value may itself contain `:` (a timestamp, a URL); a field and an operator never do. Parsed as JSON when it is valid JSON (`42`, `true`, `null`, `"42"`), otherwise taken as a literal string. `in` takes a JSON array. `contains` is a case-sensitive substring test when the stored value is a string and a membership test when it is an array. `exists` takes `true` or `false`. `lt`, `lte`, `gt` and `gte` match only stored values of the filter value's JSON type, so `seed:gt:10` never matches a seed stored as the string "42" (M4, signed off by the human at the second review, #5). - `created_at` and `updated_at` take RFC 3339 values and compare as instants (they are stored as milliseconds), so `created_at:gte:2026-09-15T00:00:00Z` works; any other value is `bad_filter`, and so are `contains` and `exists` on them. `id` compares as a string, which for ULIDs is creation order. Every value is bound as a parameter. `order` is `field:asc` or `field:desc` on one field, default `created_at:desc`, with `id` breaking ties in the same direction. Deleted records are never returned. Bad syntax, an unknown operator or more than eight clauses is 400 `bad_filter`.

Needs the records capability.

GoQuery(ctx context.Context, collection string, params *RecordsQueryParams) (*RecordPage, error)
Pythondef query(self, collection: str, *, where: Optional[Sequence[str]] = None, order: Optional[Any] = None, limit: Optional[Any] = None, cursor: Optional[Any] = None) -> Any
JavaScriptquery(collection, params = {})
ParameterInTypeAbout
collectionpath · requiredstring
wherequerystring[]
orderquerystring
limitqueryinteger
cursorquerystringThe `next_cursor` of the previous page, unchanged.
StatusBodyMeans
200RecordPageA page of records.
400ErrorThe request is malformed. Codes: `bad_request`, `bad_filter`, `bad_cursor`, `invalid_document`.
401ErrorNo studio token, or a revoked or unknown one. Code `unauthenticated`.
403ErrorThe token lacks a capability (`capability_required`, with `details.capability`), the job is not a task the studio may change (`not_a_task`), or the request came from another origin (`bad_origin`).

POST /records/{collection}

Insert a document; returns its id and etag.

06 §6. The body is the document, a JSON object. A studio may write to any collection name matching the manifest schema's pattern, declared or not. Each live record counts toward the `records` quota (Q20).

Needs the records capability.

GoInsert(ctx context.Context, collection string, body map[string]any) (*Record, error)
Pythondef insert(self, collection: str, body: Dict[str, Any]) -> Any
JavaScriptinsert(collection, body)
ParameterInTypeAbout
collectionpath · requiredstring

Request body: JSONObject (application/json)

StatusBodyMeans
201RecordThe stored record.
400ErrorThe request is malformed. Codes: `bad_request`, `bad_filter`, `bad_cursor`, `invalid_document`.
401ErrorNo studio token, or a revoked or unknown one. Code `unauthenticated`.
403ErrorThe token lacks a capability (`capability_required`, with `details.capability`), the job is not a task the studio may change (`not_a_task`), or the request came from another origin (`bad_origin`).
413ErrorThe body is over its limit. Code `too_large`, with `details.limit` in bytes.
429ErrorA per-studio quota is full. Code `quota_exceeded`, with `details` as QuotaDetails.

GET /records/{collection}/{id}

One record. Deleted, in another collection, or another studio's is 404.

Needs the records capability.

GoGet(ctx context.Context, collection string, id string) (*Record, error)
Pythondef get(self, collection: str, id: str) -> Any
JavaScriptget(collection, id)
ParameterInTypeAbout
collectionpath · requiredstring
idpath · requiredULID
StatusBodyMeans
200RecordThe record.
401ErrorNo studio token, or a revoked or unknown one. Code `unauthenticated`.
403ErrorThe token lacks a capability (`capability_required`, with `details.capability`), the job is not a task the studio may change (`not_a_task`), or the request came from another origin (`bad_origin`).
404ErrorAbsent, deleted, or not the caller's to see. Code `not_found`.

PUT /records/{collection}/{id}

Replace a document. If-Match on the etag; 409 on conflict.

Needs the records capability.

GoReplace(ctx context.Context, collection string, id string, body map[string]any, params *RecordsReplaceParams) (*Record, error)
Pythondef replace(self, collection: str, id: str, body: Dict[str, Any], *, if_match: Optional[Any] = None) -> Any
JavaScriptreplace(collection, id, body, params = {})
ParameterInTypeAbout
collectionpath · requiredstring
idpath · requiredULID
If-MatchheaderstringThe etag last read. When given and stale, the write is refused with 409 `etag_mismatch`.

Request body: JSONObject (application/json)

StatusBodyMeans
200RecordThe replaced record.
400ErrorThe request is malformed. Codes: `bad_request`, `bad_filter`, `bad_cursor`, `invalid_document`.
401ErrorNo studio token, or a revoked or unknown one. Code `unauthenticated`.
403ErrorThe token lacks a capability (`capability_required`, with `details.capability`), the job is not a task the studio may change (`not_a_task`), or the request came from another origin (`bad_origin`).
404ErrorAbsent, deleted, or not the caller's to see. Code `not_found`.
409ErrorCodes: `etag_mismatch`, `name_taken`, `job_finished`, `already_running`, `not_running`, `port_conflict`, `heavy_conflict` (with `details.heavy`), `not_installed`, `conflict`, `in_use`, `missing`, `confirm_required` and `preview_changed` (with `details.preview`), `not_reclaimable`, and from M7: `approval_required` (with `details.approval`), `id_taken`, `not_fetched`, `already_exists`.
413ErrorThe body is over its limit. Code `too_large`, with `details.limit` in bytes.

PATCH /records/{collection}/{id}

Apply a JSON merge patch (RFC 7396). If-Match on the etag; 409 on conflict.

Needs the records capability.

GoPatch(ctx context.Context, collection string, id string, body map[string]any, params *RecordsPatchParams) (*Record, error)
Pythondef patch(self, collection: str, id: str, body: Dict[str, Any], *, if_match: Optional[Any] = None) -> Any
JavaScriptpatch(collection, id, body, params = {})
ParameterInTypeAbout
collectionpath · requiredstring
idpath · requiredULID
If-MatchheaderstringThe etag last read. When given and stale, the write is refused with 409 `etag_mismatch`.

Request body: JSONObject (application/merge-patch+json)

StatusBodyMeans
200RecordThe patched record.
400ErrorThe request is malformed. Codes: `bad_request`, `bad_filter`, `bad_cursor`, `invalid_document`.
401ErrorNo studio token, or a revoked or unknown one. Code `unauthenticated`.
403ErrorThe token lacks a capability (`capability_required`, with `details.capability`), the job is not a task the studio may change (`not_a_task`), or the request came from another origin (`bad_origin`).
404ErrorAbsent, deleted, or not the caller's to see. Code `not_found`.
409ErrorCodes: `etag_mismatch`, `name_taken`, `job_finished`, `already_running`, `not_running`, `port_conflict`, `heavy_conflict` (with `details.heavy`), `not_installed`, `conflict`, `in_use`, `missing`, `confirm_required` and `preview_changed` (with `details.preview`), `not_reclaimable`, and from M7: `approval_required` (with `details.approval`), `id_taken`, `not_fetched`, `already_exists`.
413ErrorThe body is over its limit. Code `too_large`, with `details.limit` in bytes.

DELETE /records/{collection}/{id}

Soft-delete, so an undo is possible and a sync never sees a gap.

06 §6. The record stops counting toward the `records` quota.

Needs the records capability.

GoDelete(ctx context.Context, collection string, id string, params *RecordsDeleteParams) error
Pythondef delete(self, collection: str, id: str, *, if_match: Optional[Any] = None) -> Any
JavaScriptdelete(collection, id, params = {})
ParameterInTypeAbout
collectionpath · requiredstring
idpath · requiredULID
If-MatchheaderstringThe etag last read. When given and stale, the write is refused with 409 `etag_mismatch`.
StatusBodyMeans
204Deleted.
401ErrorNo studio token, or a revoked or unknown one. Code `unauthenticated`.
403ErrorThe token lacks a capability (`capability_required`, with `details.capability`), the job is not a task the studio may change (`not_a_task`), or the request came from another origin (`bad_origin`).
404ErrorAbsent, deleted, or not the caller's to see. Code `not_found`.
409ErrorCodes: `etag_mismatch`, `name_taken`, `job_finished`, `already_running`, `not_running`, `port_conflict`, `heavy_conflict` (with `details.heavy`), `not_installed`, `conflict`, `in_use`, `missing`, `confirm_required` and `preview_changed` (with `details.preview`), `not_reclaimable`, and from M7: `approval_required` (with `details.approval`), `id_taken`, `not_fetched`, `already_exists`.