Skip to main content
Glama

Bunq MCP

by WilcoKruijer
UserAPI.ts11.4 kB
import { BunqContext } from "./BunqContext"; import { formatDateYYYYMMDD } from "../utils"; // Types moved from transaction-filter.ts for centralization export interface LabelUser { uuid?: string | null; display_name?: string; country?: string; avatar?: any; public_nick_name?: string; type?: string | null; [key: string]: any; } export interface CounterpartyAlias { iban?: string | null; is_light?: boolean | null; display_name?: string; avatar?: any; label_user?: LabelUser; country?: string; merchant_category_code?: string; [key: string]: any; } export interface Payment { id: number; created: string; updated: string; monetary_account_id: number; amount: { value: string; currency: string; }; payment_fee?: any; description: string; type: string; merchant_reference?: string | null; alias: any; counterparty_alias: CounterpartyAlias; attachment: any[]; geolocation?: any; batch_id?: number | null; scheduled_id?: number | null; address_billing?: any; address_shipping?: any; sub_type?: string; payment_arrival_expected?: any; request_reference_split_the_bill?: any[]; balance_after_mutation?: { value: string; currency: string; }; payment_auto_allocate_instance?: any; payment_suspended_outgoing?: any; } export interface Counterparty { display_name?: string; public_nick_name?: string; iban?: string | null; type?: string; label_user?: LabelUser; } export interface UserProfile { id: number; displayName: string; firstName?: string; lastName?: string; email?: string; } export interface RequestInquiry { id: number; created: string; updated: string; time_responded: string; time_expiry: string; monetary_account_id: number; amount_inquired: { value: string; currency: string }; amount_responded: { value: string; currency: string }; user_alias_created: any; user_alias_revoked: any; counterparty_alias: any; description: string; merchant_reference: string; attachment: Array<{ id: number }>; status: string; batch_id: number; scheduled_id: number; minimum_age: number; require_address: string; bunqme_share_url: string; redirect_url: string; address_shipping: any; address_billing: any; geolocation: any; reference_split_the_bill: any; } export class UserAPI { #context: BunqContext; constructor(context: BunqContext) { this.#context = context; } async getInsights({ userId, dateStart, dateEnd, }: { userId?: number; dateStart?: Date; dateEnd?: Date; }) { userId = userId ?? this.#context.token.userId; let endDate = dateEnd ? new Date(dateEnd) : new Date(); // Set time to 00:00:00 for consistency endDate.setHours(0, 0, 0, 0); let startDate = dateStart ? new Date(dateStart) : new Date(endDate); if (!dateStart) { startDate.setDate(endDate.getDate() - 6); // 6 days before end, inclusive } const start = formatDateYYYYMMDD(startDate); const end = formatDateYYYYMMDD(endDate); const url = `/user/${userId}/insights?time_start=${start}&time_end=${end}`; const data = await this.#context.makeSignedRequest<unknown>(url); return data; } async getEvents(userId: number = this.#context.token.userId) { const data = await this.#context.makeSignedRequest<unknown>(`/user/${userId}/event`); return data; } async getMonetaryBankAccounts(userId: number = this.#context.token.userId) { type AccountResponse = { Response: Array<{ MonetaryAccountBank: { id: number; description: string; balance: { value: string; currency: string; }; }; }>; }; const data = await this.#context.makeSignedRequest<AccountResponse>( `/user/${userId}/monetary-account-bank`, ); return data.Response.map((item) => ({ id: item.MonetaryAccountBank.id, description: item.MonetaryAccountBank.description, balance: item.MonetaryAccountBank.balance.value, currency: item.MonetaryAccountBank.balance.currency, })); } async getUserSelf() { type UserSelfResponse = { Response: Array<{ UserApiKey: { id: number; created: string; updated: string; requested_by_user: { UserPerson: { id: number; display_name: string; public_nick_name: string; avatar: { uuid: string; image: Array<{ attachment_public_uuid: string; height: number; width: number; content_type: string; urls: Array<{ type: string; url: string; }>; }>; anchor_uuid: string; style: string; }; session_timeout: number; }; }; granted_by_user: { UserPerson: { id: number; display_name: string; public_nick_name: string; avatar: { uuid: string; image: Array<{ attachment_public_uuid: string; height: number; width: number; content_type: string; urls: Array<{ type: string; url: string; }>; }>; anchor_uuid: string; style: string; }; session_timeout: number; }; }; }; }>; Pagination: { future_url: string; newer_url: string | null; older_url: string | null; }; }; const data = await this.#context.makeSignedRequest<UserSelfResponse>(`/user`); return data; } /** * Get payments for a specific monetary account */ async getPayments({ userId = this.#context.token.userId, monetaryAccountId, }: { userId?: number; monetaryAccountId: number; }) { type PaymentResponse = { Response: Array<{ Payment: Payment; }>; }; const url = `/user/${userId}/monetary-account/${monetaryAccountId}/payment?count=200`; const data = await this.#context.makeSignedRequest<PaymentResponse>(url); return data.Response.map((item) => item.Payment); } /** * Get request inquiries for a specific monetary account */ async getRequestInquiries({ userId = this.#context.token.userId, monetaryAccountId, }: { userId?: number; monetaryAccountId: number; }) { type RequestInquiryResponse = { Response: Array<{ RequestInquiry: RequestInquiry; }>; }; const url = `/user/${userId}/monetary-account/${monetaryAccountId}/request-inquiry`; const data = await this.#context.makeSignedRequest<RequestInquiryResponse>(url); return data.Response.map((item) => item.RequestInquiry); } /** * Get payment auto-allocate objects for a specific monetary account (focused fields only) */ async getPaymentAutoAllocates({ userId = this.#context.token.userId, monetaryAccountId, }: { userId?: number; monetaryAccountId: number; }) { type PaymentAutoAllocateResponse = { Response: Array<{ PaymentAutoAllocate: { id: number; created: string; updated: string; type: string; status: string; trigger_amount: { value: string; currency: string }; payment: any; }; }>; }; const url = `/user/${userId}/monetary-account/${monetaryAccountId}/payment-auto-allocate`; const data = await this.#context.makeSignedRequest<PaymentAutoAllocateResponse>(url); // Only return the focused fields return data.Response.map((item) => ({ id: item.PaymentAutoAllocate.id, created: item.PaymentAutoAllocate.created, updated: item.PaymentAutoAllocate.updated, type: item.PaymentAutoAllocate.type, status: item.PaymentAutoAllocate.status, trigger_amount: item.PaymentAutoAllocate.trigger_amount, payment: item.PaymentAutoAllocate.payment, })); } /** * Create a request inquiry for a specific monetary account * POST /v1/user/{userID}/monetary-account/{monetary-accountID}/request-inquiry * @param userId - User ID (optional, defaults to context) * @param monetaryAccountId - Monetary Account ID * @param body - Request body for the inquiry * @returns The created request inquiry ID */ async createRequestInquiry({ userId = this.#context.token.userId, monetaryAccountId, body, }: { userId?: number; monetaryAccountId: number; body: { amount_inquired: { value: string; currency: string }; counterparty_alias: | { type: "EMAIL"; value: string; name?: string } | { type: "IBAN"; value: string; name?: string } | { type: "PHONE_NUMBER"; value: string; name?: string }; description: string; }; }): Promise<{ id: number }> { const url = `/user/${userId}/monetary-account/${monetaryAccountId}/request-inquiry`; // The Bunq API expects the body to be wrapped in an array with the key 'RequestInquiry' const requestBody = { ...body, counterparty_alias: { ...body.counterparty_alias, }, allow_bunqme: false, }; console.log("POSTING", url, requestBody); const data = await this.#context.makeSignedRequest<{ Response: [{ Id: { id: number } }] }>( url, "POST", requestBody, ); console.log("RESPONSE", JSON.stringify(data, null, 2)); // The response is { Id: { id: number } } return { id: data.Response[0].Id.id }; } async getRequestInquiry({ userId = this.#context.token.userId, monetaryAccountId, itemId, }: { userId?: number; monetaryAccountId: number; itemId: number; }): Promise<RequestInquiry> { const url = `/user/${userId}/monetary-account/${monetaryAccountId}/request-inquiry/${itemId}`; const data = await this.#context.makeSignedRequest<{ Response: [{ RequestInquiry: RequestInquiry }]; }>(url, "GET"); return data.Response[0].RequestInquiry; } async listRequestInquiryResponse({ userId = this.#context.token.userId, monetaryAccountId, }: { userId?: number; monetaryAccountId: number; }): Promise<any[]> { const url = `/user/${userId}/monetary-account/${monetaryAccountId}/request-response`; const data = await this.#context.makeSignedRequest<{ Response: any[]; }>(url, "GET"); return data.Response; } async updateRequestInquiry({ userId = this.#context.token.userId, monetaryAccountId, itemId, }: { userId?: number; monetaryAccountId: number; itemId: number; }): Promise<RequestInquiry> { const url = `/user/${userId}/monetary-account/${monetaryAccountId}/request-inquiry/${itemId}`; const requestBody = { status: "ACCEPTED", }; const data = await this.#context.makeSignedRequest<{ Response: [{ RequestInquiry: RequestInquiry }]; }>(url, "PUT", requestBody); console.log("RESPONSE", JSON.stringify(data, null, 2)); return data.Response[0].RequestInquiry; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/WilcoKruijer/bunq-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server