Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions packages/cli/src/__tests__/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -927,6 +927,7 @@ describe('production mode', () => {
currency: 'usd',
created_date: '2026-06-08',
description: 'Chase',
origin: 'external_connection',
category: 'credit_card_payment',
status: 'succeeded',
};
Expand Down Expand Up @@ -976,6 +977,12 @@ describe('production mode', () => {
'2026-04-30',
'--category',
'other_services',
'--origin',
'external_connection',
'--source',
'csmrpd_a',
'--source',
'csmrpd_b',
'--json',
);

Expand All @@ -984,9 +991,14 @@ describe('production mode', () => {
expect(lastRequest.url).toContain('limit=5');
expect(lastRequest.url).toContain('starting_after=lbctxn_cursor');
expect(lastRequest.url).toContain('ending_before=lbctxn_prev');
expect(lastRequest.url).toContain('start_date=2026-04-01');
expect(lastRequest.url).toContain('end_date=2026-04-30');
expect(lastRequest.url).toContain('date_start=2026-04-01');
expect(lastRequest.url).toContain('date_end=2026-04-30');
expect(lastRequest.url).toContain('category=other_services');
expect(lastRequest.url).toContain('origin=external_connection');
expect(lastRequest.url).toContain('sources%5B%5D=csmrpd_a');
expect(lastRequest.url).toContain('sources%5B%5D=csmrpd_b');
expect(lastRequest.url).not.toContain('start_date');
expect(lastRequest.url).not.toContain('end_date');
expect(lastRequest.url).not.toContain('transaction_category');
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ function transaction(overrides: Partial<Transaction> = {}): Transaction {
currency: 'usd',
created_date: '2026-06-08',
description: 'Chase',
origin: 'external_connection',
category: 'credit_card_payment',
status: 'succeeded',
...overrides,
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/commands/transactions/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export function createTransactionsCli(
if (opts.startDate !== undefined) params.start_date = opts.startDate;
if (opts.endDate !== undefined) params.end_date = opts.endDate;
if (opts.category !== undefined) params.category = opts.category;
if (opts.origin !== undefined) params.origin = opts.origin;
if (opts.source.length > 0) params.sources = opts.source;

if (!c.agent && !c.formatExplicit) {
return renderInteractive(
Expand Down
8 changes: 8 additions & 0 deletions packages/cli/src/commands/transactions/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,12 @@ export const listOptions = z.object({
.optional()
.describe('Only include transactions on or before this YYYY-MM-DD date.'),
category: z.string().optional().describe('Filter by transaction category.'),
origin: z
.enum(['link', 'external_connection'])
.optional()
.describe('Filter by transaction origin: link or external_connection.'),
source: z
.array(z.string())
.default([])
.describe('Filter by source ID. Repeat to include multiple sources.'),
});
15 changes: 13 additions & 2 deletions packages/sdk/src/resources/__tests__/transactions.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('TransactionsResource', () => {
currency: 'usd',
created_date: '2026-06-08',
description: 'Chase',
origin: 'external_connection',
category: 'credit_card_payment',
status: 'succeeded',
},
Expand All @@ -63,6 +64,7 @@ describe('TransactionsResource', () => {
currency: 'usd',
created_date: '2026-06-08',
description: 'Chase',
origin: 'external_connection',
category: 'credit_card_payment',
status: 'succeeded',
},
Expand All @@ -81,15 +83,24 @@ describe('TransactionsResource', () => {
start_date: '2026-06-08',
end_date: '2026-06-09',
category: 'shopping',
origin: 'link',
sources: ['csmrpd_a', 'csmrpd_b'],
});

const url = new URL(mockFetch.mock.calls[0][0]);
expect(url.searchParams.get('limit')).toBe('50');
expect(url.searchParams.get('starting_after')).toBe('cursor_a');
expect(url.searchParams.get('ending_before')).toBe('cursor_b');
expect(url.searchParams.get('start_date')).toBe('2026-06-08');
expect(url.searchParams.get('end_date')).toBe('2026-06-09');
expect(url.searchParams.get('date_start')).toBe('2026-06-08');
expect(url.searchParams.get('date_end')).toBe('2026-06-09');
expect(url.searchParams.get('category')).toBe('shopping');
expect(url.searchParams.get('origin')).toBe('link');
expect(url.searchParams.getAll('sources[]')).toEqual([
'csmrpd_a',
'csmrpd_b',
]);
expect(url.searchParams.has('start_date')).toBe(false);
expect(url.searchParams.has('end_date')).toBe(false);
expect(url.searchParams.has('transaction_category')).toBe(false);
});

Expand Down
3 changes: 3 additions & 0 deletions packages/sdk/src/resources/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {
ShippingAddressRecord,
SpendRequest,
Total,
TransactionOrigin,
TransactionsPage,
UserInfo,
WebBotAuthBlock,
Expand Down Expand Up @@ -105,6 +106,8 @@ export interface ListTransactionsParams {
start_date?: string;
end_date?: string;
category?: string;
origin?: TransactionOrigin;
sources?: string[];
}

export interface ITransactionsResource {
Expand Down
32 changes: 29 additions & 3 deletions packages/sdk/src/resources/transactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,11 @@ import type {
ITransactionsResource,
ListTransactionsParams,
} from '@/resources/interfaces';
import type { Transaction, TransactionsPage } from '@/types/index';
import type {
Transaction,
TransactionOrigin,
TransactionsPage,
} from '@/types/index';

interface ApiFetchOptions {
method: string;
Expand Down Expand Up @@ -52,6 +56,16 @@ function requireBoolean(value: unknown, field: string): boolean {
return value;
}

function requireTransactionOrigin(
value: unknown,
field: string,
): TransactionOrigin {
if (value === 'link' || value === 'external_connection') {
return value;
}
throw new TypeError(`Expected ${field} to be a transaction origin`);
}

function normalizeTransactions(value: unknown): Transaction[] {
if (!Array.isArray(value)) {
throw new TypeError('Expected transactions to be an array');
Expand All @@ -78,6 +92,10 @@ function normalizeTransactions(value: unknown): Transaction[] {
item.description,
`transactions[${index}].description`,
),
origin: requireTransactionOrigin(
item.origin,
`transactions[${index}].origin`,
),
category: requireNullableString(
item.category,
`transactions[${index}].category`,
Expand Down Expand Up @@ -203,14 +221,22 @@ export class TransactionsResource implements ITransactionsResource {
url.searchParams.set('ending_before', params.ending_before);
}
if (params.start_date !== undefined) {
url.searchParams.set('start_date', params.start_date);
url.searchParams.set('date_start', params.start_date);
}
if (params.end_date !== undefined) {
url.searchParams.set('end_date', params.end_date);
url.searchParams.set('date_end', params.end_date);
}
if (params.category !== undefined) {
url.searchParams.set('category', params.category);
}
if (params.origin !== undefined) {
url.searchParams.set('origin', params.origin);
}
if (params.sources !== undefined) {
for (const source of params.sources) {
url.searchParams.append('sources[]', source);
}
}

return url.toString();
}
Expand Down
3 changes: 3 additions & 0 deletions packages/sdk/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,16 @@ export interface ShippingAddressRecord {
address: ShippingAddress | null;
}

export type TransactionOrigin = 'link' | 'external_connection';

export interface Transaction {
id: string;
source_id: string | null;
amount: number;
currency: string;
created_date: string;
description: string;
origin: TransactionOrigin;
category: string | null;
status: string;
}
Expand Down
Loading