Authentication and permissions
UseGET /v1/organizations without an organization header to discover your permitted organizations.
See organizations, users, and teams for the complete flow.
For resource requests, send your personal API key as a bearer token and target one organization explicitly:
GET /v1/me identifies the key and owner without an organization header.
Organization detail returns access.role, access.configured_level, and access.effective_level. Effective access is the lower of the configured level and the owner’s
current organization role. Members have Write; organization admins have Admin.
Write includes Read; Admin includes Write. There are no separate related-resource grants.
New keys default to Read and all organizations you have access to. All-organization access follows current and
future memberships; selected access stays within the selected set. Existing organization keys are
migrated to Write and remain restricted to their original organization, which is also their
fallback when the header is omitted.
API keys read membership, role, and account status from Watchdog’s database on each request.
Clerk changes take effect after webhook synchronization. Key edits and revocation are immediate.
Normal Clerk sessions use the active organization; a supplied organization header must match it.
Read authentication and API keys for account, MFA, and lifecycle rules.
Values and collections
- Business resource IDs are UUIDs; organization and user IDs are opaque strings; JSON field names use
snake_case. - Amounts, quantities, and rates are decimal strings.
"0.25"means a rate of 25%. - Calendar dates use
YYYY-MM-DD; timestamps are UTC. Unknown source values remainnull. - Text values must not contain null characters (
\u0000). - Collections return
{ "data": [...], "next_cursor": null }. The defaultlimitis 50; the maximum is 100. - Pass
next_cursorascursorwith the same filters and ordering. Stop when it isnull. - A cursor is a position in current data, not a frozen snapshot or an event subscription.
created_before, which must be later than created_from.
Reversed or empty exclusive ranges return 400 with the field to correct.
Requests are JSON, except file bytes sent directly to the returned storage upload URL. The complete
JSON body is limited to 2 MiB, including line data. Support contact requests allow up to 32 MiB
to include attachments; individual fields also have documented limits.
Responses use fixed shapes with paginated relationships, rather than arbitrary field expansion.
My records and teams
Create shared teams, author predicates, and manage membership using the team management guide. Usemine=true on invoice lists and metrics, agreement lists, alert lists/metrics/groups,
and claim lists and metrics. It matches the shared teams the current user belongs to in the target
organization; personal API keys use their owner’s memberships. Personal subscriptions are excluded.
This filters existing access and grants no additional permissions.
Each team’s saved AND/OR rules are preserved. Teams combine with OR; other query filters narrow
results with AND. Alternatively, select up to 50 shared teams using comma-separated team_ids.
Combining mine=true and team_ids returns 400. Omitted mine or mine=false adds no membership
filter; explicit team_ids still applies.
Invoice rules use the invoice’s own supplier, recipient and fields, and matched agreements for
agreement rules. Agreement rules use linked parties and the agreement’s fields, and matched invoices
for invoice rules. Alerts use their own supplier, invoice recipient and fields, and their own
agreement. Another agreement matched to the same invoice does not contribute to an alert’s agreement
rules. Claims match through their agreement, including empty claims; their totals retain complete
claim membership and refunds. This mapping also applies to explicit team_ids.
No memberships or saved rules produces empty results and zero metrics. Missing, foreign, or personal
explicit team IDs return 404; invalid saved rules return 409 conflict with an instruction to fix
the team filters. This replaces silent-empty results for invalid rules.
Keep the same query, organization and user when following a mine cursor. Memberships, rules and
relative UTC dates are evaluated on each request, so changes can affect later pages. Pagination
remains live; no snapshot or scope version is stored.
Safely retrying requests
Agreement creation, price-item creation, invoice creation, import creation, and explicit import retry accept an optionalIdempotency-Key.
You can make normal requests without it. To safely retry after a timeout or lost response, choose
one unique value for the action (such as a UUID) and send the same value with the same input on each
attempt:
REQUEST_KEY for retries of this action. Use a new value for a new action. Without a key,
repeated requests are separate actions and do not replay earlier responses. The invoice duplicate
guard still applies: structured duplicates return 409, and imports can complete as duplicate.
Concurrent import requests may start separate extraction jobs before the duplicate is known.
Validation failures return
400 without committing a creation receipt. Correct the input and reuse
that key. Editing an invoice never changes its creation receipt. Retrying an import creates new
run/item identities; replaying the retry request returns those same identities. If a resource was
permanently deleted through the legacy application, replay returns 409. That key remains reserved;
use a new key for a new action. Soft-deleted invoices still replay normally.
Conditional agreement edits
Individual agreements, prices, and document relationships return a strongETag. Send that
resource’s validator in If-Match on PATCH or DELETE to avoid overwriting a concurrent change.
A stale or weak validator returns 412 precondition_failed; read the current resource before
reapplying the edit. Without the header, the edit applies to the locked current state.
Agreement PATCH preserves omitted fields; null clears nullable fields. A supplied nested object or
membership array replaces that complete field. Agreement list and detail responses include all
selected suppliers and recipients; send the complete desired ID array when changing a selection.
Successful nested deletion is 204. Creation returns 201 with Location; updates and replays
return 200.
Errors and retries
details with fields to correct. Branch on error.code,
not the human-readable message. Save the request ID for troubleshooting; you can send
X-Request-Id to supply your own correlation ID.
Correct 400, 401, and 403 failures before retrying. A resource outside your authorized
organization is unavailable. Respect Retry-After on 429; back off for network errors and 5xx
responses. A failed import admission may retain an error.import_id:
retrieve it before deciding whether to explicitly retry. Do not assume an error means no work exists.
A failed workflow run or item is still a retrievable resource: its GET returns 200, with failure
information in its body. Follow the polling guide
for asynchronous work.