chore: import upstream snapshot with attribution
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,71 @@
|
||||
import type { BrexArchiveBudgetParams, BrexArchiveBudgetResponse } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexArchiveBudgetTool: ToolConfig<BrexArchiveBudgetParams, BrexArchiveBudgetResponse> =
|
||||
{
|
||||
id: 'brex_archive_budget',
|
||||
name: 'Brex Archive Budget',
|
||||
description:
|
||||
'Archive a Brex budget, making any spend limits beneath it unusable for future expenses and removing it from the UI',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
budgetId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the budget to archive',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${BREX_API_BASE}/v2/budgets/${encodeURIComponent(params.budgetId.trim())}/archive`,
|
||||
method: 'POST',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response, params) => {
|
||||
if (!response.ok) {
|
||||
// parseBrexJson throws a descriptive error for non-2xx responses; it never
|
||||
// returns in this branch since the body cannot be a successful JSON payload.
|
||||
await parseBrexJson(response)
|
||||
}
|
||||
|
||||
// Brex's archive endpoint does not document a response body schema; fall back
|
||||
// to the request's budget ID and an ARCHIVED status when the body is empty.
|
||||
let data: Record<string, unknown> = {}
|
||||
const text = await response.text()
|
||||
if (text) {
|
||||
try {
|
||||
data = JSON.parse(text)
|
||||
} catch {
|
||||
data = {}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
budgetId: (data.budget_id as string) ?? params?.budgetId ?? '',
|
||||
spendBudgetStatus: (data.spend_budget_status as string) ?? 'ARCHIVED',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
budgetId: { type: 'string', description: 'ID of the archived budget' },
|
||||
spendBudgetStatus: {
|
||||
type: 'string',
|
||||
description: 'Status of the budget after archiving',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import type { BrexCreateBudgetParams, BrexCreateBudgetResponse } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson, splitBrexIdList } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexCreateBudgetTool: ToolConfig<BrexCreateBudgetParams, BrexCreateBudgetResponse> = {
|
||||
id: 'brex_create_budget',
|
||||
name: 'Brex Create Budget',
|
||||
description: 'Create a new budget in the Brex account',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name for the budget',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Description of what the budget is used for',
|
||||
},
|
||||
parentBudgetId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the parent budget',
|
||||
},
|
||||
periodRecurrenceType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Period type of the budget (WEEKLY, MONTHLY, QUARTERLY, YEARLY, ONE_TIME)',
|
||||
},
|
||||
amount: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Budget amount, in the smallest unit of the currency (e.g., cents for USD)',
|
||||
},
|
||||
currency: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ISO 4217 currency code (defaults to USD)',
|
||||
},
|
||||
ownerUserIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user IDs of the budget owners',
|
||||
},
|
||||
startDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Date the budget should start counting (YYYY-MM-DD)',
|
||||
},
|
||||
endDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Date the budget should stop counting (YYYY-MM-DD)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => `${BREX_API_BASE}/v2/budgets`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
...buildBrexHeaders(params.apiKey),
|
||||
'Idempotency-Key': generateId(),
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
name: params.name,
|
||||
description: params.description,
|
||||
parent_budget_id: params.parentBudgetId,
|
||||
period_recurrence_type: params.periodRecurrenceType,
|
||||
amount: {
|
||||
amount: params.amount,
|
||||
currency: params.currency || 'USD',
|
||||
},
|
||||
}
|
||||
const ownerUserIds = splitBrexIdList(params.ownerUserIds)
|
||||
if (ownerUserIds) body.owner_user_ids = ownerUserIds
|
||||
if (params.startDate) body.start_date = params.startDate
|
||||
if (params.endDate) body.end_date = params.endDate
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
budgetId: data.budget_id ?? '',
|
||||
accountId: data.account_id ?? '',
|
||||
name: data.name ?? '',
|
||||
description: data.description ?? null,
|
||||
parentBudgetId: data.parent_budget_id ?? null,
|
||||
ownerUserIds: data.owner_user_ids ?? [],
|
||||
periodRecurrenceType: data.period_recurrence_type ?? '',
|
||||
startDate: data.start_date ?? null,
|
||||
endDate: data.end_date ?? null,
|
||||
amount: data.amount ?? null,
|
||||
spendBudgetStatus: data.spend_budget_status ?? '',
|
||||
limitType: data.limit_type ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
budgetId: { type: 'string', description: 'Unique budget ID' },
|
||||
accountId: { type: 'string', description: 'Account ID the budget belongs to' },
|
||||
name: { type: 'string', description: 'Budget name' },
|
||||
description: { type: 'string', description: 'Budget description', optional: true },
|
||||
parentBudgetId: { type: 'string', description: 'Parent budget ID', optional: true },
|
||||
ownerUserIds: { type: 'array', description: 'User IDs of the budget owners' },
|
||||
periodRecurrenceType: {
|
||||
type: 'string',
|
||||
description: 'Budget period recurrence (WEEKLY, MONTHLY, QUARTERLY, YEARLY, ONE_TIME)',
|
||||
},
|
||||
startDate: { type: 'string', description: 'Budget start date', optional: true },
|
||||
endDate: { type: 'string', description: 'Budget end date', optional: true },
|
||||
amount: {
|
||||
type: 'json',
|
||||
description: 'Budget amount',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
spendBudgetStatus: { type: 'string', description: 'Status of the created budget' },
|
||||
limitType: { type: 'string', description: 'Budget limit type', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import type { BrexCreateSpendLimitParams, BrexCreateSpendLimitResponse } from '@/tools/brex/types'
|
||||
import { BREX_SPEND_LIMIT_PERIOD_BALANCE_PROPERTIES } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson, splitBrexIdList } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexCreateSpendLimitTool: ToolConfig<
|
||||
BrexCreateSpendLimitParams,
|
||||
BrexCreateSpendLimitResponse
|
||||
> = {
|
||||
id: 'brex_create_spend_limit',
|
||||
name: 'Brex Create Spend Limit',
|
||||
description: 'Create a new spend limit (hard-authorization card program) in the Brex account',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name for the spend limit',
|
||||
},
|
||||
periodRecurrenceType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Period type of the spend limit (PER_WEEK, PER_MONTH, PER_QUARTER, PER_YEAR, ONE_TIME)',
|
||||
},
|
||||
spendType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Whether the spend limit can only be spent from cards it provisions (BUDGET_PROVISIONED_CARDS_ONLY, NON_BUDGET_PROVISIONED_CARDS_ALLOWED)',
|
||||
},
|
||||
expenseVisibility: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Whether expenses on this spend limit are viewable by all members (SHARED, PRIVATE)',
|
||||
},
|
||||
authorizationVisibility: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Whether the limit amount is visible to all members, or just controllers/bookkeepers/owners (PUBLIC, PRIVATE)',
|
||||
},
|
||||
limitIncreaseSetting: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether members can request limit increases (ENABLED, DISABLED)',
|
||||
},
|
||||
autoTransferCardsSetting: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'How auto transfer works for virtual cards on this spend limit (DISABLED, ENABLED)',
|
||||
},
|
||||
autoCreateLimitCardsSetting: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'How auto limit card creation works for members (DISABLED, ALL_MEMBERS)',
|
||||
},
|
||||
expensePolicyId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the expense policy corresponding to this spend limit',
|
||||
},
|
||||
baseLimitAmount: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Base spend limit amount, without increases/rollovers, in the smallest unit of the currency (e.g., cents for USD)',
|
||||
},
|
||||
currency: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ISO 4217 currency code for the base limit (defaults to USD)',
|
||||
},
|
||||
authorizationType: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Whether authorizations decline based on available balance (HARD, SOFT)',
|
||||
},
|
||||
rolloverRefreshRate: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Recurrence at which rolled-over unused funds stop rolling over (OFF, NEVER, PER_MONTH, PER_QUARTER, PER_YEAR)',
|
||||
},
|
||||
limitBufferPercentage: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Flexible buffer on the limit as a 0-100 percentage',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Description of what the spend limit is used for',
|
||||
},
|
||||
parentBudgetId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the parent budget',
|
||||
},
|
||||
startDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Date the spend limit should start counting (YYYY-MM-DD)',
|
||||
},
|
||||
endDate: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Date the spend limit should expire (YYYY-MM-DD)',
|
||||
},
|
||||
transactionLimitAmount: {
|
||||
type: 'number',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Per-transaction limit this spend limit enforces, in the smallest unit of the currency',
|
||||
},
|
||||
ownerUserIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user IDs of the spend limit owners',
|
||||
},
|
||||
memberUserIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user IDs of the spend limit members',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => `${BREX_API_BASE}/v2/spend_limits`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
...buildBrexHeaders(params.apiKey),
|
||||
'Idempotency-Key': generateId(),
|
||||
}),
|
||||
body: (params) => {
|
||||
const currency = params.currency || 'USD'
|
||||
const body: Record<string, unknown> = {
|
||||
name: params.name,
|
||||
period_recurrence_type: params.periodRecurrenceType,
|
||||
spend_type: params.spendType,
|
||||
expense_visibility: params.expenseVisibility,
|
||||
authorization_visibility: params.authorizationVisibility,
|
||||
limit_increase_setting: params.limitIncreaseSetting,
|
||||
auto_transfer_cards_setting: params.autoTransferCardsSetting,
|
||||
auto_create_limit_cards_setting: params.autoCreateLimitCardsSetting,
|
||||
expense_policy_id: params.expensePolicyId,
|
||||
authorization_settings: {
|
||||
base_limit: {
|
||||
amount: params.baseLimitAmount,
|
||||
currency,
|
||||
},
|
||||
authorization_type: params.authorizationType,
|
||||
rollover_refresh_rate: params.rolloverRefreshRate,
|
||||
...(params.limitBufferPercentage !== undefined
|
||||
? { limit_buffer_percentage: params.limitBufferPercentage }
|
||||
: {}),
|
||||
},
|
||||
}
|
||||
if (params.description) body.description = params.description
|
||||
if (params.parentBudgetId) body.parent_budget_id = params.parentBudgetId
|
||||
if (params.startDate) body.start_date = params.startDate
|
||||
if (params.endDate) body.end_date = params.endDate
|
||||
if (params.transactionLimitAmount !== undefined) {
|
||||
body.transaction_limit = { amount: params.transactionLimitAmount, currency }
|
||||
}
|
||||
const ownerUserIds = splitBrexIdList(params.ownerUserIds)
|
||||
if (ownerUserIds) body.owner_user_ids = ownerUserIds
|
||||
const memberUserIds = splitBrexIdList(params.memberUserIds)
|
||||
if (memberUserIds) body.member_user_ids = memberUserIds
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
accountId: data.account_id ?? '',
|
||||
name: data.name ?? '',
|
||||
description: data.description ?? null,
|
||||
parentBudgetId: data.parent_budget_id ?? null,
|
||||
status: data.status ?? '',
|
||||
periodRecurrenceType: data.period_recurrence_type ?? '',
|
||||
spendType: data.spend_type ?? '',
|
||||
startDate: data.start_date ?? null,
|
||||
endDate: data.end_date ?? null,
|
||||
ownerUserIds: data.owner_user_ids ?? [],
|
||||
memberUserIds: data.member_user_ids ?? [],
|
||||
currentPeriodBalance: data.current_period_balance ?? null,
|
||||
authorizationSettings: data.authorization_settings ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique spend limit ID' },
|
||||
accountId: { type: 'string', description: 'Account ID the spend limit belongs to' },
|
||||
name: { type: 'string', description: 'Spend limit name' },
|
||||
description: { type: 'string', description: 'Spend limit description', optional: true },
|
||||
parentBudgetId: { type: 'string', description: 'Parent budget ID', optional: true },
|
||||
status: { type: 'string', description: 'Spend limit status' },
|
||||
periodRecurrenceType: {
|
||||
type: 'string',
|
||||
description: 'Period recurrence (PER_WEEK, PER_MONTH, PER_QUARTER, PER_YEAR, ONE_TIME)',
|
||||
},
|
||||
spendType: { type: 'string', description: 'Spend type of the limit' },
|
||||
startDate: { type: 'string', description: 'Spend limit start date', optional: true },
|
||||
endDate: { type: 'string', description: 'Spend limit end date', optional: true },
|
||||
ownerUserIds: { type: 'array', description: 'User IDs of the spend limit owners' },
|
||||
memberUserIds: { type: 'array', description: 'User IDs of the spend limit members' },
|
||||
currentPeriodBalance: {
|
||||
type: 'json',
|
||||
description: 'Spend and rollover amounts for the current period',
|
||||
optional: true,
|
||||
properties: BREX_SPEND_LIMIT_PERIOD_BALANCE_PROPERTIES,
|
||||
},
|
||||
authorizationSettings: {
|
||||
type: 'json',
|
||||
description: 'Authorization settings (base limit, authorization type, rollover refresh)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import type { BrexCreateTransferParams, BrexCreateTransferResponse } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexCreateTransferTool: ToolConfig<
|
||||
BrexCreateTransferParams,
|
||||
BrexCreateTransferResponse
|
||||
> = {
|
||||
id: 'brex_create_transfer',
|
||||
name: 'Brex Create Transfer',
|
||||
description: 'Create a money transfer from a Brex cash account to a vendor',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
cashAccountId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'ID of the Brex cash account to send the transfer from (found via the /accounts endpoint)',
|
||||
},
|
||||
vendorPaymentInstrumentId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
"ID of the vendor's payment instrument to send the transfer to (from the vendor's payment_accounts)",
|
||||
},
|
||||
amount: {
|
||||
type: 'number',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Amount to transfer, in the smallest unit of the currency (e.g., cents for USD)',
|
||||
},
|
||||
currency: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ISO 4217 currency code (defaults to USD)',
|
||||
},
|
||||
description: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Description of the transfer for internal use (not exposed externally)',
|
||||
},
|
||||
externalMemo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'External memo shown to the recipient (max 90 characters for ACH/Wire, 40 for Cheque)',
|
||||
},
|
||||
approvalType: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Set to MANUAL to require cash admin approval before the transfer is sent',
|
||||
},
|
||||
isPproEnabled: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Enable Principal Protection (PPRO) to have Brex cover intermediary/receiving bank fees (international wires only)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => `${BREX_API_BASE}/v1/transfers`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
...buildBrexHeaders(params.apiKey),
|
||||
// Brex requires a fresh Idempotency-Key per transfer creation to prevent duplicate money movement.
|
||||
'Idempotency-Key': generateId(),
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
counterparty: {
|
||||
type: 'VENDOR',
|
||||
payment_instrument_id: params.vendorPaymentInstrumentId,
|
||||
},
|
||||
amount: {
|
||||
amount: params.amount,
|
||||
currency: params.currency || 'USD',
|
||||
},
|
||||
description: params.description,
|
||||
external_memo: params.externalMemo,
|
||||
originating_account: {
|
||||
type: 'BREX_CASH',
|
||||
id: params.cashAccountId,
|
||||
},
|
||||
}
|
||||
if (params.approvalType) body.approval_type = params.approvalType
|
||||
if (params.isPproEnabled !== undefined) body.is_ppro_enabled = params.isPproEnabled
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
counterparty: data.counterparty ?? null,
|
||||
description: data.description ?? null,
|
||||
paymentType: data.payment_type ?? '',
|
||||
amount: data.amount ?? null,
|
||||
processDate: data.process_date ?? null,
|
||||
originatingAccount: data.originating_account ?? null,
|
||||
status: data.status ?? '',
|
||||
cancellationReason: data.cancellation_reason ?? null,
|
||||
estimatedDeliveryDate: data.estimated_delivery_date ?? null,
|
||||
creatorUserId: data.creator_user_id ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
displayName: data.display_name ?? null,
|
||||
externalMemo: data.external_memo ?? null,
|
||||
isPproEnabled: data.is_ppro_enabled ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique transfer ID' },
|
||||
counterparty: { type: 'json', description: 'Transfer counterparty details', optional: true },
|
||||
description: { type: 'string', description: 'Description of the transfer', optional: true },
|
||||
paymentType: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Payment type (ACH, DOMESTIC_WIRE, CHEQUE, INTERNATIONAL_WIRE, BOOK_TRANSFER, STABLECOIN)',
|
||||
},
|
||||
amount: {
|
||||
type: 'json',
|
||||
description: 'Transfer amount',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
processDate: { type: 'string', description: 'Transaction processing date', optional: true },
|
||||
originatingAccount: {
|
||||
type: 'json',
|
||||
description: 'Originating account details for the transfer',
|
||||
optional: true,
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Transfer status (PROCESSING, SCHEDULED, PENDING_APPROVAL, FAILED, PROCESSED)',
|
||||
},
|
||||
cancellationReason: {
|
||||
type: 'string',
|
||||
description: 'Reason the transfer was canceled',
|
||||
optional: true,
|
||||
},
|
||||
estimatedDeliveryDate: {
|
||||
type: 'string',
|
||||
description: 'Estimated delivery date for the transfer',
|
||||
optional: true,
|
||||
},
|
||||
creatorUserId: {
|
||||
type: 'string',
|
||||
description: 'ID of the user who created the transfer',
|
||||
optional: true,
|
||||
},
|
||||
createdAt: {
|
||||
type: 'string',
|
||||
description: 'Creation timestamp of the transfer',
|
||||
optional: true,
|
||||
},
|
||||
displayName: {
|
||||
type: 'string',
|
||||
description: 'Human-readable name of the transfer',
|
||||
optional: true,
|
||||
},
|
||||
externalMemo: { type: 'string', description: 'External memo of the transfer', optional: true },
|
||||
isPproEnabled: {
|
||||
type: 'boolean',
|
||||
description: 'Whether Principal Protection (PPRO) is enabled for the transfer',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import type { BrexCreateVendorParams, BrexCreateVendorResponse } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexCreateVendorTool: ToolConfig<BrexCreateVendorParams, BrexCreateVendorResponse> = {
|
||||
id: 'brex_create_vendor',
|
||||
name: 'Brex Create Vendor',
|
||||
description: 'Create a new vendor in the Brex account',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
companyName: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Name for the vendor (must be unique)',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Email address for the vendor',
|
||||
},
|
||||
phone: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Phone number for the vendor',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: () => `${BREX_API_BASE}/v1/vendors`,
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
...buildBrexHeaders(params.apiKey),
|
||||
'Idempotency-Key': generateId(),
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = { company_name: params.companyName }
|
||||
if (params.email) body.email = params.email
|
||||
if (params.phone) body.phone = params.phone
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
companyName: data.company_name ?? null,
|
||||
email: data.email ?? null,
|
||||
phone: data.phone ?? null,
|
||||
paymentAccounts: data.payment_accounts ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique vendor ID' },
|
||||
companyName: { type: 'string', description: 'Vendor company name', optional: true },
|
||||
email: { type: 'string', description: 'Vendor email address', optional: true },
|
||||
phone: { type: 'string', description: 'Vendor phone number', optional: true },
|
||||
paymentAccounts: { type: 'array', description: 'Payment accounts associated with the vendor' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import type { BrexGetBudgetParams, BrexGetBudgetResponse } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexGetBudgetTool: ToolConfig<BrexGetBudgetParams, BrexGetBudgetResponse> = {
|
||||
id: 'brex_get_budget',
|
||||
name: 'Brex Get Budget',
|
||||
description: 'Get a Brex budget by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
budgetId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the budget to fetch',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `${BREX_API_BASE}/v2/budgets/${encodeURIComponent(params.budgetId.trim())}`,
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
budgetId: data.budget_id ?? '',
|
||||
accountId: data.account_id ?? '',
|
||||
name: data.name ?? '',
|
||||
description: data.description ?? null,
|
||||
parentBudgetId: data.parent_budget_id ?? null,
|
||||
ownerUserIds: data.owner_user_ids ?? [],
|
||||
periodRecurrenceType: data.period_recurrence_type ?? '',
|
||||
startDate: data.start_date ?? null,
|
||||
endDate: data.end_date ?? null,
|
||||
amount: data.amount ?? null,
|
||||
spendBudgetStatus: data.spend_budget_status ?? '',
|
||||
limitType: data.limit_type ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
budgetId: { type: 'string', description: 'Unique budget ID' },
|
||||
accountId: { type: 'string', description: 'Account ID the budget belongs to' },
|
||||
name: { type: 'string', description: 'Budget name' },
|
||||
description: { type: 'string', description: 'Budget description', optional: true },
|
||||
parentBudgetId: { type: 'string', description: 'Parent budget ID', optional: true },
|
||||
ownerUserIds: { type: 'array', description: 'User IDs of the budget owners' },
|
||||
periodRecurrenceType: {
|
||||
type: 'string',
|
||||
description: 'Budget period recurrence (WEEKLY, MONTHLY, QUARTERLY, YEARLY, ONE_TIME)',
|
||||
},
|
||||
startDate: { type: 'string', description: 'Budget start date', optional: true },
|
||||
endDate: { type: 'string', description: 'Budget end date', optional: true },
|
||||
amount: {
|
||||
type: 'json',
|
||||
description: 'Budget amount',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
spendBudgetStatus: {
|
||||
type: 'string',
|
||||
description: 'Budget status (ACTIVE, ARCHIVED, DELETED)',
|
||||
},
|
||||
limitType: { type: 'string', description: 'Budget limit type (HARD or SOFT)', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import type { BrexGetCashAccountParams, BrexGetCashAccountResponse } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexGetCashAccountTool: ToolConfig<
|
||||
BrexGetCashAccountParams,
|
||||
BrexGetCashAccountResponse
|
||||
> = {
|
||||
id: 'brex_get_cash_account',
|
||||
name: 'Brex Get Cash Account',
|
||||
description: 'Get a Brex cash account by ID, or the primary cash account when no ID is provided',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
accountId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the cash account (defaults to the primary cash account)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const accountId = params.accountId?.trim()
|
||||
return accountId
|
||||
? `${BREX_API_BASE}/v2/accounts/cash/${encodeURIComponent(accountId)}`
|
||||
: `${BREX_API_BASE}/v2/accounts/cash/primary`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
name: data.name ?? '',
|
||||
status: data.status ?? null,
|
||||
currentBalance: data.current_balance,
|
||||
availableBalance: data.available_balance,
|
||||
accountNumber: data.account_number ?? '',
|
||||
routingNumber: data.routing_number ?? '',
|
||||
primary: data.primary ?? false,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique account ID' },
|
||||
name: { type: 'string', description: 'Account name' },
|
||||
status: { type: 'string', description: 'Account status', optional: true },
|
||||
currentBalance: {
|
||||
type: 'json',
|
||||
description: 'Current balance',
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
availableBalance: {
|
||||
type: 'json',
|
||||
description: 'Available balance',
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
accountNumber: { type: 'string', description: 'Bank account number' },
|
||||
routingNumber: { type: 'string', description: 'Bank routing number' },
|
||||
primary: { type: 'boolean', description: 'Whether this is the primary cash account' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
import type { BrexApiKeyParams, BrexGetCompanyResponse } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexGetCompanyTool: ToolConfig<BrexApiKeyParams, BrexGetCompanyResponse> = {
|
||||
id: 'brex_get_company',
|
||||
name: 'Brex Get Company',
|
||||
description: 'Get the Brex company associated with the API token',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: `${BREX_API_BASE}/v2/company`,
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
legalName: data.legal_name ?? '',
|
||||
mailingAddress: data.mailing_address ?? null,
|
||||
accountType: data.account_type ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique company ID' },
|
||||
legalName: { type: 'string', description: 'Legal name of the company' },
|
||||
mailingAddress: {
|
||||
type: 'json',
|
||||
description: 'Company mailing address (line1, line2, city, state, country, postal_code)',
|
||||
optional: true,
|
||||
},
|
||||
accountType: {
|
||||
type: 'string',
|
||||
description: 'Brex account type (BREX_CLASSIC or BREX_EMPOWER)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
import type { BrexApiKeyParams, BrexGetUserResponse } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexGetCurrentUserTool: ToolConfig<BrexApiKeyParams, BrexGetUserResponse> = {
|
||||
id: 'brex_get_current_user',
|
||||
name: 'Brex Get Current User',
|
||||
description: 'Get the Brex user associated with the API token',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: `${BREX_API_BASE}/v2/users/me`,
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
firstName: data.first_name ?? '',
|
||||
lastName: data.last_name ?? '',
|
||||
email: data.email ?? '',
|
||||
status: data.status ?? null,
|
||||
managerId: data.manager_id ?? null,
|
||||
departmentId: data.department_id ?? null,
|
||||
locationId: data.location_id ?? null,
|
||||
titleId: data.title_id ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique user ID' },
|
||||
firstName: { type: 'string', description: 'First name' },
|
||||
lastName: { type: 'string', description: 'Last name' },
|
||||
email: { type: 'string', description: 'Email address' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description:
|
||||
'User status (INVITED, ACTIVE, CLOSED, DISABLED, DELETED, PENDING_ACTIVATION, INACTIVE, ARCHIVED)',
|
||||
optional: true,
|
||||
},
|
||||
managerId: { type: 'string', description: 'ID of the manager', optional: true },
|
||||
departmentId: { type: 'string', description: 'Department ID', optional: true },
|
||||
locationId: { type: 'string', description: 'Location ID', optional: true },
|
||||
titleId: { type: 'string', description: 'Title ID', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
import type { BrexGetExpenseParams, BrexGetExpenseResponse } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const EXPAND_FIELDS = [
|
||||
'merchant',
|
||||
'user',
|
||||
'budget',
|
||||
'department',
|
||||
'location',
|
||||
'receipts.download_uris',
|
||||
]
|
||||
|
||||
export const brexGetExpenseTool: ToolConfig<BrexGetExpenseParams, BrexGetExpenseResponse> = {
|
||||
id: 'brex_get_expense',
|
||||
name: 'Brex Get Expense',
|
||||
description: 'Get a single Brex expense by its ID, including merchant, user, and receipt details',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
expenseId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the expense to fetch',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
for (const field of EXPAND_FIELDS) {
|
||||
query.append('expand[]', field)
|
||||
}
|
||||
return `${BREX_API_BASE}/v1/expenses/${encodeURIComponent(params.expenseId.trim())}?${query.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
memo: data.memo ?? null,
|
||||
status: data.status ?? null,
|
||||
paymentStatus: data.payment_status ?? null,
|
||||
expenseType: data.expense_type ?? null,
|
||||
category: data.category ?? null,
|
||||
merchantId: data.merchant_id ?? null,
|
||||
merchant: data.merchant ?? null,
|
||||
budgetId: data.budget_id ?? null,
|
||||
budget: data.budget ?? null,
|
||||
departmentId: data.department_id ?? null,
|
||||
department: data.department ?? null,
|
||||
locationId: data.location_id ?? null,
|
||||
location: data.location ?? null,
|
||||
userId: data.user_id ?? null,
|
||||
user: data.user ?? null,
|
||||
originalAmount: data.original_amount ?? null,
|
||||
billingAmount: data.billing_amount ?? null,
|
||||
purchasedAmount: data.purchased_amount ?? null,
|
||||
usdEquivalentAmount: data.usd_equivalent_amount ?? null,
|
||||
purchasedAt: data.purchased_at ?? null,
|
||||
updatedAt: data.updated_at ?? '',
|
||||
paymentPostedAt: data.payment_posted_at ?? null,
|
||||
receipts: data.receipts ?? [],
|
||||
dashboardUrl: data.dashboard_url ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique expense ID' },
|
||||
memo: { type: 'string', description: 'Memo on the expense', optional: true },
|
||||
status: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Expense status (DRAFT, SUBMITTED, APPROVED, OUT_OF_POLICY, VOID, CANCELED, SPLIT, SETTLED)',
|
||||
optional: true,
|
||||
},
|
||||
paymentStatus: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Payment status (NOT_STARTED, PROCESSING, CANCELED, DECLINED, CLEARED, REFUNDING, REFUNDED, CASH_ADVANCE, CREDITED, AWAITING_PAYMENT, SCHEDULED)',
|
||||
optional: true,
|
||||
},
|
||||
expenseType: {
|
||||
type: 'string',
|
||||
description: 'Expense type (CARD, BILLPAY, REIMBURSEMENT, CLAWBACK, UNSET)',
|
||||
optional: true,
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Expense category (e.g., RESTAURANTS, RECURRING_SOFTWARE_AND_SAAS, AIRLINE_EXPENSES)',
|
||||
optional: true,
|
||||
},
|
||||
merchantId: { type: 'string', description: 'Merchant ID', optional: true },
|
||||
merchant: {
|
||||
type: 'json',
|
||||
description: 'Merchant details (raw descriptor, MCC, country)',
|
||||
optional: true,
|
||||
properties: {
|
||||
raw_descriptor: { type: 'string', description: 'Raw merchant descriptor' },
|
||||
mcc: { type: 'string', description: 'Merchant category code' },
|
||||
country: { type: 'string', description: 'Merchant country' },
|
||||
},
|
||||
},
|
||||
budgetId: { type: 'string', description: 'Budget ID', optional: true },
|
||||
budget: {
|
||||
type: 'json',
|
||||
description: 'Budget the expense belongs to',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Budget ID' },
|
||||
name: { type: 'string', description: 'Budget name' },
|
||||
},
|
||||
},
|
||||
departmentId: { type: 'string', description: 'Department ID', optional: true },
|
||||
department: {
|
||||
type: 'json',
|
||||
description: 'Department of the expense owner',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Department ID' },
|
||||
name: { type: 'string', description: 'Department name' },
|
||||
},
|
||||
},
|
||||
locationId: { type: 'string', description: 'Location ID', optional: true },
|
||||
location: {
|
||||
type: 'json',
|
||||
description: 'Location of the expense owner',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Location ID' },
|
||||
name: { type: 'string', description: 'Location name' },
|
||||
},
|
||||
},
|
||||
userId: { type: 'string', description: 'ID of the user who made the expense', optional: true },
|
||||
user: {
|
||||
type: 'json',
|
||||
description: 'User who made the expense',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
first_name: { type: 'string', description: 'First name' },
|
||||
last_name: { type: 'string', description: 'Last name' },
|
||||
},
|
||||
},
|
||||
originalAmount: {
|
||||
type: 'json',
|
||||
description: 'Original transaction amount',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
billingAmount: {
|
||||
type: 'json',
|
||||
description: 'Amount billed to the account',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
purchasedAmount: {
|
||||
type: 'json',
|
||||
description: 'Amount at the time of purchase',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
usdEquivalentAmount: {
|
||||
type: 'json',
|
||||
description: 'USD equivalent amount',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
purchasedAt: { type: 'string', description: 'Purchase timestamp (ISO 8601)', optional: true },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp (ISO 8601)' },
|
||||
paymentPostedAt: {
|
||||
type: 'string',
|
||||
description: 'Timestamp the payment was posted (ISO 8601)',
|
||||
optional: true,
|
||||
},
|
||||
receipts: {
|
||||
type: 'array',
|
||||
description: 'Receipts attached to the expense',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Receipt ID' },
|
||||
download_uris: { type: 'array', description: 'Pre-signed receipt download URLs' },
|
||||
},
|
||||
},
|
||||
},
|
||||
dashboardUrl: { type: 'string', description: 'Link to the expense in the Brex dashboard' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
import type { BrexGetSpendLimitParams, BrexGetSpendLimitResponse } from '@/tools/brex/types'
|
||||
import { BREX_SPEND_LIMIT_PERIOD_BALANCE_PROPERTIES } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexGetSpendLimitTool: ToolConfig<BrexGetSpendLimitParams, BrexGetSpendLimitResponse> =
|
||||
{
|
||||
id: 'brex_get_spend_limit',
|
||||
name: 'Brex Get Spend Limit',
|
||||
description: 'Get a Brex spend limit by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
spendLimitId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the spend limit to fetch',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${BREX_API_BASE}/v2/spend_limits/${encodeURIComponent(params.spendLimitId.trim())}`,
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
accountId: data.account_id ?? '',
|
||||
name: data.name ?? '',
|
||||
description: data.description ?? null,
|
||||
parentBudgetId: data.parent_budget_id ?? null,
|
||||
status: data.status ?? '',
|
||||
periodRecurrenceType: data.period_recurrence_type ?? '',
|
||||
spendType: data.spend_type ?? '',
|
||||
startDate: data.start_date ?? null,
|
||||
endDate: data.end_date ?? null,
|
||||
ownerUserIds: data.owner_user_ids ?? [],
|
||||
memberUserIds: data.member_user_ids ?? [],
|
||||
currentPeriodBalance: data.current_period_balance ?? null,
|
||||
authorizationSettings: data.authorization_settings ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique spend limit ID' },
|
||||
accountId: { type: 'string', description: 'Account ID the spend limit belongs to' },
|
||||
name: { type: 'string', description: 'Spend limit name' },
|
||||
description: { type: 'string', description: 'Spend limit description', optional: true },
|
||||
parentBudgetId: { type: 'string', description: 'Parent budget ID', optional: true },
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Spend limit status (ACTIVE, EXPIRED, ARCHIVED)',
|
||||
},
|
||||
periodRecurrenceType: {
|
||||
type: 'string',
|
||||
description: 'Period recurrence (PER_WEEK, PER_MONTH, PER_QUARTER, PER_YEAR, ONE_TIME)',
|
||||
},
|
||||
spendType: { type: 'string', description: 'Spend type of the limit' },
|
||||
startDate: { type: 'string', description: 'Spend limit start date', optional: true },
|
||||
endDate: { type: 'string', description: 'Spend limit end date', optional: true },
|
||||
ownerUserIds: { type: 'array', description: 'User IDs of the spend limit owners' },
|
||||
memberUserIds: { type: 'array', description: 'User IDs of the spend limit members' },
|
||||
currentPeriodBalance: {
|
||||
type: 'json',
|
||||
description: 'Spend and rollover amounts for the current period',
|
||||
optional: true,
|
||||
properties: BREX_SPEND_LIMIT_PERIOD_BALANCE_PROPERTIES,
|
||||
},
|
||||
authorizationSettings: {
|
||||
type: 'json',
|
||||
description: 'Authorization settings (base limit, authorization type, rollover refresh)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import type { BrexGetTransferParams, BrexGetTransferResponse } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexGetTransferTool: ToolConfig<BrexGetTransferParams, BrexGetTransferResponse> = {
|
||||
id: 'brex_get_transfer',
|
||||
name: 'Brex Get Transfer',
|
||||
description: 'Get a Brex money transfer by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
transferId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the transfer to fetch',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${BREX_API_BASE}/v1/transfers/${encodeURIComponent(params.transferId.trim())}`,
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
counterparty: data.counterparty ?? null,
|
||||
description: data.description ?? null,
|
||||
paymentType: data.payment_type ?? '',
|
||||
amount: data.amount ?? null,
|
||||
processDate: data.process_date ?? null,
|
||||
originatingAccount: data.originating_account ?? null,
|
||||
status: data.status ?? '',
|
||||
cancellationReason: data.cancellation_reason ?? null,
|
||||
estimatedDeliveryDate: data.estimated_delivery_date ?? null,
|
||||
creatorUserId: data.creator_user_id ?? null,
|
||||
createdAt: data.created_at ?? null,
|
||||
displayName: data.display_name ?? null,
|
||||
externalMemo: data.external_memo ?? null,
|
||||
isPproEnabled: data.is_ppro_enabled ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique transfer ID' },
|
||||
counterparty: { type: 'json', description: 'Transfer counterparty details', optional: true },
|
||||
description: { type: 'string', description: 'Transfer description', optional: true },
|
||||
paymentType: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Payment type (ACH, DOMESTIC_WIRE, CHEQUE, INTERNATIONAL_WIRE, BOOK_TRANSFER, STABLECOIN)',
|
||||
},
|
||||
amount: {
|
||||
type: 'json',
|
||||
description: 'Transfer amount',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
processDate: { type: 'string', description: 'Date the transfer processes', optional: true },
|
||||
originatingAccount: {
|
||||
type: 'json',
|
||||
description: 'Account the transfer originates from',
|
||||
optional: true,
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Transfer status (PROCESSING, SCHEDULED, PENDING_APPROVAL, FAILED, PROCESSED)',
|
||||
},
|
||||
cancellationReason: {
|
||||
type: 'string',
|
||||
description: 'Reason the transfer was canceled',
|
||||
optional: true,
|
||||
},
|
||||
estimatedDeliveryDate: {
|
||||
type: 'string',
|
||||
description: 'Estimated delivery date',
|
||||
optional: true,
|
||||
},
|
||||
creatorUserId: {
|
||||
type: 'string',
|
||||
description: 'ID of the user who created the transfer',
|
||||
optional: true,
|
||||
},
|
||||
createdAt: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
displayName: { type: 'string', description: 'Transfer display name', optional: true },
|
||||
externalMemo: { type: 'string', description: 'External memo', optional: true },
|
||||
isPproEnabled: {
|
||||
type: 'boolean',
|
||||
description: 'Whether Principal Protection (PPRO) is enabled',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
import type { BrexGetUserParams, BrexGetUserResponse } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexGetUserTool: ToolConfig<BrexGetUserParams, BrexGetUserResponse> = {
|
||||
id: 'brex_get_user',
|
||||
name: 'Brex Get User',
|
||||
description: 'Get a Brex user by their ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the user to fetch',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `${BREX_API_BASE}/v2/users/${encodeURIComponent(params.userId.trim())}`,
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
firstName: data.first_name ?? '',
|
||||
lastName: data.last_name ?? '',
|
||||
email: data.email ?? '',
|
||||
status: data.status ?? null,
|
||||
managerId: data.manager_id ?? null,
|
||||
departmentId: data.department_id ?? null,
|
||||
locationId: data.location_id ?? null,
|
||||
titleId: data.title_id ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique user ID' },
|
||||
firstName: { type: 'string', description: 'First name' },
|
||||
lastName: { type: 'string', description: 'Last name' },
|
||||
email: { type: 'string', description: 'Email address' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description:
|
||||
'User status (INVITED, ACTIVE, CLOSED, DISABLED, DELETED, PENDING_ACTIVATION, INACTIVE, ARCHIVED)',
|
||||
optional: true,
|
||||
},
|
||||
managerId: { type: 'string', description: 'ID of the manager', optional: true },
|
||||
departmentId: { type: 'string', description: 'Department ID', optional: true },
|
||||
locationId: { type: 'string', description: 'Location ID', optional: true },
|
||||
titleId: { type: 'string', description: 'Title ID', optional: true },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import type { BrexGetVendorParams, BrexGetVendorResponse } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexGetVendorTool: ToolConfig<BrexGetVendorParams, BrexGetVendorResponse> = {
|
||||
id: 'brex_get_vendor',
|
||||
name: 'Brex Get Vendor',
|
||||
description: 'Get a Brex vendor by its ID',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
vendorId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the vendor to fetch',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `${BREX_API_BASE}/v1/vendors/${encodeURIComponent(params.vendorId.trim())}`,
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
companyName: data.company_name ?? null,
|
||||
email: data.email ?? null,
|
||||
phone: data.phone ?? null,
|
||||
paymentAccounts: data.payment_accounts ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique vendor ID' },
|
||||
companyName: { type: 'string', description: 'Vendor company name', optional: true },
|
||||
email: { type: 'string', description: 'Vendor email address', optional: true },
|
||||
phone: { type: 'string', description: 'Vendor phone number', optional: true },
|
||||
paymentAccounts: {
|
||||
type: 'array',
|
||||
description: 'Payment accounts associated with the vendor',
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
export { brexArchiveBudgetTool } from '@/tools/brex/archive_budget'
|
||||
export { brexCreateBudgetTool } from '@/tools/brex/create_budget'
|
||||
export { brexCreateSpendLimitTool } from '@/tools/brex/create_spend_limit'
|
||||
export { brexCreateTransferTool } from '@/tools/brex/create_transfer'
|
||||
export { brexCreateVendorTool } from '@/tools/brex/create_vendor'
|
||||
export { brexGetBudgetTool } from '@/tools/brex/get_budget'
|
||||
export { brexGetCashAccountTool } from '@/tools/brex/get_cash_account'
|
||||
export { brexGetCompanyTool } from '@/tools/brex/get_company'
|
||||
export { brexGetCurrentUserTool } from '@/tools/brex/get_current_user'
|
||||
export { brexGetExpenseTool } from '@/tools/brex/get_expense'
|
||||
export { brexGetSpendLimitTool } from '@/tools/brex/get_spend_limit'
|
||||
export { brexGetTransferTool } from '@/tools/brex/get_transfer'
|
||||
export { brexGetUserTool } from '@/tools/brex/get_user'
|
||||
export { brexGetVendorTool } from '@/tools/brex/get_vendor'
|
||||
export { brexListBudgetsTool } from '@/tools/brex/list_budgets'
|
||||
export { brexListCardAccountsTool } from '@/tools/brex/list_card_accounts'
|
||||
export { brexListCardStatementsTool } from '@/tools/brex/list_card_statements'
|
||||
export { brexListCardTransactionsTool } from '@/tools/brex/list_card_transactions'
|
||||
export { brexListCardsTool } from '@/tools/brex/list_cards'
|
||||
export { brexListCashAccountsTool } from '@/tools/brex/list_cash_accounts'
|
||||
export { brexListCashStatementsTool } from '@/tools/brex/list_cash_statements'
|
||||
export { brexListCashTransactionsTool } from '@/tools/brex/list_cash_transactions'
|
||||
export { brexListDepartmentsTool } from '@/tools/brex/list_departments'
|
||||
export { brexListExpensesTool } from '@/tools/brex/list_expenses'
|
||||
export { brexListLocationsTool } from '@/tools/brex/list_locations'
|
||||
export { brexListSpendLimitsTool } from '@/tools/brex/list_spend_limits'
|
||||
export { brexListTitlesTool } from '@/tools/brex/list_titles'
|
||||
export { brexListTransfersTool } from '@/tools/brex/list_transfers'
|
||||
export { brexListUsersTool } from '@/tools/brex/list_users'
|
||||
export { brexListVendorsTool } from '@/tools/brex/list_vendors'
|
||||
export { brexMatchReceiptTool } from '@/tools/brex/match_receipt'
|
||||
export { brexUpdateExpenseTool } from '@/tools/brex/update_expense'
|
||||
export { brexUpdateVendorTool } from '@/tools/brex/update_vendor'
|
||||
export { brexUploadReceiptTool } from '@/tools/brex/upload_receipt'
|
||||
@@ -0,0 +1,98 @@
|
||||
import type { BrexListBudgetsResponse, BrexPaginationParams } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListBudgetsTool: ToolConfig<BrexPaginationParams, BrexListBudgetsResponse> = {
|
||||
id: 'brex_list_budgets',
|
||||
name: 'Brex List Budgets',
|
||||
description: 'List budgets in the Brex account',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of budgets to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
return queryString
|
||||
? `${BREX_API_BASE}/v2/budgets?${queryString}`
|
||||
: `${BREX_API_BASE}/v2/budgets`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Budgets in the Brex account',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
budget_id: { type: 'string', description: 'Unique budget ID' },
|
||||
account_id: { type: 'string', description: 'Account ID the budget belongs to' },
|
||||
name: { type: 'string', description: 'Budget name' },
|
||||
description: { type: 'string', description: 'Budget description', optional: true },
|
||||
parent_budget_id: { type: 'string', description: 'Parent budget ID', optional: true },
|
||||
owner_user_ids: { type: 'array', description: 'User IDs of the budget owners' },
|
||||
period_recurrence_type: {
|
||||
type: 'string',
|
||||
description: 'Budget period recurrence (WEEKLY, MONTHLY, QUARTERLY, YEARLY, ONE_TIME)',
|
||||
},
|
||||
start_date: { type: 'string', description: 'Budget start date', optional: true },
|
||||
end_date: { type: 'string', description: 'Budget end date', optional: true },
|
||||
amount: {
|
||||
type: 'json',
|
||||
description: 'Budget amount',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
spend_budget_status: { type: 'string', description: 'Budget status' },
|
||||
limit_type: { type: 'string', description: 'Budget limit type', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
import type { BrexApiKeyParams, BrexListCardAccountsResponse } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListCardAccountsTool: ToolConfig<BrexApiKeyParams, BrexListCardAccountsResponse> =
|
||||
{
|
||||
id: 'brex_list_card_accounts',
|
||||
name: 'Brex List Card Accounts',
|
||||
description: 'List all Brex card accounts with balances and limits',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: `${BREX_API_BASE}/v2/accounts/card`,
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
accounts: Array.isArray(data) ? data : [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
accounts: {
|
||||
type: 'array',
|
||||
description: 'Card accounts',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique account ID' },
|
||||
status: { type: 'string', description: 'Account status', optional: true },
|
||||
current_balance: {
|
||||
type: 'json',
|
||||
description: 'Current balance',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
available_balance: {
|
||||
type: 'json',
|
||||
description: 'Available balance',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
account_limit: {
|
||||
type: 'json',
|
||||
description: 'Account limit',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
current_statement_period: {
|
||||
type: 'json',
|
||||
description: 'Current statement period (start_date, end_date)',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import type { BrexListStatementsResponse, BrexPaginationParams } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListCardStatementsTool: ToolConfig<
|
||||
BrexPaginationParams,
|
||||
BrexListStatementsResponse
|
||||
> = {
|
||||
id: 'brex_list_card_statements',
|
||||
name: 'Brex List Card Statements',
|
||||
description: 'List finalized statements for the primary Brex card account',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of statements to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
return queryString
|
||||
? `${BREX_API_BASE}/v2/accounts/card/primary/statements?${queryString}`
|
||||
: `${BREX_API_BASE}/v2/accounts/card/primary/statements`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Finalized card account statements',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique statement ID' },
|
||||
start_balance: {
|
||||
type: 'json',
|
||||
description: 'Balance at the start of the period',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
end_balance: {
|
||||
type: 'json',
|
||||
description: 'Balance at the end of the period',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
period: { type: 'json', description: 'Statement period (start_date, end_date)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
import type {
|
||||
BrexListCardTransactionsParams,
|
||||
BrexListCardTransactionsResponse,
|
||||
} from '@/tools/brex/types'
|
||||
import { BREX_CARD_TRANSACTION_PROPERTIES } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexArrayParam,
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
toBrexDateTime,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListCardTransactionsTool: ToolConfig<
|
||||
BrexListCardTransactionsParams,
|
||||
BrexListCardTransactionsResponse
|
||||
> = {
|
||||
id: 'brex_list_card_transactions',
|
||||
name: 'Brex List Card Transactions',
|
||||
description: 'List settled card transactions for all Brex card accounts',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
userIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user IDs to filter transactions by cardholder',
|
||||
},
|
||||
postedAtStart: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include transactions posted at or after this ISO 8601 timestamp',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of transactions to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
query.append('expand[]', 'expense_id')
|
||||
appendBrexArrayParam(query, 'user_ids', params.userIds)
|
||||
if (params.postedAtStart)
|
||||
query.append('posted_at_start', toBrexDateTime(params.postedAtStart))
|
||||
appendBrexPagination(query, params)
|
||||
return `${BREX_API_BASE}/v2/transactions/card/primary?${query.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Settled card transactions',
|
||||
items: { type: 'json', properties: BREX_CARD_TRANSACTION_PROPERTIES },
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
import type { BrexListCardsParams, BrexListCardsResponse } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListCardsTool: ToolConfig<BrexListCardsParams, BrexListCardsResponse> = {
|
||||
id: 'brex_list_cards',
|
||||
name: 'Brex List Cards',
|
||||
description: 'List cards in the Brex account, optionally filtered by card owner',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
userId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter cards by the ID of the card owner',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of cards to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
if (params.userId) query.append('user_id', params.userId.trim())
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
return queryString ? `${BREX_API_BASE}/v2/cards?${queryString}` : `${BREX_API_BASE}/v2/cards`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Cards in the Brex account',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique card ID' },
|
||||
owner: { type: 'json', description: 'Card owner (type, user_id)' },
|
||||
status: { type: 'string', description: 'Card status', optional: true },
|
||||
last_four: { type: 'string', description: 'Last four digits of the card number' },
|
||||
card_name: { type: 'string', description: 'Card name' },
|
||||
card_type: {
|
||||
type: 'string',
|
||||
description: 'Card type (VIRTUAL or PHYSICAL)',
|
||||
optional: true,
|
||||
},
|
||||
limit_type: { type: 'string', description: 'Limit type (CARD or USER)' },
|
||||
spend_controls: {
|
||||
type: 'json',
|
||||
description: 'Spend controls on the card',
|
||||
optional: true,
|
||||
},
|
||||
billing_address: { type: 'json', description: 'Billing address of the card' },
|
||||
expiration_date: { type: 'json', description: 'Card expiration date (month, year)' },
|
||||
budget_id: { type: 'string', description: 'Associated budget ID', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
import type { BrexListCashAccountsResponse, BrexPaginationParams } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListCashAccountsTool: ToolConfig<
|
||||
BrexPaginationParams,
|
||||
BrexListCashAccountsResponse
|
||||
> = {
|
||||
id: 'brex_list_cash_accounts',
|
||||
name: 'Brex List Cash Accounts',
|
||||
description: 'List all Brex cash accounts with balances and account details',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of accounts to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
return queryString
|
||||
? `${BREX_API_BASE}/v2/accounts/cash?${queryString}`
|
||||
: `${BREX_API_BASE}/v2/accounts/cash`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Cash accounts',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique account ID' },
|
||||
name: { type: 'string', description: 'Account name' },
|
||||
status: { type: 'string', description: 'Account status', optional: true },
|
||||
current_balance: {
|
||||
type: 'json',
|
||||
description: 'Current balance',
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
available_balance: {
|
||||
type: 'json',
|
||||
description: 'Available balance',
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
account_number: { type: 'string', description: 'Bank account number' },
|
||||
routing_number: { type: 'string', description: 'Bank routing number' },
|
||||
primary: { type: 'boolean', description: 'Whether this is the primary cash account' },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import type { BrexListCashStatementsParams, BrexListStatementsResponse } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListCashStatementsTool: ToolConfig<
|
||||
BrexListCashStatementsParams,
|
||||
BrexListStatementsResponse
|
||||
> = {
|
||||
id: 'brex_list_cash_statements',
|
||||
name: 'Brex List Cash Statements',
|
||||
description: 'List finalized statements for a Brex cash account',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
accountId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the cash account to list statements for',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of statements to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
const base = `${BREX_API_BASE}/v2/accounts/cash/${encodeURIComponent(params.accountId.trim())}/statements`
|
||||
return queryString ? `${base}?${queryString}` : base
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Finalized cash account statements',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique statement ID' },
|
||||
start_balance: {
|
||||
type: 'json',
|
||||
description: 'Balance at the start of the period',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
end_balance: {
|
||||
type: 'json',
|
||||
description: 'Balance at the end of the period',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
period: { type: 'json', description: 'Statement period (start_date, end_date)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
import type {
|
||||
BrexListCashTransactionsParams,
|
||||
BrexListCashTransactionsResponse,
|
||||
} from '@/tools/brex/types'
|
||||
import { BREX_CASH_TRANSACTION_PROPERTIES } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
toBrexDateTime,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListCashTransactionsTool: ToolConfig<
|
||||
BrexListCashTransactionsParams,
|
||||
BrexListCashTransactionsResponse
|
||||
> = {
|
||||
id: 'brex_list_cash_transactions',
|
||||
name: 'Brex List Cash Transactions',
|
||||
description: 'List transactions for a Brex cash account',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
accountId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the cash account to list transactions for',
|
||||
},
|
||||
postedAtStart: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include transactions posted at or after this ISO 8601 timestamp',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of transactions to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
if (params.postedAtStart)
|
||||
query.append('posted_at_start', toBrexDateTime(params.postedAtStart))
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
const base = `${BREX_API_BASE}/v2/transactions/cash/${encodeURIComponent(params.accountId.trim())}`
|
||||
return queryString ? `${base}?${queryString}` : base
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Cash account transactions',
|
||||
items: { type: 'json', properties: BREX_CASH_TRANSACTION_PROPERTIES },
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import type { BrexListDepartmentsResponse, BrexNameFilterParams } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListDepartmentsTool: ToolConfig<
|
||||
BrexNameFilterParams,
|
||||
BrexListDepartmentsResponse
|
||||
> = {
|
||||
id: 'brex_list_departments',
|
||||
name: 'Brex List Departments',
|
||||
description: 'List departments in the Brex account, optionally filtered by name',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter departments by name',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of departments to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
if (params.name) query.append('name', params.name.trim())
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
return queryString
|
||||
? `${BREX_API_BASE}/v2/departments?${queryString}`
|
||||
: `${BREX_API_BASE}/v2/departments`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Departments in the Brex account',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique department ID' },
|
||||
name: { type: 'string', description: 'Department name' },
|
||||
description: { type: 'string', description: 'Department description', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
import type { BrexListExpensesParams, BrexListExpensesResponse } from '@/tools/brex/types'
|
||||
import { BREX_EXPENSE_ITEM_PROPERTIES } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexArrayParam,
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
toBrexDateTime,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
const EXPAND_FIELDS = [
|
||||
'merchant',
|
||||
'user',
|
||||
'budget',
|
||||
'department',
|
||||
'location',
|
||||
'receipts.download_uris',
|
||||
]
|
||||
|
||||
export const brexListExpensesTool: ToolConfig<BrexListExpensesParams, BrexListExpensesResponse> = {
|
||||
id: 'brex_list_expenses',
|
||||
name: 'Brex List Expenses',
|
||||
description:
|
||||
'List expenses in the Brex account with optional filters for user, status, payment status, and purchase date range',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
userIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user IDs to filter expenses by owner',
|
||||
},
|
||||
statuses: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated expense statuses to filter by: DRAFT, SUBMITTED, APPROVED, OUT_OF_POLICY, VOID, CANCELED, SPLIT, SETTLED',
|
||||
},
|
||||
paymentStatuses: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description:
|
||||
'Comma-separated payment statuses to filter by: NOT_STARTED, PROCESSING, CANCELED, DECLINED, CLEARED, REFUNDING, REFUNDED, CASH_ADVANCE, CREDITED, AWAITING_PAYMENT, SCHEDULED',
|
||||
},
|
||||
purchasedAtStart: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include expenses purchased at or after this ISO 8601 timestamp',
|
||||
},
|
||||
purchasedAtEnd: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Only include expenses purchased at or before this ISO 8601 timestamp',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of expenses to return (max 100)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
for (const field of EXPAND_FIELDS) {
|
||||
query.append('expand[]', field)
|
||||
}
|
||||
appendBrexArrayParam(query, 'user_id[]', params.userIds)
|
||||
appendBrexArrayParam(query, 'status[]', params.statuses)
|
||||
appendBrexArrayParam(query, 'payment_status[]', params.paymentStatuses)
|
||||
if (params.purchasedAtStart)
|
||||
query.append('purchased_at_start', toBrexDateTime(params.purchasedAtStart))
|
||||
if (params.purchasedAtEnd)
|
||||
query.append('purchased_at_end', toBrexDateTime(params.purchasedAtEnd))
|
||||
appendBrexPagination(query, params)
|
||||
return `${BREX_API_BASE}/v1/expenses?${query.toString()}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Expenses matching the filters',
|
||||
items: { type: 'json', properties: BREX_EXPENSE_ITEM_PROPERTIES },
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
import type { BrexListLocationsResponse, BrexNameFilterParams } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListLocationsTool: ToolConfig<BrexNameFilterParams, BrexListLocationsResponse> = {
|
||||
id: 'brex_list_locations',
|
||||
name: 'Brex List Locations',
|
||||
description: 'List locations in the Brex account, optionally filtered by name',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter locations by name',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of locations to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
if (params.name) query.append('name', params.name.trim())
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
return queryString
|
||||
? `${BREX_API_BASE}/v2/locations?${queryString}`
|
||||
: `${BREX_API_BASE}/v2/locations`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Locations in the Brex account',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique location ID' },
|
||||
name: { type: 'string', description: 'Location name' },
|
||||
description: { type: 'string', description: 'Location description', optional: true },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
import type { BrexListSpendLimitsParams, BrexListSpendLimitsResponse } from '@/tools/brex/types'
|
||||
import { BREX_SPEND_LIMIT_PERIOD_BALANCE_PROPERTIES } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexArrayParam,
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListSpendLimitsTool: ToolConfig<
|
||||
BrexListSpendLimitsParams,
|
||||
BrexListSpendLimitsResponse
|
||||
> = {
|
||||
id: 'brex_list_spend_limits',
|
||||
name: 'Brex List Spend Limits',
|
||||
description: 'List spend limits in the Brex account, optionally filtered by member user',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
memberUserIds: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Comma-separated user IDs to filter spend limits by member',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of spend limits to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
appendBrexArrayParam(query, 'member_user_id[]', params.memberUserIds)
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
return queryString
|
||||
? `${BREX_API_BASE}/v2/spend_limits?${queryString}`
|
||||
: `${BREX_API_BASE}/v2/spend_limits`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Spend limits in the Brex account',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique spend limit ID' },
|
||||
account_id: { type: 'string', description: 'Account ID the spend limit belongs to' },
|
||||
name: { type: 'string', description: 'Spend limit name' },
|
||||
description: { type: 'string', description: 'Spend limit description', optional: true },
|
||||
parent_budget_id: { type: 'string', description: 'Parent budget ID', optional: true },
|
||||
status: { type: 'string', description: 'Spend limit status' },
|
||||
period_recurrence_type: {
|
||||
type: 'string',
|
||||
description: 'Period recurrence (PER_WEEK, PER_MONTH, PER_QUARTER, PER_YEAR, ONE_TIME)',
|
||||
},
|
||||
spend_type: { type: 'string', description: 'Spend type of the limit' },
|
||||
start_date: { type: 'string', description: 'Spend limit start date', optional: true },
|
||||
end_date: { type: 'string', description: 'Spend limit end date', optional: true },
|
||||
owner_user_ids: { type: 'array', description: 'User IDs of the spend limit owners' },
|
||||
member_user_ids: { type: 'array', description: 'User IDs of the spend limit members' },
|
||||
current_period_balance: {
|
||||
type: 'json',
|
||||
description: 'Spend and rollover amounts for the current period',
|
||||
optional: true,
|
||||
properties: BREX_SPEND_LIMIT_PERIOD_BALANCE_PROPERTIES,
|
||||
},
|
||||
authorization_settings: {
|
||||
type: 'json',
|
||||
description:
|
||||
'Authorization settings (base limit, authorization type, rollover refresh)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import type { BrexListTitlesResponse, BrexNameFilterParams } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListTitlesTool: ToolConfig<BrexNameFilterParams, BrexListTitlesResponse> = {
|
||||
id: 'brex_list_titles',
|
||||
name: 'Brex List Titles',
|
||||
description: 'List job titles in the Brex account, optionally filtered by name',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter titles by name',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of titles to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
if (params.name) query.append('name', params.name.trim())
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
return queryString
|
||||
? `${BREX_API_BASE}/v2/titles?${queryString}`
|
||||
: `${BREX_API_BASE}/v2/titles`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Job titles in the Brex account',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique title ID' },
|
||||
name: { type: 'string', description: 'Title name' },
|
||||
},
|
||||
},
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
import type { BrexListTransfersResponse, BrexPaginationParams } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListTransfersTool: ToolConfig<BrexPaginationParams, BrexListTransfersResponse> = {
|
||||
id: 'brex_list_transfers',
|
||||
name: 'Brex List Transfers',
|
||||
description: 'List money transfers in the Brex account',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of transfers to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
return queryString
|
||||
? `${BREX_API_BASE}/v1/transfers?${queryString}`
|
||||
: `${BREX_API_BASE}/v1/transfers`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Transfers in the Brex account',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique transfer ID' },
|
||||
counterparty: {
|
||||
type: 'json',
|
||||
description: 'Transfer counterparty details',
|
||||
optional: true,
|
||||
},
|
||||
description: { type: 'string', description: 'Transfer description', optional: true },
|
||||
payment_type: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Payment type (ACH, DOMESTIC_WIRE, CHEQUE, INTERNATIONAL_WIRE, BOOK_TRANSFER, STABLECOIN)',
|
||||
},
|
||||
amount: {
|
||||
type: 'json',
|
||||
description: 'Transfer amount',
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
process_date: {
|
||||
type: 'string',
|
||||
description: 'Date the transfer processes',
|
||||
optional: true,
|
||||
},
|
||||
originating_account: {
|
||||
type: 'json',
|
||||
description: 'Account the transfer originates from',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Transfer status (PROCESSING, SCHEDULED, PENDING_APPROVAL, FAILED, PROCESSED)',
|
||||
},
|
||||
cancellation_reason: {
|
||||
type: 'string',
|
||||
description: 'Reason the transfer was canceled',
|
||||
optional: true,
|
||||
},
|
||||
estimated_delivery_date: {
|
||||
type: 'string',
|
||||
description: 'Estimated delivery date',
|
||||
optional: true,
|
||||
},
|
||||
creator_user_id: {
|
||||
type: 'string',
|
||||
description: 'ID of the user who created the transfer',
|
||||
optional: true,
|
||||
},
|
||||
created_at: { type: 'string', description: 'Creation timestamp', optional: true },
|
||||
display_name: { type: 'string', description: 'Transfer display name', optional: true },
|
||||
external_memo: { type: 'string', description: 'External memo', optional: true },
|
||||
is_ppro_enabled: {
|
||||
type: 'boolean',
|
||||
description: 'Whether Principal Protection (PPRO) is enabled',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
import type { BrexListUsersParams, BrexListUsersResponse } from '@/tools/brex/types'
|
||||
import { BREX_USER_PROPERTIES } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListUsersTool: ToolConfig<BrexListUsersParams, BrexListUsersResponse> = {
|
||||
id: 'brex_list_users',
|
||||
name: 'Brex List Users',
|
||||
description: 'List users in the Brex account, optionally filtered by email',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter users by exact email address',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of users to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
if (params.email) query.append('email', params.email.trim())
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
return queryString ? `${BREX_API_BASE}/v2/users?${queryString}` : `${BREX_API_BASE}/v2/users`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Users in the Brex account',
|
||||
items: { type: 'json', properties: BREX_USER_PROPERTIES },
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
import type { BrexListVendorsResponse, BrexNameFilterParams } from '@/tools/brex/types'
|
||||
import {
|
||||
appendBrexPagination,
|
||||
BREX_API_BASE,
|
||||
buildBrexHeaders,
|
||||
parseBrexJson,
|
||||
} from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexListVendorsTool: ToolConfig<BrexNameFilterParams, BrexListVendorsResponse> = {
|
||||
id: 'brex_list_vendors',
|
||||
name: 'Brex List Vendors',
|
||||
description: 'List vendors in the Brex account, optionally filtered by name',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Filter vendors by name',
|
||||
},
|
||||
cursor: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Pagination cursor from a previous response',
|
||||
},
|
||||
limit: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Number of vendors to return (default 100, max 1000)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
const query = new URLSearchParams()
|
||||
if (params.name) query.append('name', params.name.trim())
|
||||
appendBrexPagination(query, params)
|
||||
const queryString = query.toString()
|
||||
return queryString
|
||||
? `${BREX_API_BASE}/v1/vendors?${queryString}`
|
||||
: `${BREX_API_BASE}/v1/vendors`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
items: data.items ?? [],
|
||||
nextCursor: data.next_cursor ?? null,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: 'Vendors in the Brex account',
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Unique vendor ID' },
|
||||
company_name: { type: 'string', description: 'Vendor company name', optional: true },
|
||||
email: { type: 'string', description: 'Vendor email address', optional: true },
|
||||
phone: { type: 'string', description: 'Vendor phone number', optional: true },
|
||||
payment_accounts: {
|
||||
type: 'array',
|
||||
description: 'Payment accounts associated with the vendor',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
nextCursor: {
|
||||
type: 'string',
|
||||
description: 'Cursor for fetching the next page of results',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import type { BrexMatchReceiptParams, BrexUploadReceiptResponse } from '@/tools/brex/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexMatchReceiptTool: ToolConfig<BrexMatchReceiptParams, BrexUploadReceiptResponse> = {
|
||||
id: 'brex_match_receipt',
|
||||
name: 'Brex Match Receipt',
|
||||
description: 'Upload a receipt file and let Brex automatically match it with existing expenses',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
file: {
|
||||
type: 'file',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Receipt file to upload (max 50 MB)',
|
||||
},
|
||||
receiptName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Receipt file name including extension (defaults to the uploaded file name)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/brex/upload-receipt',
|
||||
method: 'POST',
|
||||
headers: () => ({ 'Content-Type': 'application/json' }),
|
||||
body: (params) => ({
|
||||
apiKey: params.apiKey,
|
||||
file: params.file,
|
||||
receiptName: params.receiptName,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || 'Failed to match receipt')
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: data.output,
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
receiptId: { type: 'string', description: 'Unique identifier of the receipt match request' },
|
||||
receiptName: { type: 'string', description: 'Name the receipt was uploaded with' },
|
||||
expenseId: {
|
||||
type: 'string',
|
||||
description: 'Always null for receipt match (Brex matches the receipt asynchronously)',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,955 @@
|
||||
import type { OutputProperty, ToolResponse } from '@/tools/types'
|
||||
|
||||
export interface BrexPaginationParams {
|
||||
apiKey: string
|
||||
cursor?: string
|
||||
limit?: string
|
||||
}
|
||||
|
||||
export interface BrexMoney {
|
||||
amount: number
|
||||
currency: string | null
|
||||
}
|
||||
|
||||
export interface BrexExpenseReceipt {
|
||||
id: string
|
||||
download_uris?: string[]
|
||||
}
|
||||
|
||||
export interface BrexExpense {
|
||||
id: string
|
||||
memo: string | null
|
||||
status: string | null
|
||||
payment_status: string | null
|
||||
expense_type: string | null
|
||||
category: string | null
|
||||
merchant_id: string | null
|
||||
merchant: { raw_descriptor: string; mcc: string; country: string } | null
|
||||
budget_id: string | null
|
||||
budget: { id: string; name: string } | null
|
||||
department_id: string | null
|
||||
department: { id: string; name: string } | null
|
||||
location_id: string | null
|
||||
location: { id: string; name: string } | null
|
||||
user_id: string | null
|
||||
user: { id: string; first_name: string; last_name: string } | null
|
||||
original_amount: BrexMoney | null
|
||||
billing_amount: BrexMoney | null
|
||||
purchased_amount: BrexMoney | null
|
||||
usd_equivalent_amount: BrexMoney | null
|
||||
purchased_at: string | null
|
||||
updated_at: string
|
||||
payment_posted_at: string | null
|
||||
receipts: BrexExpenseReceipt[]
|
||||
dashboard_url: string
|
||||
}
|
||||
|
||||
export interface BrexCardTransaction {
|
||||
id: string
|
||||
card_id: string | null
|
||||
description: string
|
||||
amount: BrexMoney
|
||||
initiated_at_date: string
|
||||
posted_at_date: string
|
||||
type: string | null
|
||||
merchant: { raw_descriptor: string; mcc: string; country: string } | null
|
||||
expense_id: string | null
|
||||
}
|
||||
|
||||
export interface BrexCashTransaction {
|
||||
id: string
|
||||
description: string
|
||||
amount: BrexMoney | null
|
||||
initiated_at_date: string
|
||||
posted_at_date: string
|
||||
type: string | null
|
||||
transfer_id: string | null
|
||||
}
|
||||
|
||||
export interface BrexCardAccount {
|
||||
id: string
|
||||
status: string | null
|
||||
current_balance: BrexMoney | null
|
||||
available_balance: BrexMoney | null
|
||||
account_limit: BrexMoney | null
|
||||
current_statement_period: { start_date: string; end_date: string }
|
||||
}
|
||||
|
||||
export interface BrexCashAccount {
|
||||
id: string
|
||||
name: string
|
||||
status: string | null
|
||||
current_balance: BrexMoney
|
||||
available_balance: BrexMoney
|
||||
account_number: string
|
||||
routing_number: string
|
||||
primary: boolean
|
||||
}
|
||||
|
||||
export interface BrexSpendLimitPeriodBalance {
|
||||
start_date: string | null
|
||||
end_date: string | null
|
||||
start_time: string | null
|
||||
end_time: string | null
|
||||
amount_spent: BrexMoney | null
|
||||
rollover_amount: BrexMoney | null
|
||||
}
|
||||
|
||||
export interface BrexUser {
|
||||
id: string
|
||||
first_name: string
|
||||
last_name: string
|
||||
email: string
|
||||
status: string | null
|
||||
manager_id: string | null
|
||||
department_id: string | null
|
||||
location_id: string | null
|
||||
title_id: string | null
|
||||
}
|
||||
|
||||
export interface BrexDepartment {
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
}
|
||||
|
||||
export interface BrexLocation {
|
||||
id: string
|
||||
name: string
|
||||
description: string | null
|
||||
}
|
||||
|
||||
export interface BrexBudget {
|
||||
budget_id: string
|
||||
account_id: string
|
||||
name: string
|
||||
description: string | null
|
||||
parent_budget_id: string | null
|
||||
owner_user_ids: string[]
|
||||
period_recurrence_type: string
|
||||
start_date: string | null
|
||||
end_date: string | null
|
||||
amount: BrexMoney | null
|
||||
spend_budget_status: string
|
||||
limit_type: string | null
|
||||
}
|
||||
|
||||
export interface BrexSpendLimit {
|
||||
id: string
|
||||
account_id: string
|
||||
name: string
|
||||
description: string | null
|
||||
parent_budget_id: string | null
|
||||
status: string
|
||||
period_recurrence_type: string
|
||||
spend_type: string
|
||||
start_date: string | null
|
||||
end_date: string | null
|
||||
owner_user_ids: string[]
|
||||
member_user_ids: string[]
|
||||
current_period_balance: BrexSpendLimitPeriodBalance | null
|
||||
authorization_settings: Record<string, unknown> | null
|
||||
}
|
||||
|
||||
export interface BrexVendor {
|
||||
id: string
|
||||
company_name: string | null
|
||||
email: string | null
|
||||
phone: string | null
|
||||
payment_accounts: unknown[]
|
||||
}
|
||||
|
||||
export interface BrexTransfer {
|
||||
id: string
|
||||
counterparty: Record<string, unknown> | null
|
||||
description: string | null
|
||||
payment_type: string
|
||||
amount: BrexMoney
|
||||
process_date: string | null
|
||||
originating_account: Record<string, unknown>
|
||||
status: string
|
||||
cancellation_reason: string | null
|
||||
estimated_delivery_date: string | null
|
||||
creator_user_id: string | null
|
||||
created_at: string | null
|
||||
display_name: string | null
|
||||
external_memo: string | null
|
||||
is_ppro_enabled: boolean | null
|
||||
}
|
||||
|
||||
export interface BrexCard {
|
||||
id: string
|
||||
owner: Record<string, unknown>
|
||||
status: string | null
|
||||
last_four: string
|
||||
card_name: string
|
||||
card_type: string | null
|
||||
limit_type: string
|
||||
spend_controls: Record<string, unknown> | null
|
||||
billing_address: Record<string, unknown>
|
||||
expiration_date: Record<string, unknown>
|
||||
budget_id: string | null
|
||||
}
|
||||
|
||||
export interface BrexStatement {
|
||||
id: string
|
||||
start_balance: BrexMoney | null
|
||||
end_balance: BrexMoney | null
|
||||
period: { start_date: string; end_date: string }
|
||||
}
|
||||
|
||||
export interface BrexTitle {
|
||||
id: string
|
||||
name: string
|
||||
}
|
||||
|
||||
export interface BrexListExpensesParams extends BrexPaginationParams {
|
||||
userIds?: string
|
||||
statuses?: string
|
||||
paymentStatuses?: string
|
||||
purchasedAtStart?: string
|
||||
purchasedAtEnd?: string
|
||||
}
|
||||
|
||||
export interface BrexGetExpenseParams {
|
||||
apiKey: string
|
||||
expenseId: string
|
||||
}
|
||||
|
||||
export interface BrexUpdateExpenseParams {
|
||||
apiKey: string
|
||||
expenseId: string
|
||||
memo: string
|
||||
}
|
||||
|
||||
export interface BrexUploadReceiptParams {
|
||||
apiKey: string
|
||||
expenseId: string
|
||||
file?: unknown
|
||||
receiptName?: string
|
||||
}
|
||||
|
||||
export interface BrexMatchReceiptParams {
|
||||
apiKey: string
|
||||
file?: unknown
|
||||
receiptName?: string
|
||||
}
|
||||
|
||||
export interface BrexListCardTransactionsParams extends BrexPaginationParams {
|
||||
userIds?: string
|
||||
postedAtStart?: string
|
||||
}
|
||||
|
||||
export interface BrexListCashTransactionsParams extends BrexPaginationParams {
|
||||
accountId: string
|
||||
postedAtStart?: string
|
||||
}
|
||||
|
||||
export interface BrexListUsersParams extends BrexPaginationParams {
|
||||
email?: string
|
||||
}
|
||||
|
||||
export interface BrexGetUserParams {
|
||||
apiKey: string
|
||||
userId: string
|
||||
}
|
||||
|
||||
export interface BrexNameFilterParams extends BrexPaginationParams {
|
||||
name?: string
|
||||
}
|
||||
|
||||
export interface BrexListSpendLimitsParams extends BrexPaginationParams {
|
||||
memberUserIds?: string
|
||||
}
|
||||
|
||||
export interface BrexApiKeyParams {
|
||||
apiKey: string
|
||||
}
|
||||
|
||||
export interface BrexGetCashAccountParams {
|
||||
apiKey: string
|
||||
accountId?: string
|
||||
}
|
||||
|
||||
export interface BrexListCardsParams extends BrexPaginationParams {
|
||||
userId?: string
|
||||
}
|
||||
|
||||
export interface BrexListCashStatementsParams extends BrexPaginationParams {
|
||||
accountId: string
|
||||
}
|
||||
|
||||
export interface BrexGetBudgetParams {
|
||||
apiKey: string
|
||||
budgetId: string
|
||||
}
|
||||
|
||||
export interface BrexGetSpendLimitParams {
|
||||
apiKey: string
|
||||
spendLimitId: string
|
||||
}
|
||||
|
||||
export interface BrexGetVendorParams {
|
||||
apiKey: string
|
||||
vendorId: string
|
||||
}
|
||||
|
||||
export interface BrexGetTransferParams {
|
||||
apiKey: string
|
||||
transferId: string
|
||||
}
|
||||
|
||||
export interface BrexCreateTransferParams {
|
||||
apiKey: string
|
||||
cashAccountId: string
|
||||
vendorPaymentInstrumentId: string
|
||||
amount: number
|
||||
currency?: string
|
||||
description: string
|
||||
externalMemo: string
|
||||
approvalType?: string
|
||||
isPproEnabled?: boolean
|
||||
}
|
||||
|
||||
export interface BrexCreateBudgetParams {
|
||||
apiKey: string
|
||||
name: string
|
||||
description: string
|
||||
parentBudgetId: string
|
||||
periodRecurrenceType: string
|
||||
amount: number
|
||||
currency?: string
|
||||
ownerUserIds?: string
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
}
|
||||
|
||||
export interface BrexArchiveBudgetParams {
|
||||
apiKey: string
|
||||
budgetId: string
|
||||
}
|
||||
|
||||
export interface BrexCreateSpendLimitParams {
|
||||
apiKey: string
|
||||
name: string
|
||||
periodRecurrenceType: string
|
||||
spendType: string
|
||||
expenseVisibility: string
|
||||
authorizationVisibility: string
|
||||
limitIncreaseSetting: string
|
||||
autoTransferCardsSetting: string
|
||||
autoCreateLimitCardsSetting: string
|
||||
expensePolicyId: string
|
||||
baseLimitAmount: number
|
||||
currency?: string
|
||||
authorizationType: string
|
||||
rolloverRefreshRate: string
|
||||
limitBufferPercentage?: number
|
||||
description?: string
|
||||
parentBudgetId?: string
|
||||
startDate?: string
|
||||
endDate?: string
|
||||
transactionLimitAmount?: number
|
||||
ownerUserIds?: string
|
||||
memberUserIds?: string
|
||||
}
|
||||
|
||||
export interface BrexCreateVendorParams {
|
||||
apiKey: string
|
||||
companyName: string
|
||||
email?: string
|
||||
phone?: string
|
||||
}
|
||||
|
||||
export interface BrexUpdateVendorParams {
|
||||
apiKey: string
|
||||
vendorId: string
|
||||
companyName?: string
|
||||
email?: string
|
||||
phone?: string
|
||||
}
|
||||
|
||||
export interface BrexListExpensesResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexExpense[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexGetExpenseResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
memo: string | null
|
||||
status: string | null
|
||||
paymentStatus: string | null
|
||||
expenseType: string | null
|
||||
category: string | null
|
||||
merchantId: string | null
|
||||
merchant: BrexExpense['merchant']
|
||||
budgetId: string | null
|
||||
budget: BrexExpense['budget']
|
||||
departmentId: string | null
|
||||
department: BrexExpense['department']
|
||||
locationId: string | null
|
||||
location: BrexExpense['location']
|
||||
userId: string | null
|
||||
user: BrexExpense['user']
|
||||
originalAmount: BrexMoney | null
|
||||
billingAmount: BrexMoney | null
|
||||
purchasedAmount: BrexMoney | null
|
||||
usdEquivalentAmount: BrexMoney | null
|
||||
purchasedAt: string | null
|
||||
updatedAt: string
|
||||
paymentPostedAt: string | null
|
||||
receipts: BrexExpenseReceipt[]
|
||||
dashboardUrl: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexUpdateExpenseResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
memo: string | null
|
||||
status: string | null
|
||||
paymentStatus: string | null
|
||||
category: string | null
|
||||
merchantId: string | null
|
||||
budgetId: string | null
|
||||
originalAmount: BrexMoney | null
|
||||
billingAmount: BrexMoney | null
|
||||
purchasedAt: string | null
|
||||
updatedAt: string
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexUploadReceiptResponse extends ToolResponse {
|
||||
output: {
|
||||
receiptId: string
|
||||
receiptName: string
|
||||
expenseId: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListCardTransactionsResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexCardTransaction[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListCashTransactionsResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexCashTransaction[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListCardAccountsResponse extends ToolResponse {
|
||||
output: {
|
||||
accounts: BrexCardAccount[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListCashAccountsResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexCashAccount[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListUsersResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexUser[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexGetUserResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
firstName: string
|
||||
lastName: string
|
||||
email: string
|
||||
status: string | null
|
||||
managerId: string | null
|
||||
departmentId: string | null
|
||||
locationId: string | null
|
||||
titleId: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListDepartmentsResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexDepartment[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListLocationsResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexLocation[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListBudgetsResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexBudget[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListSpendLimitsResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexSpendLimit[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListVendorsResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexVendor[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListTransfersResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexTransfer[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexGetCompanyResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
legalName: string
|
||||
mailingAddress: Record<string, unknown> | null
|
||||
accountType: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListCardsResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexCard[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListTitlesResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexTitle[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexGetCashAccountResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
name: string
|
||||
status: string | null
|
||||
currentBalance: BrexMoney
|
||||
availableBalance: BrexMoney
|
||||
accountNumber: string
|
||||
routingNumber: string
|
||||
primary: boolean
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexListStatementsResponse extends ToolResponse {
|
||||
output: {
|
||||
items: BrexStatement[]
|
||||
nextCursor: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexGetBudgetResponse extends ToolResponse {
|
||||
output: {
|
||||
budgetId: string
|
||||
accountId: string
|
||||
name: string
|
||||
description: string | null
|
||||
parentBudgetId: string | null
|
||||
ownerUserIds: string[]
|
||||
periodRecurrenceType: string
|
||||
startDate: string | null
|
||||
endDate: string | null
|
||||
amount: BrexMoney | null
|
||||
spendBudgetStatus: string
|
||||
limitType: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexGetSpendLimitResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
accountId: string
|
||||
name: string
|
||||
description: string | null
|
||||
parentBudgetId: string | null
|
||||
status: string
|
||||
periodRecurrenceType: string
|
||||
spendType: string
|
||||
startDate: string | null
|
||||
endDate: string | null
|
||||
ownerUserIds: string[]
|
||||
memberUserIds: string[]
|
||||
currentPeriodBalance: BrexSpendLimitPeriodBalance | null
|
||||
authorizationSettings: Record<string, unknown> | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexGetVendorResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
companyName: string | null
|
||||
email: string | null
|
||||
phone: string | null
|
||||
paymentAccounts: unknown[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexGetTransferResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
counterparty: Record<string, unknown> | null
|
||||
description: string | null
|
||||
paymentType: string
|
||||
amount: BrexMoney | null
|
||||
processDate: string | null
|
||||
originatingAccount: Record<string, unknown> | null
|
||||
status: string
|
||||
cancellationReason: string | null
|
||||
estimatedDeliveryDate: string | null
|
||||
creatorUserId: string | null
|
||||
createdAt: string | null
|
||||
displayName: string | null
|
||||
externalMemo: string | null
|
||||
isPproEnabled: boolean | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexCreateTransferResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
counterparty: Record<string, unknown> | null
|
||||
description: string | null
|
||||
paymentType: string
|
||||
amount: BrexMoney | null
|
||||
processDate: string | null
|
||||
originatingAccount: Record<string, unknown> | null
|
||||
status: string
|
||||
cancellationReason: string | null
|
||||
estimatedDeliveryDate: string | null
|
||||
creatorUserId: string | null
|
||||
createdAt: string | null
|
||||
displayName: string | null
|
||||
externalMemo: string | null
|
||||
isPproEnabled: boolean | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexCreateBudgetResponse extends ToolResponse {
|
||||
output: {
|
||||
budgetId: string
|
||||
accountId: string
|
||||
name: string
|
||||
description: string | null
|
||||
parentBudgetId: string | null
|
||||
ownerUserIds: string[]
|
||||
periodRecurrenceType: string
|
||||
startDate: string | null
|
||||
endDate: string | null
|
||||
amount: BrexMoney | null
|
||||
spendBudgetStatus: string
|
||||
limitType: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexArchiveBudgetResponse extends ToolResponse {
|
||||
output: {
|
||||
budgetId: string
|
||||
spendBudgetStatus: string | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexCreateSpendLimitResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
accountId: string
|
||||
name: string
|
||||
description: string | null
|
||||
parentBudgetId: string | null
|
||||
status: string
|
||||
periodRecurrenceType: string
|
||||
spendType: string
|
||||
startDate: string | null
|
||||
endDate: string | null
|
||||
ownerUserIds: string[]
|
||||
memberUserIds: string[]
|
||||
currentPeriodBalance: BrexSpendLimitPeriodBalance | null
|
||||
authorizationSettings: Record<string, unknown> | null
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexCreateVendorResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
companyName: string | null
|
||||
email: string | null
|
||||
phone: string | null
|
||||
paymentAccounts: unknown[]
|
||||
}
|
||||
}
|
||||
|
||||
export interface BrexUpdateVendorResponse extends ToolResponse {
|
||||
output: {
|
||||
id: string
|
||||
companyName: string | null
|
||||
email: string | null
|
||||
phone: string | null
|
||||
paymentAccounts: unknown[]
|
||||
}
|
||||
}
|
||||
|
||||
export type BrexResponse =
|
||||
| BrexListExpensesResponse
|
||||
| BrexGetExpenseResponse
|
||||
| BrexUpdateExpenseResponse
|
||||
| BrexUploadReceiptResponse
|
||||
| BrexListCardTransactionsResponse
|
||||
| BrexListCashTransactionsResponse
|
||||
| BrexListCardAccountsResponse
|
||||
| BrexListCashAccountsResponse
|
||||
| BrexListUsersResponse
|
||||
| BrexGetUserResponse
|
||||
| BrexListDepartmentsResponse
|
||||
| BrexListLocationsResponse
|
||||
| BrexListBudgetsResponse
|
||||
| BrexListSpendLimitsResponse
|
||||
| BrexListVendorsResponse
|
||||
| BrexListTransfersResponse
|
||||
| BrexGetCompanyResponse
|
||||
| BrexListCardsResponse
|
||||
| BrexListTitlesResponse
|
||||
| BrexGetCashAccountResponse
|
||||
| BrexListStatementsResponse
|
||||
| BrexGetBudgetResponse
|
||||
| BrexGetSpendLimitResponse
|
||||
| BrexGetVendorResponse
|
||||
| BrexGetTransferResponse
|
||||
| BrexCreateTransferResponse
|
||||
| BrexCreateBudgetResponse
|
||||
| BrexArchiveBudgetResponse
|
||||
| BrexCreateSpendLimitResponse
|
||||
| BrexCreateVendorResponse
|
||||
| BrexUpdateVendorResponse
|
||||
|
||||
export const BREX_MONEY_PROPERTIES: Record<string, OutputProperty> = {
|
||||
amount: {
|
||||
type: 'number',
|
||||
description: 'Amount in the smallest unit of the currency (e.g., cents for USD)',
|
||||
},
|
||||
currency: {
|
||||
type: 'string',
|
||||
description: 'ISO 4217 currency code (e.g., USD)',
|
||||
optional: true,
|
||||
},
|
||||
}
|
||||
|
||||
export const BREX_SPEND_LIMIT_PERIOD_BALANCE_PROPERTIES: Record<string, OutputProperty> = {
|
||||
start_date: { type: 'string', description: 'Start date of the current period', optional: true },
|
||||
end_date: { type: 'string', description: 'End date of the current period', optional: true },
|
||||
start_time: {
|
||||
type: 'string',
|
||||
description: 'Start time of the current period (ISO 8601)',
|
||||
optional: true,
|
||||
},
|
||||
end_time: {
|
||||
type: 'string',
|
||||
description: 'End time of the current period (ISO 8601)',
|
||||
optional: true,
|
||||
},
|
||||
amount_spent: {
|
||||
type: 'json',
|
||||
description: 'Amount spent in the current period',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
rollover_amount: {
|
||||
type: 'json',
|
||||
description: 'Amount rolled over from previous periods',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
}
|
||||
|
||||
export const BREX_EXPENSE_ITEM_PROPERTIES: Record<string, OutputProperty> = {
|
||||
id: { type: 'string', description: 'Unique expense ID' },
|
||||
memo: { type: 'string', description: 'Memo on the expense', optional: true },
|
||||
status: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Expense status (DRAFT, SUBMITTED, APPROVED, OUT_OF_POLICY, VOID, CANCELED, SPLIT, SETTLED)',
|
||||
optional: true,
|
||||
},
|
||||
payment_status: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Payment status (NOT_STARTED, PROCESSING, CANCELED, DECLINED, CLEARED, REFUNDING, REFUNDED, CASH_ADVANCE, CREDITED, AWAITING_PAYMENT, SCHEDULED)',
|
||||
optional: true,
|
||||
},
|
||||
expense_type: {
|
||||
type: 'string',
|
||||
description: 'Expense type (CARD, BILLPAY, REIMBURSEMENT, CLAWBACK, UNSET)',
|
||||
optional: true,
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Expense category (e.g., RESTAURANTS, RECURRING_SOFTWARE_AND_SAAS, AIRLINE_EXPENSES)',
|
||||
optional: true,
|
||||
},
|
||||
merchant: {
|
||||
type: 'json',
|
||||
description: 'Merchant details',
|
||||
optional: true,
|
||||
properties: {
|
||||
raw_descriptor: { type: 'string', description: 'Raw merchant descriptor' },
|
||||
mcc: { type: 'string', description: 'Merchant category code' },
|
||||
country: { type: 'string', description: 'Merchant country' },
|
||||
},
|
||||
},
|
||||
user: {
|
||||
type: 'json',
|
||||
description: 'User who made the expense',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'User ID' },
|
||||
first_name: { type: 'string', description: 'First name' },
|
||||
last_name: { type: 'string', description: 'Last name' },
|
||||
},
|
||||
},
|
||||
budget: {
|
||||
type: 'json',
|
||||
description: 'Budget the expense belongs to',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Budget ID' },
|
||||
name: { type: 'string', description: 'Budget name' },
|
||||
},
|
||||
},
|
||||
department: {
|
||||
type: 'json',
|
||||
description: 'Department of the expense owner',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Department ID' },
|
||||
name: { type: 'string', description: 'Department name' },
|
||||
},
|
||||
},
|
||||
location: {
|
||||
type: 'json',
|
||||
description: 'Location of the expense owner',
|
||||
optional: true,
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Location ID' },
|
||||
name: { type: 'string', description: 'Location name' },
|
||||
},
|
||||
},
|
||||
original_amount: {
|
||||
type: 'json',
|
||||
description: 'Original transaction amount',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
billing_amount: {
|
||||
type: 'json',
|
||||
description: 'Amount billed to the account',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
purchased_amount: {
|
||||
type: 'json',
|
||||
description: 'Amount at the time of purchase',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
receipts: {
|
||||
type: 'array',
|
||||
description: 'Receipts attached to the expense',
|
||||
optional: true,
|
||||
items: {
|
||||
type: 'json',
|
||||
properties: {
|
||||
id: { type: 'string', description: 'Receipt ID' },
|
||||
download_uris: { type: 'array', description: 'Pre-signed receipt download URLs' },
|
||||
},
|
||||
},
|
||||
},
|
||||
purchased_at: { type: 'string', description: 'Purchase timestamp (ISO 8601)', optional: true },
|
||||
updated_at: { type: 'string', description: 'Last update timestamp (ISO 8601)' },
|
||||
dashboard_url: { type: 'string', description: 'Link to the expense in the Brex dashboard' },
|
||||
}
|
||||
|
||||
export const BREX_CARD_TRANSACTION_PROPERTIES: Record<string, OutputProperty> = {
|
||||
id: { type: 'string', description: 'Unique transaction ID' },
|
||||
card_id: { type: 'string', description: 'ID of the card used', optional: true },
|
||||
description: { type: 'string', description: 'Transaction description' },
|
||||
amount: {
|
||||
type: 'json',
|
||||
description: 'Transaction amount',
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
initiated_at_date: { type: 'string', description: 'Date the transaction was initiated' },
|
||||
posted_at_date: { type: 'string', description: 'Date the transaction was posted' },
|
||||
type: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Transaction type (PURCHASE, REFUND, CHARGEBACK, REWARDS_CREDIT, COLLECTION, BNPL_FEE)',
|
||||
optional: true,
|
||||
},
|
||||
merchant: {
|
||||
type: 'json',
|
||||
description: 'Merchant details',
|
||||
optional: true,
|
||||
properties: {
|
||||
raw_descriptor: { type: 'string', description: 'Raw merchant descriptor' },
|
||||
mcc: { type: 'string', description: 'Merchant category code' },
|
||||
country: { type: 'string', description: 'Merchant country' },
|
||||
},
|
||||
},
|
||||
expense_id: { type: 'string', description: 'Associated expense ID', optional: true },
|
||||
}
|
||||
|
||||
export const BREX_CASH_TRANSACTION_PROPERTIES: Record<string, OutputProperty> = {
|
||||
id: { type: 'string', description: 'Unique transaction ID' },
|
||||
description: { type: 'string', description: 'Transaction description' },
|
||||
amount: {
|
||||
type: 'json',
|
||||
description: 'Transaction amount',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
initiated_at_date: { type: 'string', description: 'Date the transaction was initiated' },
|
||||
posted_at_date: { type: 'string', description: 'Date the transaction was posted' },
|
||||
type: { type: 'string', description: 'Transaction type', optional: true },
|
||||
transfer_id: { type: 'string', description: 'Associated transfer ID', optional: true },
|
||||
}
|
||||
|
||||
export const BREX_USER_PROPERTIES: Record<string, OutputProperty> = {
|
||||
id: { type: 'string', description: 'Unique user ID' },
|
||||
first_name: { type: 'string', description: 'First name' },
|
||||
last_name: { type: 'string', description: 'Last name' },
|
||||
email: { type: 'string', description: 'Email address' },
|
||||
status: {
|
||||
type: 'string',
|
||||
description:
|
||||
'User status (INVITED, ACTIVE, CLOSED, DISABLED, DELETED, PENDING_ACTIVATION, INACTIVE, ARCHIVED)',
|
||||
optional: true,
|
||||
},
|
||||
manager_id: { type: 'string', description: 'ID of the manager', optional: true },
|
||||
department_id: { type: 'string', description: 'Department ID', optional: true },
|
||||
location_id: { type: 'string', description: 'Location ID', optional: true },
|
||||
title_id: { type: 'string', description: 'Title ID', optional: true },
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import type { BrexUpdateExpenseParams, BrexUpdateExpenseResponse } from '@/tools/brex/types'
|
||||
import { BREX_MONEY_PROPERTIES } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexUpdateExpenseTool: ToolConfig<BrexUpdateExpenseParams, BrexUpdateExpenseResponse> =
|
||||
{
|
||||
id: 'brex_update_expense',
|
||||
name: 'Brex Update Expense',
|
||||
description: 'Update the memo of a Brex card expense',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
expenseId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the card expense to update',
|
||||
},
|
||||
memo: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New memo for the expense',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) =>
|
||||
`${BREX_API_BASE}/v1/expenses/card/${encodeURIComponent(params.expenseId.trim())}`,
|
||||
method: 'PUT',
|
||||
headers: (params) => buildBrexHeaders(params.apiKey),
|
||||
body: (params) => ({ memo: params.memo }),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
memo: data.memo ?? null,
|
||||
status: data.status ?? null,
|
||||
paymentStatus: data.payment_status ?? null,
|
||||
category: data.category ?? null,
|
||||
merchantId: data.merchant_id ?? null,
|
||||
budgetId: data.budget_id ?? null,
|
||||
originalAmount: data.original_amount ?? null,
|
||||
billingAmount: data.billing_amount ?? null,
|
||||
purchasedAt: data.purchased_at ?? null,
|
||||
updatedAt: data.updated_at ?? '',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique expense ID' },
|
||||
memo: { type: 'string', description: 'Updated memo on the expense', optional: true },
|
||||
status: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Expense status (DRAFT, SUBMITTED, APPROVED, OUT_OF_POLICY, VOID, CANCELED, SPLIT, SETTLED)',
|
||||
optional: true,
|
||||
},
|
||||
paymentStatus: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Payment status (NOT_STARTED, PROCESSING, CANCELED, DECLINED, CLEARED, REFUNDING, REFUNDED, CASH_ADVANCE, CREDITED, AWAITING_PAYMENT, SCHEDULED)',
|
||||
optional: true,
|
||||
},
|
||||
category: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Expense category (e.g., RESTAURANTS, RECURRING_SOFTWARE_AND_SAAS, AIRLINE_EXPENSES)',
|
||||
optional: true,
|
||||
},
|
||||
merchantId: { type: 'string', description: 'Merchant ID', optional: true },
|
||||
budgetId: { type: 'string', description: 'Budget ID', optional: true },
|
||||
originalAmount: {
|
||||
type: 'json',
|
||||
description: 'Original transaction amount',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
billingAmount: {
|
||||
type: 'json',
|
||||
description: 'Amount billed to the account',
|
||||
optional: true,
|
||||
properties: BREX_MONEY_PROPERTIES,
|
||||
},
|
||||
purchasedAt: { type: 'string', description: 'Purchase timestamp (ISO 8601)', optional: true },
|
||||
updatedAt: { type: 'string', description: 'Last update timestamp (ISO 8601)' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import type { BrexUpdateVendorParams, BrexUpdateVendorResponse } from '@/tools/brex/types'
|
||||
import { BREX_API_BASE, buildBrexHeaders, parseBrexJson } from '@/tools/brex/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexUpdateVendorTool: ToolConfig<BrexUpdateVendorParams, BrexUpdateVendorResponse> = {
|
||||
id: 'brex_update_vendor',
|
||||
name: 'Brex Update Vendor',
|
||||
description: 'Update an existing vendor in the Brex account',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
vendorId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the vendor to update',
|
||||
},
|
||||
companyName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New name for the vendor',
|
||||
},
|
||||
email: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New email address for the vendor',
|
||||
},
|
||||
phone: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'New phone number for the vendor',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => `${BREX_API_BASE}/v1/vendors/${encodeURIComponent(params.vendorId.trim())}`,
|
||||
method: 'PUT',
|
||||
headers: (params) => ({
|
||||
...buildBrexHeaders(params.apiKey),
|
||||
// Optional per Brex's spec for this endpoint, but included for safe-retry semantics.
|
||||
'Idempotency-Key': generateId(),
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {}
|
||||
if (params.companyName) body.company_name = params.companyName
|
||||
if (params.email) body.email = params.email
|
||||
if (params.phone) body.phone = params.phone
|
||||
if (Object.keys(body).length === 0) {
|
||||
throw new Error(
|
||||
'At least one of company name, email, or phone must be provided to update the vendor'
|
||||
)
|
||||
}
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await parseBrexJson(response)
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
id: data.id ?? '',
|
||||
companyName: data.company_name ?? null,
|
||||
email: data.email ?? null,
|
||||
phone: data.phone ?? null,
|
||||
paymentAccounts: data.payment_accounts ?? [],
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
id: { type: 'string', description: 'Unique vendor ID' },
|
||||
companyName: { type: 'string', description: 'Vendor company name', optional: true },
|
||||
email: { type: 'string', description: 'Vendor email address', optional: true },
|
||||
phone: { type: 'string', description: 'Vendor phone number', optional: true },
|
||||
paymentAccounts: { type: 'array', description: 'Payment accounts associated with the vendor' },
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import type { BrexUploadReceiptParams, BrexUploadReceiptResponse } from '@/tools/brex/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const brexUploadReceiptTool: ToolConfig<BrexUploadReceiptParams, BrexUploadReceiptResponse> =
|
||||
{
|
||||
id: 'brex_upload_receipt',
|
||||
name: 'Brex Upload Receipt',
|
||||
description: 'Upload a receipt file and attach it to a specific Brex card expense',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Brex user token (generated from Developer Settings in the Brex dashboard)',
|
||||
},
|
||||
expenseId: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'ID of the card expense to attach the receipt to',
|
||||
},
|
||||
file: {
|
||||
type: 'file',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Receipt file to upload (max 50 MB)',
|
||||
},
|
||||
receiptName: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Receipt file name including extension (defaults to the uploaded file name)',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: '/api/tools/brex/upload-receipt',
|
||||
method: 'POST',
|
||||
headers: () => ({ 'Content-Type': 'application/json' }),
|
||||
body: (params) => ({
|
||||
apiKey: params.apiKey,
|
||||
expenseId: params.expenseId,
|
||||
file: params.file,
|
||||
receiptName: params.receiptName,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response) => {
|
||||
const data = await response.json()
|
||||
if (!data.success) {
|
||||
throw new Error(data.error || 'Failed to upload receipt')
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
output: data.output,
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
receiptId: { type: 'string', description: 'Unique identifier of the receipt upload' },
|
||||
receiptName: { type: 'string', description: 'Name the receipt was uploaded with' },
|
||||
expenseId: {
|
||||
type: 'string',
|
||||
description: 'ID of the expense the receipt was attached to',
|
||||
optional: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* @vitest-environment node
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { appendBrexArrayParam, appendBrexPagination, toBrexDateTime } from '@/tools/brex/utils'
|
||||
|
||||
describe('toBrexDateTime', () => {
|
||||
it('strips a Z suffix by converting to naive UTC', () => {
|
||||
expect(toBrexDateTime('2026-01-01T00:00:00Z')).toBe('2026-01-01T00:00:00')
|
||||
expect(toBrexDateTime('2026-01-01T12:30:45.123Z')).toBe('2026-01-01T12:30:45')
|
||||
})
|
||||
|
||||
it('converts timezone offsets to UTC before stripping', () => {
|
||||
expect(toBrexDateTime('2026-01-01T02:00:00+02:00')).toBe('2026-01-01T00:00:00')
|
||||
expect(toBrexDateTime('2025-12-31T19:00:00-05:00')).toBe('2026-01-01T00:00:00')
|
||||
})
|
||||
|
||||
it('passes through timestamps without a timezone unchanged', () => {
|
||||
expect(toBrexDateTime('2026-01-01T00:00:00')).toBe('2026-01-01T00:00:00')
|
||||
expect(toBrexDateTime('2026-01-01T00:00:00.000')).toBe('2026-01-01T00:00:00.000')
|
||||
})
|
||||
|
||||
it('passes through unparseable values unchanged', () => {
|
||||
expect(toBrexDateTime('not-a-date-Z')).toBe('not-a-date-Z')
|
||||
})
|
||||
})
|
||||
|
||||
describe('appendBrexArrayParam', () => {
|
||||
it('appends repeated params from a comma-separated value, trimming entries', () => {
|
||||
const query = new URLSearchParams()
|
||||
appendBrexArrayParam(query, 'status[]', ' APPROVED, SETTLED ,, ')
|
||||
expect(query.getAll('status[]')).toEqual(['APPROVED', 'SETTLED'])
|
||||
})
|
||||
|
||||
it('does nothing for an empty value', () => {
|
||||
const query = new URLSearchParams()
|
||||
appendBrexArrayParam(query, 'status[]', undefined)
|
||||
expect(query.toString()).toBe('')
|
||||
})
|
||||
})
|
||||
|
||||
describe('appendBrexPagination', () => {
|
||||
it('appends cursor and limit only when present', () => {
|
||||
const query = new URLSearchParams()
|
||||
appendBrexPagination(query, { cursor: 'abc', limit: '10' })
|
||||
expect(query.get('cursor')).toBe('abc')
|
||||
expect(query.get('limit')).toBe('10')
|
||||
|
||||
const empty = new URLSearchParams()
|
||||
appendBrexPagination(empty, {})
|
||||
expect(empty.toString()).toBe('')
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,78 @@
|
||||
export const BREX_API_BASE = 'https://api.brex.com'
|
||||
|
||||
/**
|
||||
* Builds the standard headers for Brex API requests.
|
||||
*/
|
||||
export function buildBrexHeaders(apiKey: string): Record<string, string> {
|
||||
return {
|
||||
Authorization: `Bearer ${apiKey}`,
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a Brex API response body, throwing a descriptive error for non-2xx responses.
|
||||
*/
|
||||
export async function parseBrexJson(response: Response) {
|
||||
if (!response.ok) {
|
||||
const text = await response.text()
|
||||
let message = text
|
||||
try {
|
||||
const parsed = JSON.parse(text)
|
||||
message = parsed.message ?? text
|
||||
} catch {
|
||||
message = text
|
||||
}
|
||||
throw new Error(`Brex API error (${response.status}): ${message}`)
|
||||
}
|
||||
return response.json()
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a comma-separated value as repeated query parameters (Brex array syntax).
|
||||
*/
|
||||
export function appendBrexArrayParam(query: URLSearchParams, key: string, value?: string): void {
|
||||
if (!value) return
|
||||
for (const item of value.split(',')) {
|
||||
const trimmed = item.trim()
|
||||
if (trimmed) query.append(key, trimmed)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends standard cursor/limit pagination parameters to a query.
|
||||
*/
|
||||
export function appendBrexPagination(
|
||||
query: URLSearchParams,
|
||||
params: { cursor?: string; limit?: string }
|
||||
): void {
|
||||
if (params.cursor) query.append('cursor', params.cursor)
|
||||
if (params.limit) query.append('limit', params.limit)
|
||||
}
|
||||
|
||||
/**
|
||||
* Splits a comma-separated string of IDs into a trimmed, non-empty array for
|
||||
* use in a JSON request body (as opposed to repeated query parameters).
|
||||
*/
|
||||
export function splitBrexIdList(value?: string): string[] | undefined {
|
||||
if (!value) return undefined
|
||||
const ids = value
|
||||
.split(',')
|
||||
.map((id) => id.trim())
|
||||
.filter(Boolean)
|
||||
return ids.length > 0 ? ids : undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a timestamp to the timezone-less date-time form the Brex Transactions
|
||||
* API requires (e.g., 2026-01-01T00:00:00). Brex rejects timezone-suffixed
|
||||
* timestamps on these endpoints, so offsets are converted to UTC and stripped.
|
||||
*/
|
||||
export function toBrexDateTime(value: string): string {
|
||||
const trimmed = value.trim()
|
||||
if (!/(?:z|[+-]\d{2}:?\d{2})$/i.test(trimmed)) return trimmed
|
||||
const parsed = new Date(trimmed)
|
||||
if (Number.isNaN(parsed.getTime())) return trimmed
|
||||
return parsed.toISOString().slice(0, 19)
|
||||
}
|
||||
Reference in New Issue
Block a user