# Requests, lists, and errors

Flat operation bodies, cursor pagination, stable errors, and request identifiers.

## Field names and values

Built-in operations use `snake_case` for request and response fields. Record filters and sort also use those published names, for example `data.start_time`; internal field spellings are not accepted. App-defined fields keep the names explicitly published by that app. Dictionary keys and user content are not renamed.

Timestamp fields declare their units in the operation reference. Record timestamps use Unix milliseconds; ISO date-time strings remain strings where specified. Monetary fields on deals, products and product lines are Norwegian øre (100 øre = NOK 1). These records do not support selecting another currency.

## Operation requests

The API base is `https://api.brigg.app/v1`. Discover operations with `GET /operations`, inspect one with `GET /operations/{name}`, and invoke it with `POST /operations/{name}`. Operation names are case-sensitive. Follow the released schema for each operation.

Send `Content-Type: application/json` and a JSON object matching the input schema. Responses match the output schema directly. Schema definitions use the terms input and output; those terms do not add wrappers to the wire format.

## Operation names

Names follow product areas, such as `customers.ListCompanies` and
`meetings.ListMeetings`. Use the exact published name in operation requests.

Operations published by workspace apps use `apps.<app namespace>.<Operation>`,
for example `apps.helpdesk.ReadMetrics`. Discover the exact names available to
your credential with `GET https://api.brigg.app/v1/operations`.

## Lists and pagination

List contracts use `items` and an optional `next_cursor`:

```json
{
  "items": [{ "id": "example_record" }],
  "next_cursor": "opaque_cursor"
}
```

Use the next cursor as the following request's `cursor`, keeping the same filters. Record-list operations default to 100 rows per page and accept a positive integer `limit`; they do not impose a fixed maximum. Other list operations document their own defaults, bounds and cursor support. Cursors are opaque: store and pass them unchanged.

The final page omits `next_cursor`. An empty result is `{ "items": [] }`. Do not infer that the list is complete from the number of items in a page.

## Errors

HTTP failures use a stable error code, a human-readable message, and optional structured details:

```json
{
  "error": "rate_limited",
  "message": "The request limit has been reached."
}
```

Agents and integrations should branch on `error` and structured `details`, rather than parsing the message.

| Status | Meaning | What to do |
| --- | --- | --- |
| 400 | Invalid input | Correct the request using the schema and details |
| 401 | Missing, invalid, or revoked credential | Check authentication |
| 403 | Insufficient access or approval required | Review permissions or complete approval |
| 404 | Operation or permitted resource not found | Check the catalog and identifier |
| 409 | Conflict or incompatible retry | Reconcile the operation's state |
| 429 | Rate or resource limit reached | Respect `Retry-After` and back off |
| 5xx | Server failure | Follow the operation's retry rules |

## Request identifiers

The `X-Request-Id` response header identifies the request for diagnostics. Include it when reporting a failed call. Request IDs identify attempts; an idempotency key identifies the logical command across retries.

See [safe retries and limits](https://developer.brigg.app/reliability) before retrying a command.

## Iterate through a list on your server

The [Node.js server helper](https://developer.brigg.app/client.mjs) exposes each row as an async iterator. It follows the server's cursors, preserves your filters and sort order, and keeps only the current page in memory:

```typescript
import { createBriggClient } from "./brigg.mjs";
const brigg = createBriggClient({ apiKey: process.env.BRIGG_API_KEY!, baseUrl: "https://api.brigg.app" });

for await (const company of brigg.items("customers.ListCompanies", {
  filters: [{ field: "data.name", op: "contains", value: "Example" }],
  sort: { field: "data.name", direction: "asc" },
})) {
  console.log(company.id, company.data.name);
}
```

Use this with operations that return `items` and an optional `next_cursor`. Plain `call(operation, input, options)` returns the original response, which is useful when your application stores its own cursor. Commands require an explicit `requestId` in `options`; reuse it when retrying the same command. The helper does not automatically repeat an ambiguous command failure.

## Synchronize records without duplicates

Use a resource's Upsert operation with `external_ref: {namespace, id}` for a permanent source-system match. Choose the namespace once and use the source system's record ID. The reference is scoped to the workspace, resource type and private/workspace ownership; it survives API key replacement and the retry-receipt window. It never guesses a match by email or name.

For larger files, [start a resumable record import](https://developer.brigg.app/reference/integrations/StartRecordImport). Upload UTF-8 NDJSON with one complete Upsert request per line, then inspect progress and per-row outcomes. It applies the same grants, validation and webhooks as individual calls. Invalid rows are reported; interrupted work can resume without duplicating completed rows.

