Skip to main content
A shared team has members and one saved filter. The filter defines which records match. Membership determines whose mine=true includes that filter. Teams grant no additional resource access. An organization member with Write access can manage any shared team in that organization; Read access is enough to inspect teams, rules, and users.

Discover an organization

Start without an organization header:
The response is { "data": [{ "id": "org_...", "name": "Example" }], "next_cursor": null }. Follow next_cursor with cursor until it is null. The default page size is 50; the maximum is 100. Organizations are ordered by name, then ID. Discovery requires a valid key or signed-in Clerk session, but no selected organization. A supplied X-Organization-Id is ignored for this endpoint. Only synchronized memberships with a supported organization role appear. Selected keys remain within their selected set; migrated keys remain within their original organization. No memberships produces an empty page. Discovery does not enforce organization MFA or grant resource access. For subsequent requests, choose a returned organization ID and send X-Organization-Id. All normal role, key-access, and MFA checks apply. Clerk sessions must first select that organization as active, and any supplied header must match it. Membership changes appear after Clerk webhook synchronization.

Find members and create a team

GET /v1/members lists the selected organization’s current members, including user_id, name, email, role and join date. Use user_id when adding a team member. GET /v1/teams/{id}/members returns paginated {id, first_name, last_name, email} records. Both lists are ordered by user ID. Names and email can be null. User and organization IDs are opaque strings; team IDs are UUIDs. Pending invitations are not members.
Creation returns 201 and a Location header. Names are trimmed, limited to 100 characters, and unique case-sensitively within the organization. Repeating a name returns 409 conflict. The team initially has no members and no filter. You may provide a filter during creation. GET /v1/teams remains a compact name-searchable selector with id, name, and organization_number: null. Retrieve /v1/teams/{id} for the saved filter, created_at, and updated_at; retrieve its /members endpoint for membership.

Define the predicate

A condition has field, op, and value. A group combines conditions or nested groups using combinator: "and" or "or". There is no implicit precedence or group-level NOT. The version-1 definition has schema_version: 1 and a root group. Maximums are 20 conditions and three group levels, including the root. Empty groups and empty value lists are invalid. This selects supplier A or B, and recipient C. Replace the placeholders with actual UUIDs:
Use is / is_not for one exact value and is_any_of / is_none_of for a list. All four accept both a scalar and a nonempty array for compatibility. A positive list means any listed value; a negative list excludes every listed value. Reference values must identify records in the selected organization, including when used with negative operators.

Fields and operators

Exact text comparisons are case-sensitive and do not trim or normalize stored rule values. contains and not_contains accept a single nonempty string, are case-insensitive, and treat % and _ literally. Exact text operators also accept a nonempty string array. invoice.amount uses the invoice total including VAT. It performs no currency conversion. Use decimal strings when exact precision matters. Date values must be real calendar dates. within accepts last_30_days, last_90_days, last_365_days, expiring_30_days, expiring_60_days, or expiring_90_days. These ranges include both endpoints and use the current UTC date on each request. invoice.reference checks contract, buyer, seller, project, credit-note invoice and payment references, plus exact elements of order_references. Substring operators do not search the order-reference array. invoice.delivery_address checks delivery name, street, postal code, city, state, and country. A positive condition matches any supported component; a negative condition requires every supported component to avoid the value.

Which records are evaluated?

Conditions over related records are evaluated independently. For example, an agreement linked to a USD 2,000 invoice and a NOK 100 invoice matches invoice.amount > 1000 AND invoice.currency is NOK. One invoice satisfies each condition; no single invoice needs to satisfy both. Grouping those conditions with AND does not introduce a same-invoice requirement. The same principle applies to conditions over multiple matched agreements or linked suppliers. Version 1 has no same-related-record group. For related collections, a positive condition needs a matching related record; a negative condition means no related record matches its corresponding positive condition. Empty relationships therefore satisfy negative conditions. On nullable scalar fields, negative exact/substring conditions include null, while positive, numeric, and date comparisons require a value. Normal resource visibility and deletion rules still apply. An alert uses its own agreement: another agreement matched to the same invoice cannot satisfy its agreement rules. Claims match through their agreement even without attached alerts. Team predicates select complete claims; they do not trim claim members or refund totals.

Save a supplier / recipient / tag rule

Resolve IDs through /v1/suppliers, /v1/recipients, and /v1/agreements/tags. Follow their pagination when selecting records. With your chosen UUIDs in the variables below:
This selects records matching any of the three conditions. PATCH replaces the complete filter when supplied. Omit filter to preserve it, or send {"filter":null} to clear it. Clearing matches no records; it does not mean match everything. Name and filter edits commit together. Invalid writes return 400 validation_error with up to 20 details identifying fields such as filter.root.children.0. Foreign references are reported as unavailable without disclosing foreign records. Read responses preserve malformed legacy JSON so it can be inspected. Replace or clear it to repair it; an unrelated name edit leaves it unchanged. Selecting a team with unsupported stored rules returns 409 conflict until those rules are corrected.

Add a member and observe the scope

For mine=true, add the authenticated key owner or session user. Their ID is available in /v1/me:
Adding another current organization user works the same way using user_id from /v1/members. New membership returns 201; repeated addition returns the existing user with 200. Missing/foreign users or teams and personal subscriptions return 404. mine=true unions the user’s shared team predicates with OR. Explicit team_ids unions the selected teams, regardless of membership. Ordinary query filters narrow that union with AND. Selecting the same record through multiple teams does not duplicate records or totals. mine=true and team_ids together return 400; explicit selection accepts up to 50 teams. No memberships or no saved predicates produces empty lists/groups and zero metrics. Changes to rules and memberships apply on subsequent requests, including requests using an existing cursor. Pagination remains live: keep the same user, organization, query, and ordering. There is no snapshot, automatic restart, or scope version. Cursors cannot be transferred between queries.

Remove membership or delete the team

Membership removal returns 204, including when a current organization user is already absent. Removing the last member is allowed; the team and its filter remain usable through team_ids. Team deletion returns 204 and permanently removes the team, saved filter, and membership links. It does not delete organization users or business records. There is no restore. Missing or already deleted teams return 404. The deleted team stops contributing to mine; explicit team_ids containing it returns 404, including on later pages. Personal subscriptions are outside this API.