---
title: Best practices
url: https://www.tines.com/docs/api/records/best-practices/
updated: 2026-07-15T13:53:43+00:00
description: Guidance for building with the Records API. Endpoint pages describe what each call does; this page recommends how to combine them.
---

*[tines.com](https://www.tines.com/llms.txt) › [Docs](https://www.tines.com/docs/llms.txt) › [Tines API](https://www.tines.com/docs/api/llms.txt) › [Records](https://www.tines.com/llm/docs/api/records.md)*

# Best practices

*[View on tines.com](https://www.tines.com/docs/api/records/best-practices/)*

Guidance for building with the Records API. Endpoint pages describe what each call does; this page recommends how to combine them.

See the [Get Records API](/api/records/get) for the record model.

## Reference by ID, not name

`record_type_id` and `field_id` are stable; names can be renamed at any time. Discover the schema once via the [List record types API](/api/records/record_types/list), cache field IDs by name, and refresh on `404`. The [Query API](/api/records/query) also accepts `field_name` — use it only for ad-hoc queries.

## Use /records/query for aggregations

For counts, sums, averages, or grouped breakdowns, call the [Query API](/api/records/query). It answers in one request with its own rate-limit budget. Only reach for the [List API](/api/records/list) when you need the individual rows.

## Only request what you need

- Pass `record_field_ids` on [Get](/api/records/get) or [List](/api/records/list) to return only the columns you need.
- Artifacts come back as `{ id, is_artifact: true }` references. On [Get](/api/records/get), [Create](/api/records/create), and [Update](/api/records/update) you can pass `resolve_artifacts=true` to inline the contents — only do this when you need them now. [List](/api/records/list) always returns references with no inline option, so fetch them lazily via [Get artifact](/api/records/artifacts/get) when you need the value.
- Child records come back as ID stubs. Set `include_children=true` only when you need their fields. Use `parent_ids` on [List](/api/records/list) to fetch children in one request.

## Paginate deliberately

`per_page` defaults to 20 and caps at 500. Filter and sort server-side (`filters`, `order_field_id`), follow `meta.next_page`, and bound the total pages your client will walk.

## Filter operator gotchas

- `IS_ANY_OF` / `IS_NONE_OF` accept up to 10 values — prefer them over repeated `EQUAL` requests.
- `CONTAINS`, `STARTS_WITH`, `ENDS_WITH` need three-plus characters and one pattern filter per request.
- Values compare as strings, case-insensitively — a JSON `true` matches `"true"`. Store fields as strings if you plan to filter on them.
- `ARTIFACT` fields cannot be filtered.

## Handle rate limits and retries

The Records API is rate-limited ([API rate limits](/api/api-rate-limits)); record and query limits are separate. On `429`, back off exponentially with jitter and a max attempts cap.

Retry safety:

- `POST /records` is not idempotent — a retry after a timeout or connection drop can create a duplicate. Dedupe on a natural key, or verify with a follow-up read before retrying.
- `PUT` and `DELETE /records/{id}` and `POST /records/query` are safe to retry.

## Use test_mode for test data

Pass `test_mode: true` on writes from staging or test workflows. Test records are excluded from live listings and aggregations unless you pass `test_mode=true` on [List](/api/records/list) or [Query](/api/records/query).

## Attaching records to cases from the record side

- On [Create](/api/records/create): `case_ids: [<id>, ...]` (max 5).
- On [Update](/api/records/update): `add_case_ids` / `remove_case_ids` (max 5).

For status, comments, fields, or Cases-only reads, use the [Cases API](/api/cases/get).

## Configure retention explicitly

Set `ttl_days`, `retention_column_name`, `max_records_limit`, and `on_limit_reached` at creation time (see [Create record type](/api/records/record_types/create)):

- Enrichment cache: `retention_column_name: "UPDATED_AT"`, `on_limit_reached: "EVICT_OLDEST"`.
- Audit trail: `retention_column_name: "CREATED_AT"`, `on_limit_reached: "REJECT"`.

## Common errors

| Status | Typical cause                                                                                                                      |
| ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| 400    | Malformed request — `filters` not valid JSON, unknown field, disallowed operator.                                                  |
| 404    | Record, type, or field not found — **or** the token can't see it. Under-privileged tokens return `404`, not `403`.                 |
| 422    | Validation failure — `TEXT_ENUM` value not in `fixed_values`, updating a record whose record type isn't editable, circular parent. |
| 429    | Rate limited — back off.                                                                                                           |
| 504    | [Query API](/api/records/query) exceeded 30s — narrow filters or time range.                                                       |

Treat `404` on a resource you expect to exist as a scope check on the API key.
