Choose an organization
Every request sends your API key asAuthorization: Bearer … and the organization it’s for as
X-Organization-Id, as in the 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 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 acceptmine=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 to set
them up.
Safely retrying requests
Requests that create something or start work accept an optionalIdempotency-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.
409.
Avoid overwriting someone else’s change
Resources that support conditional updates return anETag 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 returns202, 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:- Use
codein your code, notmessage. Validation errors also list the fields to fix indetails. - Fix
400,401and403errors before trying again. Retrying won’t help. - On
429, you’ve sent too many requests. Wait the number of seconds inRetry-After. - On
5xxor a network error, wait a moment and try again, waiting longer each time. - Include
request_idwhen you contact us about an 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.
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”.