> ## Documentation Index
> Fetch the complete documentation index at: https://docs.watchdog.no/llms.txt
> Use this file to discover all available pages before exploring further.

# API conventions

> Organizations, access, pagination, safe retries and errors: the rules every endpoint shares.

These rules apply to every endpoint. The endpoint reference has the exact fields, parameters and
responses for each one.

## Choose an organization

Every request sends your API key as `Authorization: Bearer …` and the organization it's for as
`X-Organization-Id`, as in the [quickstart](/api-reference/quickstart). To see which organizations
your key can use, call `GET /v1/organizations`, the one request that doesn't need
`X-Organization-Id`.

## Access levels

A key has one of three levels: **Read** lets you read data, **Write** also lets you create, change
and start work, and **Admin** adds organization settings. A key can never do more than its owner's
role in the organization allows. `GET /v1/organization` shows what your key can
do there. See [authentication and API keys](/api-reference/authentication) for more.

## Values

* Amounts, quantities and rates are decimal strings, such as `"1250.00"`. Rates are fractions:
  `"0.25"` means 25%. Amounts are exact as recorded; amounts converted to the organization
  currency are rounded to cents.
* Dates are `YYYY-MM-DD`. Timestamps are in UTC.
* A value Watchdog doesn't know is `null`.

## Pages of results

Lists return up to 50 records by default (`limit` goes up to 1000), plus a `next_cursor`. To get the
next page, send it back as `cursor` with the same filters. When `next_cursor` is `null`, you have
everything.

Reads accept `fields` to return only the fields you need, comma-separated, with dots for nested
fields: `fields=title,impact_amount,invoice.invoice_number`. On lists it applies to each row in
`data`. `id` (or a group's `key`) is always returned, and an unknown field returns 400 with the
fields that exist. Response types still describe the full record; leave `fields` out to get it.

Lists use `cursor` and `limit`, with `sort` and `direction=asc|desc`. There is no offset pagination.
Free-text filters are called `search`; search commands take `query`. Multi-value filters have plural
names, such as `statuses`, and accept comma-separated values.

Use `supplier_ids=A,B&supplier_operator=is_not` to exclude values. `is` is the default. Supported
relation filters combine with `join_operator=and|or`; the reference describes which ones participate.
Organization and access boundaries always apply. Calendar dates use inclusive `_from`/`_through`;
timestamps use `_from`/`_before`. `has_topic=false` and `has_claim=false` select missing relationships;
`topic_ids=A,null` includes a topic and uncategorized alerts together.

## My records and teams

Lists of invoices, agreements, alerts and claims accept `mine=true`, which returns only the records
covered by the teams you belong to. To use specific teams instead, send `team_ids`. Teams only
narrow what you see; they never give you access to more. See [teams](/api-reference/teams) to set
them up.

## Safely retrying requests

Requests that create something or start work accept an optional `Idempotency-Key` header. The
endpoint reference shows which ones. If a request times out or you don't get an answer, send it
again with the same key and the same body. You get the original result back instead of a second
copy. No endpoint requires a key. Other commands are safe to repeat: setting a state that already
holds, or removing something already gone, changes nothing.

```bash theme={null}
curl "$API_URL/v1/invoices" \
  -H "Authorization: Bearer $API_KEY" \
  -H "X-Organization-Id: $ORGANIZATION_ID" \
  -H "Idempotency-Key: $(uuidgen)" \
  -H 'Content-Type: application/json' \
  --data-binary @invoice.json
```

Use a new key for each new action. Reusing a key with a different body returns `409`.

## Avoid overwriting someone else's change

Resources that support conditional updates return an `ETag` header. Send it back as
`If-Match` when you change or delete the record. If someone changed it in the meantime, you get
`412` and can read it again before retrying. `If-Match` is optional.

## Commands and deletion

Background work returns `202`, a `workflow_run_id`, and a `Location` pointing to that run. Cancel
and retry its original inputs through `/v1/workflow-runs`; use `item_ids` to select items. Checking
with no eligible invoices returns `200` and a null workflow ID. Checking all agreements can start
several runs: its `workflow_run_ids` lists them and Location points to the first. Invoice import creation
keeps its import resource Location; the returned import includes the workflow ID.

Delete a record with `DELETE /{collection}/{id}`; send options in the query string. Bulk commands
use `POST /{collection}/{verb}` with `{ids}` or `{filter}`. Creation batches use `{entries}`. A
preview is `POST {command}/preview` with the same input as the command.

Read comments with `GET /v1/activity?entry_type=comment`.

## Errors

Errors look like this:

```json theme={null}
{
  "error": {
    "code": "validation_error",
    "message": "The request could not be validated.",
    "request_id": "…"
  }
}
```

* Use `code` in your code, not `message`. Validation errors also list the fields to fix in
  `details`.
* Fix `400`, `401` and `403` errors before trying again. Retrying won't help.
* On `429`, you've sent too many requests. Wait the number of seconds in `Retry-After`.
* On `5xx` or a network error, wait a moment and try again, waiting longer each time.
* Include `request_id` when you contact us about an error.

Work that runs in the background, such as an import, can fail after the request succeeded. That
failure shows up on the import or [workflow run](/api-reference/workflow-runs), not as an HTTP error.

## Changes to the API

The API has one version, `/v1`, and it is in beta. Until we release a stable version, endpoints,
fields and behavior may change or be removed without notice. We announce the stable version in the
[changelog](/api-reference/changelog).

The API also grows over time: we add new endpoints, fields, optional parameters and values without
notice, during the beta and after it. Build your integration to:

* ignore fields it doesn't know;
* treat a value it doesn't recognize, such as a new status, as "other".

Once the API is stable, a change that could break an integration is marked as deprecated in the
endpoint reference, announced in the changelog, and emailed to the customers who use it. It keeps
working for at least 3 months after that.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.