Brex
Automate expense workflows
- Category
- Finance
- Primary Subcategory
- Corporate Spend, Expense & Procurement
Integration details
Description
Brex helps finance teams and employees review company spend through ChatGPT. Users can look up expenses, cards, limits, users, bills, vendors, and accounting records; analyze spend; attach receipts from HTTPS URLs; update memos and attendees; assign card expenses to limits; export expense data; and submit product feedback.
- Integration type
- Plugin
- Verification status
- Not applicable
- Platform
- ChatGPT
- Primary Subcategory
- Corporate Spend, Expense & Procurement
- Secondary Subcategories
- None listed
- Brand
- Brex
- Access
- Account required
- First tracked
- 2026-09-21
- Tool count
- 33
- Geography
- US
The Primary Subcategory used for this profile’s headline score.
Other Subcategories where the Integration is listed.
Get alerts for Brex
Get updates when Brex’s Discoverability Score or category rank changes.
ChatGPT Plugin Discovery Score
ChatGPT Plugin discovery is coming soon
ChatGPT can surface a Plugin when it matches a user's request.Your Plugin Discovery Score measures how often yours appears.
No spam. Unsubscribe any time.
What discovery looks like

Competing in ChatGPT Corporate Spend, Expense & Procurement
View Category33 tools agents can invoke
Assign one or more card expenses to a spend limit. TERMINOLOGY: On Brex, these are called "limits" — NOT "budgets." There are two kinds: card limits (built into a card) and spend limits (exist independently). "Budget" is a separate Premium-only planning/tracking feature. When users say "budget" they almost always mean "limit." Prefer "limit" in responses unless the user is specifically asking about the Budget feature. The API returns fields named "budget_*" but these should be presented as "limits" to users. Parameters: - expense_ids: Array of card expense IDs to assign the limit to - limit_id: The spend limit ID to assign to all expenses Example (single card expense): { "expense_ids": ["card_exp_123"], "limit_id": "limit_123" } Example (multiple card expenses with same limit): { "expense_ids": ["card_exp_123", "card_exp_456", "card_exp_789"], "limit_id": "limit_abc" }
assign_limit_for_card_expenses
Get the currently active accounting integration. Returns { "id": null, "vendor": null } if no active accounting integration is found. Example response: { "id": "SW50ZWdyYXRpb246NzEwOWU5YWMtYWRiZi00MGZjLTliMzMtZTE4OTBjYTk1MzM1", "vendor": "QuickBooks Online" }
get_active_integration
Get a card by its ID, including its current spend configuration (merchant category controls and expense policy). For a shared card, shared_card_details.authorized_users is a preview, not the roster: it stops at the first 100 members. Always check it against member_count, which is the real total. If member_count is larger than the number of users listed, you are seeing a partial list — report the count and say the rest are not shown. Never present a partial list as everyone who has access. Describe MANUAL_INCLUDE as "added directly" — that person stays until someone removes them. Describe MEMBER_CONDITION as "added by a rule" — they match a rule such as a department, and they drop off on their own if they stop matching, so removing them means changing the rule rather than the person. GLOBAL USER-FACING RULES — apply to EVERY message you send the operator. Everything in this description is internal contract. It is here so you can reason and build a correct call; none of it belongs in a reply. - Never show wire values. Statuses, reasons, card categories, card forms, reset frequencies and limit flexibilities are internal codes: say "locked", "cancelled", "a virtual card", "monthly", "up to 10% over the limit". - Never show request or response FIELD NAMES, and never use `field: value` form. Describe what the operator will observe, in prose. - Never show internal IDs (card, user, policy, limit). Name objects by display name only, e.g. "Tony Report", "Default Policy". - Never name tools or internal steps: no "list_cards", "duplicate check", "preflight", "tool call". - Never volunteer how Brex records a card action internally. The spend limits, budgets and other bookkeeping behind locking, cancelling, transferring or issuing a card are implementation, not product: describe only what the operator will observe. Spend limits ARE a product the operator can be offered in their own right — the rule is about not explaining a card action in terms of them. - Any examples below use wire vocabulary deliberately, because they show the shape of a call. Copy their structure, never their vocabulary.
get_card_by_id
Get the current authenticated user This tool retrieves the profile information of the currently authenticated user making the request. Use this when you need to get information about who is currently logged in or making the API call. - include: Optional tokens. phone_number, brex_assistant. Unrequested fields are null. Example Output: { "id": "cuuser_123", "first_name": "John", "last_name": "Doe", "email": "[email protected]", "role": "CARD_ADMIN", "status": "ACTIVE" }
get_user_myself
Get an expense by its ID Declines and failed authorizations: - payment_status - the payment outcome. DECLINED means the card authorization was refused; CANCELED means an authorization was reversed or expired without clearing; CLEARED means it settled. - payment_status_label - the same wording the Brex dashboard shows, for display. Use payment_status for logic and filtering, this for phrasing: it varies by expense type, so DECLINED reads "Declined" on a card but "Failed" elsewhere, CLEARED reads "Completed" but "Paid" on a clawback, and CREDITED reads "Chargeback". Prefer it over inventing your own wording for a status. - payment_status_reason - WHY, for card authorizations. This is the field that explains a decline: e.g. EXCEEDED_CARD_LIMIT (over the card's limit), EXCEEDED_BUDGET_LIMIT / EXCEEDED_ANCESTOR_BUDGET_LIMIT (over the budget), BUDGET_CATEGORY_RESTRICTION / BUDGET_MERCHANT_RESTRICTION / MCC_BLOCKED (merchant or category not allowed), CARD_SUSPENDED / CARD_TERMINATED / CARD_NOT_ACTIVE / CARD_EXPIRED (card state), INVALID_CVV / INVALID_EXPIRATION_DATE / INVALID_BILLING_ADDRESS (bad credentials at checkout), SUSPECTED_FRAUD, ACCOUNT_SUSPENDED, USER_SUSPENDED, NO_BUDGET. THIS TOOL IS THE ONLY SOURCE OF payment_status_reason. list_expenses returns payment_status but not payment_status_reason, because resolving the reason costs one extra lookup per expense. To answer "why was my card declined?": call list_expenses to find the failed attempts (payment_statuses: ["DECLINED", "CANCELED"], plus merchant/date/card as needed), then call this tool with each expense id to read its reason. A null payment_status_reason here means no authorization decision is recorded (or the expense is not a card expense) -- it does NOT mean "declined for an unknown reason". Include (opt-in fields; otherwise null): - include: Array of tokens. Supported values: * "receipts" - Full receipt details (asset_id, download_uri, parsed content/line items). Without this, receipts only contain IDs. * "travel_metadata" - Flight, car rental, lodging, and train data * "travel_booking" - Associated Brex Travel booking (trip ID and booking status) * "location" - Expense location details * "memo_details" - Detailed memo source information * "accounting_field_values" - Accounting coding fields assigned to the expense Location and travel data are ALWAYS null unless requested via include. A null location does NOT mean "no location exists" — re-call with include: ["location"]. Same for travel_metadata. ⚠️ When the user asks ANYTHING about receipts — line items, itemized charges, "what did I buy", receipt content/images/downloads, parsed receipt data, whether a receipt is attached, or receipt counts — you MUST pass include: ["receipts"]. A receipt with only an ID and no content/download_uri means include did not contain "receipts" — re-call with include: ["receipts"]. Example 1: Getting an expense with expanded receipts (includes line items, download URL, parsed content): { "id": "expense_123", "include": ["receipts"] } Example 2: Getting an expense with travel metadata and location: { "id": "expense_123", "include": ["travel_metadata", "location"] } Example 3: Getting an expense with everything: { "id": "expense_123", "include": ["receipts", "travel_metadata", "location"] } Understanding Expense Lifecycle and Compliance Status: Expense Lifecycle: 1. New expense created (card transaction or reimbursement submitted) 2. Documentation phase: System checks if documentation is required (receipts, memo, attendees, etc.) 3. Spender submits documentation if needed 4. Review phase: System checks if approval/review is required based on company policy 5. Reviewer reviews and approves/rejects if necessary 6. Expense is finalized Each expense returns TWO sets of compliance-related fields: A. DOCUMENTATION COMPLIANCE (for spenders - receipts, memo, attendees): - documentationComplianceStatus: Status of documentation requirements that the spender must fulfill * "NOT_REQUIRED" - Company policy does not require any documentation for this expense. Empty receipts/memo are acceptable. * "COMPLETED" - All required documentation has been provided according to policy. * "DUE" - Documentation is required by policy but not yet provided. Check missingDocumentations field for specifics. * "OVERDUE" - Required documentation is past its submission deadline. Check missingDocumentations field for specifics. - missingDocumentations: Array of specific items required by policy but not yet provided. Possible values: ["MEMO", "RECEIPT", "ATTENDEES", "EXTENDED_FIELD"] * Empty array [] - Either no documentation is required OR all required documentation is complete * Non-empty array - Lists specific items that must be provided (e.g., ["MEMO", "RECEIPT"]) - documentationSubmissionDeadline: The UTC timestamp by which documentation must be submitted (only present if documentation is required) B. REVIEW COMPLIANCE (for reviewers - approval/rejection): - reviewComplianceStatus: Status of review/approval requirements that the reviewer must fulfill * "NOT_REQUIRED" - Company policy does not require review/approval for this expense. * "COMPLETED" - The expense has been reviewed and approved/rejected. * "DUE" - Review is required by policy but not yet completed. * "OVERDUE" - Required review is past its deadline. - reviewDeadline: The UTC timestamp by which the review must be completed (only present if review is required) KEY DISTINCTIONS: - DocumentationComplianceStatus="NOT_REQUIRED" and missingDocumentations=[] → Documentation not required by company policy - Empty receipts/memo with documentationComplianceStatus="DUE" or "OVERDUE" and missingDocumentations=["RECEIPT","MEMO"] → Documentation IS required by policy but missing - reviewComplianceStatus="DUE" or "OVERDUE" → Expense is waiting for someone to review/approve it - reviewComplianceStatus="NOT_REQUIRED" → No approval needed
get_expense_by_id
Check the status of an expense download job and get the download URL when ready. Call this after start_expense_download to poll for completion. Download jobs typically take 10-300 seconds. Response statuses: - PROCESSING: Job is still running. Poll again after waiting - COMPLETED: Job finished successfully. The download_url field contains a URL to download the CSV file. - FAILED: Job failed. The error field contains the failure reason IMPORTANT: Recommended polling strategy: 1. Wait 5 seconds after calling start_expense_download 2. Poll every 10 seconds until status is COMPLETED or FAILED 3. Maximum expected duration: 5 minutes The CSV file includes 15 columns: Parent ID, Flagged Expenses, Transaction Date, Expense Type, Card Last 4, Amount, Currency, Original Amount, Original Currency, Merchant Name, User, Budget Name, Memo, Expense Status, Payment Status. If fetching `download_url` fails after status is COMPLETED: The signed URL is valid and reusable. The most common cause of a download failure is that the client environment blocks outbound requests to external hosts. If the client has a domain/URL allowlist (e.g., Claude Code Web's "Allowed domains" setting), the user must add `api.brex.com` to it before the fetch will succeed. After allowlisting, simply retry the fetch — there is no need to re-run start_expense_download. Parameters: - job_id (required): The job ID returned by start_expense_download
get_expense_download_result
Get the full RULES of the expense policy attached to a specific spend limit (receipts, memos, approvals, merchant restrictions). TERMINOLOGY: On Brex, these are called "limits" — NOT "budgets." There are two kinds: card limits (built into a card) and spend limits (exist independently). "Budget" is a separate Premium-only planning/tracking feature. When users say "budget" they almost always mean "limit." Prefer "limit" in responses unless the user is specifically asking about the Budget feature. The API returns fields named "budget_*" but these should be presented as "limits" to users. **WHEN TO USE (policy RULES on a limit):** - User asks whether a specific expense is allowed, or what compliance rules apply on THEIR limit - Examples: "Do I need a receipt for this $30 lunch?", "Can I expense alcohol?", "Who approves my $500 dinner?", "Can I expense Uber Eats?", "What are the rules for my Travel limit?" **WHEN NOT TO USE — pick a different tool instead:** - User wants limit balances, remaining spend, or limit names/IDs only → use list_my_limits (e.g. "How much is left on my Marketing limit?") **Requires spend_limit_id.** When the user asks policy-RULE questions without naming a limit: 1. Call list_my_limits to get limit IDs 2. If only one active limit, use its ID automatically 3. If multiple limits and context is unclear, ask which limit This tool returns rules in the same format as the Dashboard "View policy" sidebar (structured JSON). **What you'll get:** - formatted_text: Human-readable policy text matching Dashboard display - sections: Array of policy sections with rules - has_restrictions: Boolean indicating if there are any policy restrictions - budget_name: Name of the limit - policy_name: Name of the policy **Parameters:** - spend_limit_id: **REQUIRED** - Limit ID from list_my_limits (e.g. spl_abc123). - rules_filter: **OPTIONAL** - "ONLY_RELEVANT_FOR_REQUESTER" (default) or "ALL_RULES" (admin; all user exceptions). **Response Format:** Returns structured JSON with formatted_text, sections[], has_restrictions, budget_name, policy_name.
get_expense_policy
Get complete details for one spend limit by its raw ID. Use this only after the request or a prior tool result identifies one specific spend limit. Pass the raw budget_... ID returned by list_spend_limits or list_my_limits; do not pass a limit name or Relay-encoded ID. ROUTING EXAMPLES: - "Show full details for spend limit budget_123" → use this tool - "Show full details for the Marketing limit I manage" → use list_spend_limits first, then this tool with the resolved ID - "How much is available on that limit?" after a list result identified one limit → use this tool with that result's ID - "Which spend limits can I manage?" → use list_spend_limits - "What limits can I spend from?" → use list_my_limits Returns financial details, dates, owners, members, visibility, and the visible containing-budget path when the caller is authorized to view them.
get_spend_limit_by_id
Get the expected payout date for a reimbursement expense. Returns the date when the reimbursement payment is expected to arrive. Only works for REIMBURSEMENT type expenses that are paid through Brex. Note: This tool will return an error if the reimbursement is configured to be paid outside of Brex (Pay outside of Brex / PoB). In those cases, the payment is handled directly between the company and employee (usually at payroll time), and Brex does not control the payment date. Parameters: - expense_id: The ID of the reimbursement expense Example: { "expense_id": "expense_123" } Response includes: - expense_id: The ID of the expense - expense_type: The type of the expense (will be REIMBURSEMENT) - expected_reimbursement_payout_date: The expected date when the reimbursement will be paid out (ISO 8601 format, UTC timezone) - status: The current status of the expense
get_reimbursement_payout_date
Get a user by their unique ID This tool retrieves detailed information about a specific user using their unique user ID. Use this when you have a user ID and need to get their complete profile information. - include: Optional tokens. phone_number, brex_assistant. Unrequested fields are null. Example Input: { "id": "cuuser_123", "include": ["phone_number"] } Example Output: { "id": "cuuser_123", "first_name": "John", "last_name": "Doe", "email": "[email protected]", "role": "CARD_ADMIN", "status": "ACTIVE", "manager_id": "cuuser_5678", "manager_first_name": "Jane", "manager_last_name": "Smith", "manager_title_id": "ti_1234", "manager_title_name": "Engineering Manager", "department_id": "cudmnt_1234", "department_name": "Engineering", "location_id": "culoc_1234", "location_name": "San Francisco", "title_id": "ti_5678", "title_name": "Software Engineer" }
get_user_by_id
Get a vendor by ID
get_vendor_by_id
List Brex account roles. REQUIRED prerequisite for the list_users `role` and `access` filters: list_users only accepts role IDs, so whenever the user mentions a role or access type by name (e.g. "card admin", "employee", "bill pay approver", "card access"), call this tool first and pass the returned `id` into list_users. Roles have two types: - FUNCTIONAL ("what the user is" — CARD_ADMIN, EMPLOYEE, ACCOUNT_ADMIN, ...). These IDs feed the list_users `role` filter. - ACCESS ("what the user can do" — card access, bill pay approver, travel admin, ...). These IDs feed the list_users `access` filter. Pass role_type=["FUNCTIONAL"] to resolve names for the `role` filter, role_type=["ACCESS"] to resolve names for the `access` filter, or omit to return both. For FUNCTIONAL roles, is_admin=true indicates the role grants admin-level access. Parameters: limit (1-100, default 10), cursor (pagination), role_type (filter by type). Example Output: { "items": [ { "id": "role_1234", "name": "CARD_ADMIN", "display_name": "Card Admin", "role_type": "FUNCTIONAL", "is_admin": true } ], "next_cursor": null }
list_roles
List users with optional filtering and pagination. 🚨 CRITICAL — ID filters REQUIRE companion discovery tools FIRST 🚨 The following filters accept ONLY Brex resource IDs (never names, labels, or human-readable strings). If the user describes a filter in plain English (e.g. "Engineering department", "San Francisco office", "Brex Inc. entity", "Software Engineer title", "R&D cost center", "bill pay approvers", "card admins", "reports to Jane Smith"), you MUST call the matching discovery tool first to resolve that string into an ID, then pass the ID here. DO NOT guess IDs. DO NOT pass the human-readable name directly. DO NOT silently drop the filter — always resolve it. Filter → required discovery tool (ALWAYS call the discovery tool first when the user gives you a name instead of an ID): • department → list_departments (IDs look like cudmnt_...) • location → list_locations (IDs look like culoc_...) • cost_center → list_cost_centers (IDs look like cc_...) • entity → list_legal_entities (IDs look like le_...) • title → list_titles (IDs look like ti_...) • role → list_roles with role_type=["FUNCTIONAL"] (IDs look like role_... or aurl_...) • access → list_roles with role_type=["ACCESS"] (IDs look like role_... or aurl_...) • manager → list_users_by_name_or_email (IDs look like cuuser_...) Decision rule: if the user says a NAME, call the discovery tool; pass only the returned ID(s) into list_users. If the user already supplied an ID with the expected prefix, skip discovery and pass it straight through. ✅ Pagination & sorting (no discovery needed): - cursor: Pagination cursor returned from a previous response. - limit: Number of users to return (1-100, default: 10). - direction / sort: Sort direction ("asc"/"desc") and field (FIRST_NAME, LAST_NAME, EMAIL, ...). ✅ Enum filters (values listed inline — no discovery tool needed): - status: Array of UserStatus (INVITED, PENDING_ACTIVATION, ACTIVE, INACTIVE, DISABLED, ARCHIVED). Defaults to [INVITED, PENDING_ACTIVATION, ACTIVE, INACTIVE] if omitted. - admin_role: Single value of "ADMINS" | "NON_ADMINS" | "ALL". - include: Optional token phone_number. Unrequested fields are null. brex_assistant is not available on list. ⚠️ When combining filters (e.g. "active users in Finance"), you MUST still resolve every name-based filter via its discovery tool AND include all requested filters in the final list_users call. Never drop a filter just because another filter was already applied. Example Input 1 (basic, with defaults): {} Example Input 2 (filter by department and role — department and role were resolved via list_departments and list_roles first): { "department": ["cudmnt_1234"], "role": ["role_5678"], "status": ["ACTIVE"] } Example Input 3 (sort descending by last name, paginated): { "limit": 50, "direction": "desc", "sort": "LAST_NAME", "cursor": "cursor_1234" } Example Output: { "items": [ { "id": "cuuser_123", "first_name": "John", "last_name": "Doe", "email": "[email protected]", "role": "CARD_ADMIN", "status": "ACTIVE" } ], "next_cursor": "cursor_5678" }
list_users
List all available GL accounts for the active accounting integration. This tool executes a 3-step process: 1. Get the active accounting integration ID 2. Use the integration ID to get the extended field ID for GL accounts 3. Use the extended field ID to get all GL account options Returns an object containing: - accounts: Array of GL accounts - glAccountField: The extended field definition for GL accounts - gl_account_type: Nullable GL account type on each account when the ERP provides it Returns null if no active integration is found. Example response: { "glAccountField": { "id": "extended_field_cm9vkatzo0b8e0i36rxhz7z4r", "key": "user_category_int_cm9vkas3n00zx0e00zsk25h7z", "name": "GL Account", "status": "ACTIVE" }, "accounts": [ { "id": "efo_cm9vkauq30b9h0i36k2r3p66f", "identifier": "1328446901", "value": "1010 Cash", "status": "ACTIVE", "gl_account_type": "ASSET" }, { "id": "efo_cm9vkauq30b9i0i36pkb9lzan", "identifier": "-1076004205", "value": "1020 Accounts Receivable", "status": "ACTIVE", "gl_account_type": null } ] }
list_gl_accounts
List accounting records with filters and pagination. This tool returns accounting records with the full record shape, including amounts, source information, users, vendors, receipts, and line items. Supported filters: - ids: specific accounting record IDs - review_status: workflow stage for CARD and REIMBURSEMENT records - source_type: high-level source filter such as CARD, REIMBURSEMENT, or BILL - updated_at: gt/gte/lt/lte timestamp filters for polling - erp_posting_date: inclusive from/to timestamp filters for ERP posting date (accruedAt) range - timezone: IANA timezone for interpreting date-only and local datetime filters; defaults to UTC - single_entry: return single-entry line items instead of the default double-entry view - cursor and limit: pagination controls Date filters accept date-only strings (e.g., "2025-05-06"), local datetimes (e.g., "2025-05-06T14:00:00"), or UTC/offset datetimes. Date-only and local datetime values are converted to UTC using timezone. Always tell the user which timezone was used when presenting date-filtered results. Constraint: - review_status is not supported with source_type=BILL
list_accounting_records
List cards with comprehensive filtering. Use this for both personal card queries and company-wide card management. 🔍 IMPORTANT - How to scope card queries with card_holder: Personal queries (use card_holder: "ME"): - "my cards", "my locked cards", "do my cards have..." - "which of my cards is active" - "show me my cards" → SET card_holder: "ME" to return only the calling user's cards IMPORTANT DEFAULT BEHAVIOR: When the query is ambiguous (no explicit "company/all/team" scope), default to card_holder: "ME" since users typically want their own cards. Company-wide queries (omit card_holder): - "all company cards", "list all cards", "company card inventory" → OMIT card_holder to return all cards across the organization (admin only) Specific user queries (use user_ids): - "John's cards", "show Alice's cards" → First call list_users_by_name_or_email to get user ID, then pass user_ids parameter Pagination: - limit: max cards per page (1-100, default 10); a higher value is clamped to 100 - cursor: pass next_cursor from the previous response - To retrieve the full set, continue until next_cursor is null. An account holds far more cards than fit on one page, so a single page is not the account's card list — never answer "how many", "list every", or any question that needs the whole set from one page. Attributing a returned card to a person (e.g. "which are Alex's cards?"): rely on each card's `user` field — the current, authoritative cardholder. Do NOT use holder_name (the *original* holder's name, which can be stale if the card was reassigned) or display_name (just the card's label/nickname, e.g. "Salesforce"). On any mismatch, trust `user`. IMPORTANT: card_holder and user_ids are mutually exclusive. Use card_holder for self-scoping or user_ids for specific user IDs, but not both. Filtering by card type (card_category): - To list P-Cards, pass card_category: ["PURCHASING_CARD"]. To check if a card exists for a certain purpose, do NOT set search_query — fetch the broad list and judge contextual relevance yourself. - To check whether a specific user already has an employee card, pass user_ids: [their_id] together with card_category: ["EMPLOYEE_CARD"]. EMPLOYEE_CARD covers both funded and $0-limit cards — read the returned card's limit to tell them apart. Filtering by card form (card_form): - Pass card_form: ["VIRTUAL"] to scope results to virtual cards. Sorting: - Pass sort with direction for server-side ordering. Use sort: "ISSUED_AT", direction: "desc", limit: 1 to get the most recently issued matching card. Telling shared cards apart: - A card is shared when shared_card_details is present on it, and not shared when the field is absent. Use that to answer questions like "how many of these are shared?" without a follow-up call per card. - Results are not limited to cards the queried user holds or can spend on. Admins can list every card in the account, and managers can list their reports' cards. Use each card's `user` field to see who actually holds a card. - shared_card_details.is_authorized_user is positive evidence only. True means the queried user is an authorized user on that shared card. False means only that they are NOT — it does not tell you they are the cardholder, and does not explain why the card is visible to them. Never read false as "this card is theirs". - On list results member_count is null and authorized_users is empty — that means "not fetched here", NOT "shared with nobody". Call get_card_by_id on a specific card to see who actually has access. Example 1: Getting all my active cards: { "card_holder": "ME", "status": ["ACTIVE"] } Example 2: Getting all my cards (any status): { "card_holder": "ME" } Example 3: Getting all active cards (admin): { "status": ["ACTIVE"] } Example 4: Getting all active and locked cards for specific users: { "status": ["ACTIVE", "LOCKED"], "user_ids": ["user_123"] } Example 5: Getting cards for a specific user: { "user_ids": ["user_123"] } Example 6: Listing the company's purchasing cards: { "card_category": ["PURCHASING_CARD"] } Example 7: Checking whether a specific user already has an employee card: { "user_ids": ["cuuser_abc123"], "card_category": ["EMPLOYEE_CARD"] } Example 8: Getting a user's most recently issued physical card: { "user_ids": ["cuuser_abc123"], "card_form": ["PHYSICAL"], "sort": "ISSUED_AT", "direction": "desc", "limit": 1 } GLOBAL USER-FACING RULES — apply to EVERY message you send the operator. Everything in this description is internal contract. It is here so you can reason and build a correct call; none of it belongs in a reply. - Never show wire values. Statuses, reasons, card categories, card forms, reset frequencies and limit flexibilities are internal codes: say "locked", "cancelled", "a virtual card", "monthly", "up to 10% over the limit". - Never show request or response FIELD NAMES, and never use `field: value` form. Describe what the operator will observe, in prose. - Never show internal IDs (card, user, policy, limit). Name objects by display name only, e.g. "Tony Report", "Default Policy". - Never name tools or internal steps: no "list_cards", "duplicate check", "preflight", "tool call". - Never volunteer how Brex records a card action internally. The spend limits, budgets and other bookkeeping behind locking, cancelling, transferring or issuing a card are implementation, not product: describe only what the operator will observe. Spend limits ARE a product the operator can be offered in their own right — the rule is about not explaining a card action in terms of them. - Any examples below use wire vocabulary deliberately, because they show the shape of a call. Copy their structure, never their vocabulary.
list_cards
List cost centers (id + display name). REQUIRED prerequisite for the list_users `cost_center` filter: list_users only accepts cost center IDs, so whenever the user mentions a cost center by name (e.g. "R&D", "Sales"), call this tool first and pass the returned `id` into list_users. Parameters: limit (1-100, default 10), cursor (pagination), search_text (narrow by name). Example Output: { "items": [{ "id": "cc_1234", "name": "Engineering" }], "next_cursor": "cursor_abc" }
list_cost_centers
List departments (id + name). REQUIRED prerequisite for the list_users `department` filter: list_users only accepts department IDs, so whenever the user mentions a department by name (e.g. "Engineering", "Finance"), call this tool first and pass the returned `id` into list_users. Parameters: limit (1-100, default 10), cursor (pagination), search_text (narrow by name). Example Output: { "items": [{ "id": "cudmnt_1234", "name": "Engineering" }], "next_cursor": "cursor_abc" }
list_departments
List expenses with comprehensive filtering, OR aggregate them by group. Use this for personal expense queries, company-wide financial analysis, AND group-by/aggregation analytics ("total spend by vendor", "monthly burn", "top departments by spend", "expense count by status"). PREFER THIS TOOL for any expense question that can be answered with filter + group_by + SUM/COUNT. That covers: "total spend by X", "top N by spend", "count of expenses by X", "monthly/weekly/daily breakdowns", "spend per vendor/department/category/user/budget/expense_type". Reserve query_expense_analytics for higher-order analytics (anomaly detection, trends quarter-over-quarter, forecasting, recommendations, vendor benchmarking) — use it only when the question genuinely requires those capabilities. Two modes — same filters, different output: - LISTING mode (default): omit aggregations → returns a paginated list of expenses - AGGREGATION mode: supply aggregations (with or without group_by) → returns one row per group with computed totals/counts, or a single row when group_by is omitted. Use this for any "how many", "what's the total", "by", "per", "sum", "count", or "breakdown" question. SINGLE-NUMBER QUESTIONS ("how many", "what's the total") — use AGGREGATION mode with no group_by: For any question asking for ONE number (no breakdown), supply aggregations and OMIT group_by. The result is a one-row aggregation envelope keyed by your aliases. - "How many expenses missing receipts?" → receipt_status:"RECEIPT_ABSENT", aggregations:[{func:"COUNT", field:"all", alias:"missing_receipts"}] - "How many submitted card expenses?" → types:["CARD"], approval_statuses:["SUBMITTED"], aggregations:[{func:"COUNT", field:"all", alias:"submitted_count"}] - "How many expenses missing memo OR missing receipt?" → documentation_missing_any:["MEMO","RECEIPT"], aggregations:[{func:"COUNT", field:"all", alias:"missing_docs"}] - "What's our total card spend this quarter?" → types:["CARD"], purchased_at_start/end for the quarter, aggregations:[{func:"SUM", field:"billing_amount", alias:"total_spend"}] - "What's the total amount of CLEARED transactions in May?" → payment_statuses:["CLEARED"], purchased_at_start/end for May, aggregations:[{func:"SUM", field:"billing_amount", alias:"total"}] - SUM returns a multi-currency array (e.g. ["1234.00 USD","56.78 CAD"]) when expenses span multiple currencies — present each bucket separately or convert client-side. BREAKDOWN QUESTIONS ("by", "per", "top N") — use AGGREGATION mode with group_by: - "Total spend by vendor this quarter" → group_by:["vendor"], aggregations:[{func:"SUM", field:"billing_amount", alias:"total_spend"}] - "List each vendor with its spend" / "spend for every vendor" / "vendors ranked by spend" → group_by:["vendor"], aggregations:[{func:"SUM", field:"billing_amount", alias:"total_spend"}]. Use THIS tool (not list_vendors — that only lists vendor records and has no spend) whenever the ask pairs vendors/departments/categories/users with an amount or count. - "Monthly card burn this year" → types:["CARD"], group_by:["month"], aggregations:[{func:"SUM", field:"billing_amount", alias:"total_spend"}] - "Top 10 departments by spend" → group_by:["department"], aggregations:[{func:"SUM", field:"billing_amount", alias:"total_spend"}], limit:10 - "How many expenses are missing receipts, BY USER?" → receipt_status:"RECEIPT_ABSENT", group_by:["user_first_name","user_last_name"], aggregations:[{func:"COUNT", field:"all", alias:"expense_count"}] - "Expense counts by category" → group_by:["category"], aggregations:[{func:"COUNT", field:"all", alias:"expense_count"}] RANKING / ORDER (important for "top N by spend"): - When you pass sort_by, rows come back already sorted — present them in that order. If ranked_by.basis is set, the ranking is a USD-normalized total that nets multiple currencies together, so the per-currency amounts shown are NOT the sort key — never re-order by a single currency's figure (it produces a wrong order). EXTREMA — a SINGLE most/least expensive expense (not a total/breakdown): use LISTING mode with order_by:"amount", order_direction DESC (most) or ASC (least), limit:1. Do NOT use the default recency sort — it returns the most RECENT expense, not the highest-dollar one. order_by:"amount" ranks by the USD-normalized amount shown per row, so it's correct across mixed currencies. CUSTOM AMOUNT BUCKETS (e.g. "<$25, $25-$75, $75-$250, ..."): For numeric range bucketing, make N targeted calls with min_amount/max_amount filters per bucket (each with COUNT aggregation, no group_by) and combine the results client-side. Counting expenses: - For a single count, use AGG mode + aggregations:[{func:"COUNT", field:"all"}] with no group_by. - For a count broken down by a dimension, use that dimension as group_by with the same COUNT aggregation. - receipt_status / memo_status are FILTERS, not group keys. To answer "how many missing receipts", set receipt_status:"RECEIPT_ABSENT" as a filter and aggregate with COUNT (or pair with group_by:["user_first_name","user_last_name"] for a per-user breakdown). Time bucketing: - Supported granularities: day, week, month, quarter, year (based on purchased_at). - Time buckets honor the timezone parameter: bucket boundaries align to the caller's local calendar (defaults to UTC when timezone is omitted), consistent with the tz-aware date range filters. - For yearly totals across multiple years, group by "year". For a single year's total, omit group_by entirely with a year-bounded purchased_at_start/end and a SUM aggregation. Naming note: "merchant_category" and "category" are interchangeable group keys — both group on vendor category name. Use either. DECLINED / FAILED CARD AUTHORIZATIONS ("why was my card declined?"): Failed authorization attempts ARE returned by this tool -- they are expenses whose payment_status is DECLINED (authorization refused) or CANCELED (authorization reversed or expired without clearing). A merchant retrying a charge produces several such records followed by a CLEARED one. To investigate, use LISTING mode with payment_statuses: ["DECLINED", "CANCELED"] plus whatever narrows it down (merchant/vendor, purchased_at_start/end, card, user). Each row's payment_status tells you the outcome, and payment_status_label carries the dashboard's own wording for it -- prefer that over paraphrasing the enum (CREDITED is shown as "Chargeback", and DECLINED is "Declined" on a card but "Failed" elsewhere). Then, for each expense you want explained, call get_expense_by_id with its id to read payment_status_reason -- the actual decline reason (EXCEEDED_CARD_LIMIT, MCC_BLOCKED, CARD_SUSPENDED, SUSPECTED_FRAUD, ...). This tool does NOT return payment_status_reason at all, because resolving it costs one extra lookup per expense; never report "declined for an unknown reason" from a list result. IMPORTANT - For large LISTING results (50+ expenses): consider start_expense_download for CSV export instead. Aggregation mode collapses results to one row per group, so it's the right answer when the user wants totals rather than individual transactions. IMPORTANT - How to scope expense queries with expense_owner: 🔍 The primary users of this tool are admins and finance operators, so the DEFAULT SCOPE is company-wide. Only narrow to the caller when the user explicitly says so. IMPORTANT DEFAULT BEHAVIOR: When the query is ambiguous or phrased generically (no possessive "my"/"mine" and no subject "I"), OMIT expense_owner. This returns all expenses across the organization, which is what admins asking questions like "show expenses over $500", "find expenses missing receipts", "which expenses are declined" expect. ⚠️ "show me X" is NOT a personal-scope signal. The "me" is the indirect object of "show", not a claim of ownership. Only the possessive "my"/"mine" or the subject "I" indicates personal scope. Personal queries (use expense_owner: "ME") apply only when the user explicitly refers to themselves: - Possessive: "my expenses", "my receipts", "my reimbursements", "my card transactions", "which of my expenses..." - Subject "I": "how much have I spent", "what did I spend on...", "am I spending more on..." - Set expense_owner: "ME" only when the user message contains "my", "mine", or "I" as a subject referring to the caller. - Treat as company-wide: "show expenses", "find expenses", "list expenses", "which expenses", "show me expenses", "expenses over $X", "expenses from [merchant]" Company-wide queries (OMIT expense_owner — this is the default): - Any request without a first-person pronoun: "show expenses", "find expenses missing receipts", "which expenses are flagged", "list card expenses over $500" - Explicit company language: "our burn rate", "we spent", "company expenses", "overall spending", "analyze all card transactions", "total reimbursements" → OMIT expense_owner to return all expenses across the organization Team queries (use expense_owner: "DIRECT_REPORTS" or "ALL_REPORTS"): - "my team's expenses", "direct reports' spending" - "expenses from all my reports" → Use DIRECT_REPORTS for immediate reports only, or ALL_REPORTS for nested reports Specific user queries (use user_ids): - "show John's expenses", "Alice's reimbursements" → First call list_users_by_name_or_email to get user ID, then pass user_ids parameter IMPORTANT - Location and travel data: 🌍 Location and travel data are ALWAYS null by default unless you explicitly request them via include. When user asks about WHERE an expense occurred (location, city, country, address): - Query asks: "Where was that expense?", "What city was this from?", "Show me the location" → You MUST pass include: ["location"] When user asks about travel details (flights, hotels, car rentals): - Query asks: "Was this a flight?", "Show me travel expenses", "Which hotel?" → You MUST pass include: ["travel_metadata"] When user asks about booking status or trip cancellation (e.g. was a travel booking canceled?): → You MUST pass include: ["travel_booking"] ⚠️ A null location does NOT mean "no location exists" - it means you did not request it in include. **IMPORTANT - Bills (BILLPAY) in expenses vs list_bills tool:** Bills appear in this tool as expenses with type=BILLPAY, but with LESS comprehensive detail than the dedicated list_bills tool. Use this tool when bills are just ONE expense type in a broader analysis: - Overall spending: "total expenses this month", "all expenses over $500" - Mixed queries: "show card and bill expenses", "compare reimbursements to bills" - General patterns without bill-specific details Note: Bills here have minimal vendor details and NO due date information Use list_bills tool when the user needs BILL-SPECIFIC information: - Due dates, overdue status: "overdue bills", "bills due this week" - Vendor details: "bills from vendor X", "how much do we owe" - Payment schedules and comprehensive bill metadata See list_bills tool description for full bill-specific filtering capabilities. Expense types: BILLPAY, CARD, CLAWBACK, REIMBURSEMENT **Filter Usage Best Practices:** - For CARD expense queries, always include date filters (purchased_at_start/purchased_at_end) for reliable results - When using min_amount/max_amount filters, combine with date filters for better performance **Timezone:** - timezone: IANA timezone string (e.g., "America/Los_Angeles", "America/New_York", "Europe/London", "Asia/Tokyo"). Pass the user's timezone from your system context if available. If not provided, defaults to UTC. IMPORTANT - Transparency: When presenting results, ALWAYS tell the user which timezone was used for the query: - If timezone was provided: "Based on your Pacific Time zone, here are expenses from May 6, 2026..." - If timezone was NOT provided (UTC default): "Note: dates are interpreted in UTC. If you'd like results in your local timezone, let me know your timezone." Why this matters: When a user says "show this week's expenses", they mean their local week. The timezone parameter ensures date filters match the user's local calendar days. Without it, dates are treated as UTC which may not match the user's intent. Date Filtering (only one type can be used at a time): IMPORTANT - Default to purchased_at for ALL time-based queries. The purchased_at filter covers when the transaction actually occurred and is the correct filter for general date queries like "this week", "last month", "recent", "posted this week", etc. Only use posted_at when the user explicitly needs the bank settlement/accounting posting date for reconciliation purposes. - Purchase Date (DEFAULT): purchased_at_start/purchased_at_end - Filter by when expense was purchased/transacted. Use this for all general date queries. Pass dates/datetimes in the user's local timezone (will be converted to UTC automatically). - Posted Date (accounting only): posted_at_start/posted_at_end - Filter by bank settlement/posting date. Only use when user specifically needs accounting posting dates. Pass dates/datetimes in the user's local timezone (will be converted to UTC automatically). - Reimbursement Submission Date: reimbursement_submitted_at_start/reimbursement_submitted_at_end - Filter by when reimbursement was submitted (only works with REIMBURSEMENT type). Pass dates/datetimes in the user's local timezone (will be converted to UTC automatically). - Assigned Date: assigned_at_start/assigned_at_end - Filter by when expense was assigned for review. Pass dates/datetimes in the user's local timezone (will be converted to UTC automatically). Status Filters: - approval_statuses: Filter by approval/review status. Commonly used: APPROVED, SUBMITTED, OUT_OF_POLICY, CANCELED (the enum also accepts internal states DRAFT, SETTLED, SPLIT, VOID). - pending_approvals: FROM_ME (pending your approval) or FROM_OTHERS (pending another approval). - payment_statuses: Payment lifecycle state (NOT_STARTED, SCHEDULED, PROCESSING, CANCELED, CLEARED, DECLINED, REFUNDING, REFUNDED, CASH_ADVANCE, CREDITED, AWAITING_PAYMENT). - dispute_statuses: Commonly used: DISPUTE_STATUS_IN_PROGRESS, DISPUTE_STATUS_CLOSED (also accepts DISPUTE_STATUS_CANCELLED, DISPUTE_STATUS_COMPLETE). - reimbursement_export_statuses: EXPORTED or NOT_EXPORTED (reimbursement export state). - user_status: Filter by spender's user status. Commonly used: ACTIVE, DELETED, DISABLED (also accepts INACTIVE, INVITED, PENDING_ACTIVATION). Compliance Filters (matches dashboard labels): - compliance_statuses: Overall compliance status (DOCUMENTATION_DUE, REVIEW_DUE, COMPLETED). Matches the 'Compliance status' filter in the Brex dashboard. - documentation_statuses: Documentation deadline status (DUE, OVERDUE, COMPLETED). Matches the 'Documentation status' filter in the Brex dashboard. Use [DUE, OVERDUE] to find expenses missing required documentation. - receipt_status: RECEIPT_PRESENT or RECEIPT_ABSENT. - memo_status: MEMO_PRESENT or MEMO_ABSENT. - require_review_reasons: MEALS, CAR_RENTAL, FLIGHTS, LODGING, TRAINS, MILEAGE, MERCHANT_OR_CATEGORY, AMOUNT, OTHERS. - government_attendees_status: GOVERNMENT_OFFICIAL_PRESENT or GOVERNMENT_OFFICIAL_ABSENT. Field notes: - ID filters (department_ids, card_ids, vendor_ids, merchant_ids, merchant_category_ids, expense_category_ids, expense_policy_ids, trip_ids, approver_user_ids, next_approver_user_ids, limit_ids, user_ids) accept IDs only, never names. Resolve names to IDs first with the matching list_* tool (e.g. list_departments, list_vendors, list_merchants, list_merchant_categories, list_expense_categories, list_active_and_upcoming_travel_trips, list_users_by_name_or_email, list_my_limits). - ERP debit GL account filter (send both parts together): (1) call list_gl_accounts, (2) copy gl_account_field.key into erp_debit_gl_account_field_key, (3) copy one or more accounts[].identifier values (NOT id, NOT value) into erp_debit_gl_account_option_ids. The key is dynamic per accounting integration (shape "custom_gl_account_<uuid>") — never hardcode or guess it. Sending only one of the pair returns a 400. - group_by dimensions: vendor, vendor_id, category, category_id, merchant_category, merchant_category_id, department, budget, expense_type, expense_status, payment_status, user_first_name, user_last_name, user_id, cost_center. Time buckets: day, week, month, quarter, year. group_by requires at least one aggregation; receipt_status and memo_status are filters, not group keys. Use *_id group keys only when chaining the result back into a filter; otherwise prefer the human-readable name variant. - aggregations: each entry is { func: SUM | COUNT, field, alias }. SUM is meaningful only on amount fields (billing_amount = USD spend, original_amount = transaction currency, customer_invariant_amount = single comparable currency). COUNT can target any field — use field:"all" for count of expenses. The alias becomes the result-row key. All dates should be in ISO 8601 format. Results are paginated - use limit and cursor for pagination.
list_expenses
List legal entities (id, display name, verification status). Deleted entities are excluded. REQUIRED prerequisite for the list_users `entity` filter: whenever the user mentions a legal entity by name (e.g. "Brex Inc.", "Brex UK Ltd."), call this tool first and pass the returned `id` — never invent one. is_verified reflects KYC completion; it does not affect whether the entity can be used with the list_users entity filter. Parameters: limit (1-100, default 10), cursor (pagination), search_text (narrow by display name). Example Output: { "items": [{ "id": "le_1234", "name": "Brex Inc.", "is_verified": true }], "next_cursor": "cursor_abc" }
list_legal_entities
List locations (id + name). Deleted locations are excluded. REQUIRED prerequisite for the list_users `location` filter: list_users only accepts location IDs, so whenever the user mentions a location by name (e.g. "San Francisco", "New York"), call this tool first and pass the returned `id` into list_users. Parameters: limit (1-100, default 10), cursor (pagination), search_text (narrow by name). Example Output: { "items": [{ "id": "culoc_1234", "name": "San Francisco" }], "next_cursor": "cursor_abc" }
list_locations
Get spend LIMITS for the current user — amounts, balances, names, and IDs. This is NOT a policy catalog and does NOT return policy rules. TERMINOLOGY: On Brex, these are called "limits" — NOT "budgets." There are two kinds: card limits (built into a card) and spend limits (exist independently). "Budget" is a separate Premium-only planning/tracking feature. When users say "budget" they almost always mean "limit." Prefer "limit" in responses unless the user is specifically asking about the Budget feature. The API returns fields named "budget_*" but these should be presented as "limits" to users. **WHEN TO USE:** - "My limits", spending limits, balances, remaining amount, limit names/IDs - Resolving a limit name to an ID for list_expenses, list_cards, or get_expense_policy - FIRST step before get_expense_policy when user asks policy RULES tied to their spending (receipts, approvals, "can I expense X on my card?") **WHEN NOT TO USE — pick a different tool instead:** - User already has a spend_limit_id and wants full policy rules → get_expense_policy directly **Routing (policies vs limits):** | User wants | Tool | | --- | --- | | My limit balances / limit names & IDs | list_my_limits (this tool) | | Policy rules on a limit (receipts, approvals) | list_my_limits → get_expense_policy | DO NOT use list_users_by_name_or_email when the user asks about their own limits, even if the limit name contains a person's name. For policy-RULE questions (not catalog): call this tool first to get spend_limit_id, then get_expense_policy. If only one active limit, use it automatically. This includes: - User Limit: personal spending limit for the user - Spend Limits: shared limits where the user is a member Each limit shows: - Limit amount and remaining balance - Period type (MONTHLY, QUARTERLY, etc.) - Status (ACTIVE, EXPIRED, etc.) - Start and end dates - Amount spent in current period Pagination: - 'limit': how many to return (1-100, default 10) - 'cursor': pass next_cursor from the previous response - To retrieve the full set, continue until next_cursor is null SCENARIO-BASED EXAMPLES: Example 1: Get the first page of my limits (default - returns up to 10 limits): {} Example 2: Request 10 limits explicitly: { "limit": 10 } Example 3: Get next page of limits: { "limit": 10, "cursor": "eyJhZnRlciI6IjEwIn0=" } TOOL CHAINING WORKFLOWS: Scenario 1: "How much is left on my Marketing Q1 limit?" → Call list_my_limits with {}; follow next_cursor as needed to find the limit → Find limit where name="Marketing Q1", extract ID (e.g., "spl_abc123") → Present the available balance from the "available" field Scenario 2: "Show me all expenses on my Travel & Entertainment limit" → Call list_my_limits with {}; follow next_cursor as needed to find the limit → Find limit where name contains "Travel", extract ID (e.g., "spl_xyz789") → Call list_my_expenses with: { "limit_ids": ["spl_xyz789"] } Scenario 3: "Can I expense Uber Eats on my company card?" (policy RULES — not catalog) → Call list_my_limits with {}; follow next_cursor as needed to inspect active limits → If user has only one active limit, extract its ID (e.g., "spl_policy123") → Call get_expense_policy with: { "spend_limit_id": "spl_policy123" } → Check policy rules for Uber Eats / Food Delivery merchant restrictions Scenario 4: "Do I need approval for a $200 dinner?" (policy RULES) → Call list_my_limits with {}; follow next_cursor as needed to get the relevant limit ID → Extract relevant limit ID (e.g., "spl_corp456") → Call get_expense_policy with: { "spend_limit_id": "spl_corp456" } → Check policy rules for approval thresholds on meal expenses Scenario 5: "What did I spend on my Q1 Marketing limit last month?" → Call list_my_limits with {}; follow next_cursor as needed to find "Q1 Marketing" → Extract limit ID (e.g., "spl_mkt789") → Call list_my_expenses with: { "limit_ids": ["spl_mkt789"], "purchased_at_start": "2025-03-01T00:00:00.000Z", "purchased_at_end": "2025-03-31T23:59:59.999Z" } Scenario 6: "What are the rules for my Travel limit?" (policy RULES on named limit) → Call list_my_limits with {}; follow next_cursor as needed to find "Travel" → Extract the Travel limit ID (e.g., "spl_travel101") → Call get_expense_policy with: { "spend_limit_id": "spl_travel101" } → Present the complete policy rules from the response
list_my_limits
Lists spend limits available to the caller, filterable by name, status, eligible policy workflow, or owner. For ordinary lookups, callers with account-wide budget access can list every spend limit. Other callers receive limits they own directly or through an ancestor budget. For Bill Pay drafting, set status=ACTIVE and eligible_workflow=BILLPAY_BILLS. Callers with account-wide budget access and AP clerks receive every matching limit; other callers receive matching limits where they are a member. Bill creation permission alone does not widen discovery. Spend limits and budgets are distinct. budget_path contains the visible containing budgets, ordered from the immediate parent to the outermost ancestor. Use this operation without a workflow filter to discover managed limits. Use the Bill Pay filters above to discover limits available for a draft bill. It returns compact summaries with name, status, budget_path, and an opaque ID for follow-up operations. In user-facing responses, identify spend limits and containing budgets by name. Use IDs silently for operation chaining; do not display them unless the user explicitly asks for IDs. Disambiguate duplicate names with status and budget_path before using IDs. ROUTING EXAMPLES: - "Which spend limits can I manage?" → use this operation - "Which spend limit can I use for this draft bill?" → use this operation with status=ACTIVE and eligible_workflow=BILLPAY_BILLS - "Find a Bill Pay spend limit named Marketing" → use this operation with search="Marketing", status=ACTIVE, and eligible_workflow=BILLPAY_BILLS - "What limits can I spend from?" → use list_my_limits - "What are everyone's employee card limits?" → use list_employee_card_limits FILTERS: narrow by name, status, eligible policy workflow, or a single owner. Omit filters to return managed limits of every status. Callers without account-wide budget access may omit owner_user_id or pass only their own user ID. For account-wide requests that name someone, resolve the person with list_users_by_name_or_email first and filter by the user it returns. Bill Pay drafting must always use both status=ACTIVE and eligible_workflow=BILLPAY_BILLS and should omit owner_user_id so the service applies the caller's Bill Pay scope.
list_spend_limits
List employee titles (id + display title). REQUIRED prerequisite for the list_users `title` filter: list_users only accepts title IDs, so whenever the user mentions a title by name (e.g. "Software Engineer", "Product Manager"), call this tool first and pass the returned `id` into list_users. Parameters: limit (1-100, default 10), cursor (pagination), search_text (narrow by title name). Example Output: { "items": [{ "id": "ti_1234", "name": "Software Engineer" }], "next_cursor": "cursor_abc" }
list_titles
List travel bookings across the company with filtering, sorting, and pagination. Mirrors the Bookings page on the Brex dashboard (Travel → Company → Bookings). Use this tool when the user asks specifically about individual bookings (flights, hotels, car rentals) rather than whole trips. Results are always sorted by booking start date descending. Filters are grouped to match the dashboard: General: - booking_start_date_on_or_after / booking_start_date_on_or_before: Booking start date. Date (YYYY-MM-DD) bounding the booking start date (local). - booking_types: Booking type. Array of booking categories (AIR, CAR_RENTAL, LIMO, LODGING, MISC, RAIL). People: - booker_user_ids: Booker. Array of booker (employee) user IDs (cuuser_*) to filter by specific bookers. - traveler_user_ids: Traveler. Array of traveler user IDs (cuuser_*) to filter by traveler. - traveler_guest_emails: Traveler. Array of guest traveler emails to filter by traveler. - traveler_types: Traveler type. Array of ["EMPLOYEE", "GUEST"] to narrow by traveler type. Status: - booking_statuses: Booking status. Array of booking statuses (CONFIRMED, PENDING, CANCELED, etc.). - approval_statuses: Approval status. Array of approval statuses (APPROVED, PENDING, REJECTED). - policy_statuses: Policy status. Array (IN_POLICY, OUT_OF_POLICY). Pagination: - limit: Max items per page (1-100, default 10). - cursor: Cursor from a previous response's next_cursor. Example: { "booking_types": ["AIR"], "booking_statuses": ["CONFIRMED"] }
list_bookings
List travel trips across the company with filtering, sorting, and pagination. Mirrors the Trips page on the Brex dashboard (Travel → Company → Trips). Use this tool when the user asks for a company-wide view of trips (e.g. "show all pending trips", "trips booked for Jane next month"). Filters are grouped to match the dashboard: General: - trip_statuses: Trip status. Array of trip statuses to include (e.g. ["UPCOMING", "PENDING"]). When omitted, trips in DRAFT, DELETED, or VOIDED states are excluded by default. To surface those, set trip_statuses explicitly. - policy_status: Policy status. Filter by policy compliance across a trip's bookings (IN_POLICY, ANY_OUT_OF_POLICY, ALL_OUT_OF_POLICY). Trips without bookings are excluded when this is set. Payment: - spend_limit_ids: Spend limit. Array of spend-limit IDs to filter trips by the associated spend limit. Date: - billable_at_on_or_after / billable_at_on_or_before: Billable at. ISO-8601 timestamps bounding the trip's billable-at time. - start_date_on_or_after / start_date_on_or_before: Start date. Date (YYYY-MM-DD) bounding the trip start date. People: - booker_scope: ALL_USERS, SELECTED_USERS, or SELF. Defaults to SELF on the GraphQL layer when omitted. - booker_user_ids: Booker. Array of booker (employee) user IDs (cuuser_*). Required when booker_scope is SELECTED_USERS. - traveler_user_ids: Traveler. Array of traveler user IDs (cuuser_*) for employees to filter by traveler. - traveler_guest_emails: Traveler. Array of guest traveler emails. - traveler_types: Traveler type. Array of ["EMPLOYEE", "GUEST"] to narrow by traveler type. Sorting: - order_field: Field to sort by (START_DATE, BILLABLE_AT, INSERTED_AT). - order_direction: ASCENDING or DESCENDING. Defaults to DESCENDING. Pagination: - limit: Max items per page (1-100, default 10). - cursor: Cursor from a previous response's next_cursor. Example: { "trip_statuses": ["UPCOMING", "PENDING"], "order_field": "START_DATE", "order_direction": "DESCENDING", "limit": 25 }
list_trips
List users by name or email This tool allows you to search for users by name or email. It will return a paginated list of up to 10 users that match the search criteria, sorted by first name. If you only have the name or email, you can use this tool to find the user ID and their full details. - include: Optional token phone_number. Unrequested fields are null. brex_assistant is not available on list. Example Input 1 (search by name): { "search_text": "John Smith" } Example Input 2 (search by email): { "search_text": "[email protected]" } Example Output: { "items": [ { "id": "cuuser_123", "first_name": "John", "last_name": "Smith", "email": "[email protected]", "role": "CARD_ADMIN", "status": "ACTIVE" } ], "next_cursor": "cursor_1234" }
list_users_by_name_or_email
Get all vendors for the current authenticated user. Results are paginated - use limit and cursor for pagination. IMPORTANT: Always use search_text to filter results when users ask about specific vendors, categories, or contact information. This tool is designed for efficient searching and filtering. Parameters: - cursor: Pagination cursor for next page of results - limit: Max items per page (1-100, default 10) - search_text: Input used for trigram-based fuzzy matching of vendor names - status: Optional single vendor status to filter by (ACTIVE, DELETED, PENDING, DRAFT, DECLINED, or MERGED) * If omitted: Returns only ACTIVE vendors (default backend behavior) * If empty array []: Returns vendors of ALL statuses * If specified: Returns only vendors with that single status * NOTE: Only ONE status can be filtered at a time Search capabilities: - Text search: Fuzzy matches vendor names using trigram word similarity - Status filtering: Filter by a single vendor status at a time - Combined filtering: Use both search_text and status together for precise results Example usage: - "Show me all my vendors" (only ACTIVE by default): { "limit": 100 } - "Show me ALL my vendors regardless of status": { "status": [], "limit": 100 } - Search for a specific vendor by name across ALL statuses: { "search_text": "Acme", "status": [], "limit": 25 } - Find active software vendors: { "search_text": "Software", "status": ["ACTIVE"], "limit": 100 } - Search by vendor name (ACTIVE by default): { "search_text": "Corp", "limit": 10 }
list_vendors
Update attendees for a card expense. This tool allows you to update both external and internal attendees to any card expense. Note: This tool will replace the existing attendees with the new attendees provided. Parameters: - card_expense_id: The ID of the card expense to add attendees to - external_attendees: Array of external attendees (people outside the company with different email domains) - internal_attendees: Array of internal attendee IDs (company employees by customer user ID) Internal vs External Attendees: - Internal attendees: Company employees with the SAME email domain as the current user (e.g., @acmecorp.com) → These require a user ID lookup using list_users_by_name_or_email - External attendees: People from OTHER companies with DIFFERENT email domains (e.g., @vendorco.com) → These can be added directly with just their name, title, and company info (no user ID needed) Example workflow - Adding both internal and external attendees: Scenario: Current user is [email protected] adding a business dinner expense with: - Jane Doe ([email protected]) - internal colleague (SAME @acmecorp.com domain) - John Smith ([email protected]) - external vendor (DIFFERENT @vendorco.com domain) Step 1: Identify which attendees are internal vs external by comparing email domains - Current user: [email protected] - [email protected] → SAME @acmecorp.com domain → internal attendee → needs user ID lookup - [email protected] → DIFFERENT @vendorco.com domain → external attendee → add directly Step 2: Find the internal user ID using list_users_by_name_or_email Input to list_users_by_name_or_email: { "search_text": "[email protected]" } Response from list_users_by_name_or_email: { "items": [ { "id": "user_456", "firstName": "Jane", "lastName": "Doe", "email": "[email protected]", ... } ], "next_cursor": null } Step 3: Add both internal and external attendees to the expense Input to replace_attendees_for_card_expense: { "card_expense_id": "card_exp_123", "external_attendees": [ { "name": "John Smith", "title": "Account Manager", "company_name": "VendorCo", "is_government_official": false } ], "internal_attendees": [ "user_456" ] }
replace_attendees_for_card_expense
Start an asynchronous expense download job. Returns a job ID immediately. IMPORTANT: Download jobs can take up to 5 minutes to complete. Use get_expense_download_result with the returned job_id to poll for completion. Recommended polling strategy: - Poll 5s after starting - If PROCESSING, wait 10 seconds and poll again - Continue with 10-second intervals until COMPLETED or FAILED - Maximum expected duration: 5 minutes Parameters: - start_date (required): Start of date range (ISO 8601, UTC) - end_date (required): End of date range (ISO 8601, UTC) - expense_types (optional): Filter by expense types: CARD, REIMBURSEMENT, BILLPAY, CLAWBACK - statuses (optional): Filter by statuses: APPROVED, CANCELED, OUT_OF_POLICY, SETTLED, SUBMITTED - user_ids (optional): Filter by specific user IDs - min_amount (optional): Minimum expense amount (USD) - max_amount (optional): Maximum expense amount (USD) The CSV includes 15 columns: Parent ID, Flagged Expenses, Transaction Date, Expense Type, Card Last 4, Amount, Currency, Original Amount, Original Currency, Merchant Name, User, Budget Name, Memo, Expense Status, Payment Status. IMPORTANT: This tool is only useful if your client environment can download files from URLs. The completed export provides a download URL for the CSV file. If you cannot download files (e.g., you are in a plain chat session without filesystem access), this tool will not help — use list_expenses with pagination instead. When to prefer this over list_expenses: - The dataset has 50+ expenses and the task involves aggregation, analysis, or bulk processing - Your environment can download files AND run scripts to process the CSV locally (e.g., pandas, awk, shell commands) - This avoids loading all expense data into the conversation context If you are unsure whether you can download files from the returned URL, ask the user before starting the export. Example: Start export for Q1 2025: { "start_date": "2025-01-01T00:00:00.000Z", "end_date": "2025-03-31T23:59:59.999Z" }
start_expense_download
Submit feedback about the Brex API or MCP tools to the Brex product team on behalf of the user. This feedback is reviewed by the product team and used to prioritize improvements. WHEN TO USE THIS TOOL: - When a tool cannot fulfill the user's request due to missing functionality or unsupported filters. Example: User asks to filter expenses by custom field, but list_expenses doesn't support that filter. -> submit_feedback with type "limitation" and describe the missing filter capability. - When list or query tools return empty or unhelpful results for a reasonable request. Example: User asks "show me spend by department last quarter" but no tool supports that aggregation. -> submit_feedback with type "limitation" describing the analytics gap. - When the user explicitly asks to share a suggestion or report a problem with Brex tools. - When a tool returns an error indicating a platform limitation (not a user input error). Example: "This operation is not supported for this card type." -> submit_feedback with type "bug" including the error message and context. - When accomplishing a task requires an unreasonably complex sequence of tool calls that could be simplified. Example: Getting a budget summary requires 4 separate API calls that could be one. -> submit_feedback with type "suggestion" describing the desired simplified workflow. - You should proactively submit feedback when you encounter tool limitations, even if the user hasn't explicitly complained. WHEN NOT TO USE: - Authentication or permission errors - help the user fix their token or scopes instead. - Invalid input errors - help the user correct their input. - Transient errors - retry the original tool first. SOURCE FIELD: - Use source 'agent' when YOU (the AI assistant) identify a gap, limitation, or issue while helping the user. You should proactively submit feedback when you encounter tool limitations, even if the user hasn't explicitly complained. - Use source 'user' when the user explicitly asks to submit feedback or report a problem. IMPORTANT: Always inform the user before submitting feedback. Briefly explain what you're reporting and why.
submit_feedback
Update memo for multiple expenses in bulk. All expenses will receive the same memo. Parameters: - expense_ids: Array of expense IDs to update - memo: The memo text to apply to all expenses Example (single expense): { "expense_ids": ["exp_123"], "memo": "Business lunch with client" } Example (multiple expenses with same memo): { "expense_ids": ["exp_123", "exp_456", "exp_789"], "memo": "Q1 team building event" }
update_expense_memo
Upload receipts to a card expense by downloading them from provided URLs. This tool downloads receipts from the given URLs and uploads them to the specified card expense. Parameters: - expense_id: The ID of the card expense to attach the receipts to - receipt_urls: Array of URLs where receipts can be downloaded (must be publicly accessible) Example: { "expense_id": "card_exp_123", "receipt_urls": [ "https://example.com/receipts/lunch_receipt.jpg", "https://example.com/receipts/lunch_receipt_page2.pdf" ] }
upload_card_expense_receipt_from_urls
How do I improve a ChatGPT Plugin's discoverability?
The levers are the listing surface agents actually read: names, descriptions, keywords, tool metadata, and registry health. Which lever matters depends on where discovery breaks, which is what continuous measurement shows.
What are Brex alternatives on ChatGPT?
As of 2026-09-28, Brex competes with Allowance, Expensify, Perk, PuraFi Expenses, Ramp, Ramp Data, Rydoo, Tropic in ChatGPT Corporate Spend, Expense & Procurement, ranked by public Discoverability Score.
Where is this profile measured?
This profile uses the geography attached to the latest public registry snapshot: US. Locale tags are intentionally omitted.