From d91c3a08b20ba19d679e7b7506a58b645e88f0a7 Mon Sep 17 00:00:00 2001 From: Eric Viana Date: Mon, 27 Jul 2026 12:11:32 -0300 Subject: [PATCH 1/2] feat: add customer.* webhook events and customer_id list filters (wave 1) The deployed API has dual-emitted customer.new/update/delete alongside the legacy receiver.* webhook events since June, and already accepts customer_id as a query filter on GET payins/payouts and customer_id + customer_name on GET customers. The SDK never picked these up because api-sync only diffs receiver_* field renames, not new enum values. - WebhookEvents: add customer.new/update/delete, mark receiver.new/ update/delete @deprecated in place (still fires, not removed). - ListPayinsInput / ListPayoutsInput: add optional customer_id filter alongside the existing receiver_id. - customers resource list(): drop the customer_id/customer_name -> receiver_id/receiver_name translation shim now that the API accepts the customer_* names natively. - CLAUDE.md: fix stale UpdateReceiverInput/DeleteReceiverInput naming example and /receivers path examples that no longer match the code. Bump to 4.1.0 (minor, additive only). The remaining receiver_* -> customer_* field/error-code renames are deliberately not included here; the deployed API does not accept them until blindpay-v2 PR #1799 ships. Claude-Session: https://claude.ai/code/session_01F1stiNzuNtJXoXtiW9ZCbs --- .changeset/customer-webhooks-and-list-filters.md | 7 +++++++ CLAUDE.md | 12 ++++++------ package.json | 2 +- src/resources/customers/index.ts | 14 +------------- src/resources/payins/index.ts | 1 + src/resources/payouts/index.ts | 1 + src/resources/webhooks/index.ts | 9 ++++++--- 7 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 .changeset/customer-webhooks-and-list-filters.md diff --git a/.changeset/customer-webhooks-and-list-filters.md b/.changeset/customer-webhooks-and-list-filters.md new file mode 100644 index 0000000..358b5f9 --- /dev/null +++ b/.changeset/customer-webhooks-and-list-filters.md @@ -0,0 +1,7 @@ +--- +"@blindpay/node": minor +--- + +Add `customer.new`, `customer.update` and `customer.delete` to `WebhookEvents` (the deployed API has been dual-emitting these alongside the legacy `receiver.*` events since June). Mark the `receiver.*` webhook events `@deprecated` in place; they still fire and are not removed. + +Add `customer_id` as an accepted filter on `ListPayinsInput` and `ListPayoutsInput`, matching what the deployed API already accepts. Drop the internal `customer_id`/`customer_name` -> `receiver_id`/`receiver_name` query translation in the `customers` resource's `list()` now that the deployed API accepts the `customer_*` filter names natively. diff --git a/CLAUDE.md b/CLAUDE.md index 9cd8da5..2130a20 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -71,8 +71,8 @@ All types are defined in the same file as the factory function, above it. Naming | Create response | `CreatePartnerFeeResponse` | | Get input | `GetPayinInput` | | Get response | `GetPayinResponse` | -| Update input | `UpdateReceiverInput` | -| Delete input | `DeleteReceiverInput` | +| Update input | `UpdateCustomerInput` | +| Delete input | `DeleteCustomerInput` | | String-only IDs | `export type GetPayinInput = string;` | | Object inputs | `export type CreatePayoutInput = { ... };` | @@ -724,18 +724,18 @@ update({ widget_id, ...data }: UpdateWidgetInput): Promise> { return client.get( - `/instances/${instanceId}/receivers/${receiver_id}/bank-accounts/${bank_account_id}/offramp-wallets/${id}` + `/instances/${instanceId}/customers/${customer_id}/bank-accounts/${bank_account_id}/offramp-wallets/${id}` ); }, ``` @@ -758,7 +758,7 @@ When one endpoint creates different variants based on a `type` field, create sep createIndividualWithStandardKYC( data: CreateIndividualWithStandardKYCInput ): Promise> { - return client.post(`/instances/${instanceId}/receivers`, { + return client.post(`/instances/${instanceId}/customers`, { kyc_type: "standard", type: "individual", ...data, diff --git a/package.json b/package.json index 5762117..ba94cb0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@blindpay/node", - "version": "4.0.2", + "version": "4.1.0", "description": "Official Node.js SDK for Blindpay API - Stablecoin API for global payments", "keywords": [ "blindpay", diff --git a/src/resources/customers/index.ts b/src/resources/customers/index.ts index fbc1c83..c5b5fa6 100644 --- a/src/resources/customers/index.ts +++ b/src/resources/customers/index.ts @@ -515,19 +515,7 @@ export type RequestLimitIncreaseResponse = { export function createCustomersResource(instanceId: string, client: InternalApiClient) { return { list(params?: ListCustomersInput): Promise> { - // The API's filter schema still uses receiver_id/receiver_name. Translate - // customer_* inputs to the wire-level names so consumers see a clean - // customer-only surface. Drop this mapping once the API accepts customer_*. - const wireParams = params - ? Object.fromEntries( - Object.entries(params).map(([k, v]) => { - if (k === "customer_id") return ["receiver_id", v]; - if (k === "customer_name") return ["receiver_name", v]; - return [k, v]; - }) - ) - : undefined; - const queryParams = wireParams ? `?${new URLSearchParams(wireParams)}` : ""; + const queryParams = params ? `?${new URLSearchParams(params)}` : ""; return client.get(`/instances/${instanceId}/customers${queryParams}`); }, createIndividualWithStandardKYC( diff --git a/src/resources/payins/index.ts b/src/resources/payins/index.ts index eee57f0..a3c529c 100644 --- a/src/resources/payins/index.ts +++ b/src/resources/payins/index.ts @@ -99,6 +99,7 @@ export type Payin = { export type ListPayinsInput = PaginationParams & { status?: TransactionStatus; receiver_id?: string; + customer_id?: string; }; export type ListPayinsResponse = diff --git a/src/resources/payouts/index.ts b/src/resources/payouts/index.ts index 1ca03b3..eac38cd 100644 --- a/src/resources/payouts/index.ts +++ b/src/resources/payouts/index.ts @@ -96,6 +96,7 @@ export type Payout = { export type ListPayoutsInput = PaginationParams & { receiver_id?: string; + customer_id?: string; }; export type ListPayoutsResponse = { diff --git a/src/resources/webhooks/index.ts b/src/resources/webhooks/index.ts index 1a8bdd0..c233966 100644 --- a/src/resources/webhooks/index.ts +++ b/src/resources/webhooks/index.ts @@ -2,8 +2,8 @@ import type { BlindpayApiResponse } from "../../../types"; import type { InternalApiClient } from "../../internal/api-client"; export type WebhookEvents = - | "receiver.new" - | "receiver.update" + | "receiver.new" // @deprecated use "customer.new" instead + | "receiver.update" // @deprecated use "customer.update" instead | "bankAccount.new" | "payout.new" | "payout.update" @@ -24,7 +24,10 @@ export type WebhookEvents = | "transfer.complete" | "wallet.new" | "wallet.inbound" - | "receiver.delete"; + | "receiver.delete" // @deprecated use "customer.delete" instead + | "customer.new" + | "customer.update" + | "customer.delete"; export type CreateWebhookEndpointInput = { url: string; From fd444d2115d275a4e10a27b281fdd0d16b1d3661 Mon Sep 17 00:00:00 2001 From: Eric Viana Date: Mon, 27 Jul 2026 12:47:50 -0300 Subject: [PATCH 2/2] fix: align blockchain/offramp wallet and limit-increase response fields with production The deployed API sends receiver_id in these response bodies, not customer_id. The customer_id name was applied optimistically ahead of the server-side rename (blindpay-v2 PR #1799, not yet deployed), so these fields never actually arrived on the wire. In Python/Swift this is a hard failure at deserialization; in TypeScript it silently resolved to undefined. Fixed schemas, confirmed 1:1 against the deployed openapi.json: - BlockchainWalletOut (list/get/create blockchain wallet responses) - OfframpWallet (list/get offramp wallet responses) - GetReceiverLimitIncreaseOut (get limit increase requests response) - ReceiverOut.owners[] (nested owner on customers.get()/list() for business customers) Left untouched: request-side customer_id path/body params (correct, matches the new /customers/... routes), receiver_amount, currency_type, and the bank-accounts owner-id field (pre-existing separate type-accuracy issue, out of scope here since the deployed BankAccountOut schema has neither receiver_id nor customer_id). Updated test fixtures accordingly and added type annotations to the offramp fixtures so a regression of this field name would now fail type-check. Claude-Session: https://claude.ai/code/session_01F1stiNzuNtJXoXtiW9ZCbs --- src/resources/customers/customers.test.ts | 6 +++--- src/resources/customers/index.ts | 4 ++-- src/resources/wallets/blockchain.test.ts | 8 ++++---- src/resources/wallets/blockchain.ts | 6 +++--- src/resources/wallets/offramp.test.ts | 9 +++++---- src/resources/wallets/offramp.ts | 2 +- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/resources/customers/customers.test.ts b/src/resources/customers/customers.test.ts index 7dba1b8..9e34e47 100644 --- a/src/resources/customers/customers.test.ts +++ b/src/resources/customers/customers.test.ts @@ -577,7 +577,7 @@ describe("Customers", () => { const mockedLimitIncreaseRequests: GetLimitIncreaseRequestsResponse = [ { id: "rl_000000000000", - customer_id: "re_YuaMcI2B8zbQ", + receiver_id: "re_YuaMcI2B8zbQ", status: "in_review", daily: 50000, monthly: 250000, @@ -589,7 +589,7 @@ describe("Customers", () => { }, { id: "rl_000000000000", - customer_id: "re_YuaMcI2B8zbQ", + receiver_id: "re_YuaMcI2B8zbQ", status: "approved", daily: 30000, monthly: 150000, @@ -618,7 +618,7 @@ describe("Customers", () => { const mockedLimitIncreaseRequests: GetLimitIncreaseRequestsResponse = [ { id: "rl_000000000000", - customer_id: "re_YuaMcI2B8zbQ", + receiver_id: "re_YuaMcI2B8zbQ", status: "approved", daily: 50000, monthly: 250000, diff --git a/src/resources/customers/index.ts b/src/resources/customers/index.ts index c5b5fa6..d2102c2 100644 --- a/src/resources/customers/index.ts +++ b/src/resources/customers/index.ts @@ -73,7 +73,7 @@ export type Owner = { title: string | null; tax_type?: OwnerTaxType | null; instance_id?: string; - customer_id?: string; + receiver_id?: string; }; export type IndividualWithStandardKYC = { @@ -485,7 +485,7 @@ export type LimitIncreaseRequestSupportingDocumentType = export type GetLimitIncreaseRequestsResponse = Array<{ id: string; - customer_id: string; + receiver_id: string; status: LimitIncreaseRequestStatus; daily: number; monthly: number; diff --git a/src/resources/wallets/blockchain.test.ts b/src/resources/wallets/blockchain.test.ts index f9fa396..e0c277b 100644 --- a/src/resources/wallets/blockchain.test.ts +++ b/src/resources/wallets/blockchain.test.ts @@ -40,7 +40,7 @@ describe("Blockchain wallets", () => { address: "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", signature_tx_hash: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", is_account_abstraction: false, - customer_id: "re_000000000000", + receiver_id: "re_000000000000", }, ]; @@ -64,7 +64,7 @@ describe("Blockchain wallets", () => { address: "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", signature_tx_hash: undefined, is_account_abstraction: true, - customer_id: "re_000000000000", + receiver_id: "re_000000000000", }; fetchMock.mockResponseOnce(JSON.stringify(mockedWallet), { @@ -92,7 +92,7 @@ describe("Blockchain wallets", () => { address: "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", signature_tx_hash: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", is_account_abstraction: false, - customer_id: "re_000000000000", + receiver_id: "re_000000000000", }; fetchMock.mockResponseOnce(JSON.stringify(mockedWallet), { @@ -120,7 +120,7 @@ describe("Blockchain wallets", () => { address: "0xDD6a3aD0949396e57C7738ba8FC1A46A5a1C372C", signature_tx_hash: "0x3c499c542cef5e3811e1192ce70d8cc03d5c3359", is_account_abstraction: false, - customer_id: "re_000000000000", + receiver_id: "re_000000000000", }; fetchMock.mockResponseOnce(JSON.stringify(mockedWallet), { diff --git a/src/resources/wallets/blockchain.ts b/src/resources/wallets/blockchain.ts index 5b239b3..e3fa9f5 100644 --- a/src/resources/wallets/blockchain.ts +++ b/src/resources/wallets/blockchain.ts @@ -16,7 +16,7 @@ export type ListBlockchainWalletsResponse = Array<{ address?: string; signature_tx_hash?: string; is_account_abstraction: boolean; - customer_id: string; + receiver_id: string; }>; export type CreateBlockchainWalletWithAddressInput = { @@ -50,7 +50,7 @@ export type GetBlockchainWalletResponse = { address?: string; signature_tx_hash?: string; is_account_abstraction: boolean; - customer_id: string; + receiver_id: string; }; export type CreateBlockchainWalletResponse = { @@ -60,7 +60,7 @@ export type CreateBlockchainWalletResponse = { address?: string; signature_tx_hash?: string; is_account_abstraction: boolean; - customer_id: string; + receiver_id: string; }; export type CreateAssetTrustlineInput = string; diff --git a/src/resources/wallets/offramp.test.ts b/src/resources/wallets/offramp.test.ts index a0e48cd..b61a842 100644 --- a/src/resources/wallets/offramp.test.ts +++ b/src/resources/wallets/offramp.test.ts @@ -1,5 +1,6 @@ import { afterEach, describe, expect, it } from "vitest"; import { BlindPay } from "../../client"; +import type { OfframpWallet } from "./offramp"; describe("Offramp wallets", () => { afterEach(() => fetchMock.resetMocks()); @@ -8,12 +9,12 @@ describe("Offramp wallets", () => { describe("List offramp wallets", () => { it("should list offramp wallets", async () => { - const mockedOfframpWallets = [ + const mockedOfframpWallets: OfframpWallet[] = [ { id: "ow_000000000000", external_id: "your_external_id", instance_id: "in_000000000000", - customer_id: "re_000000000000", + receiver_id: "re_000000000000", bank_account_id: "ba_000000000000", network: "tron", address: "TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb", @@ -87,11 +88,11 @@ describe("Offramp wallets", () => { describe("Get offramp wallet", () => { it("should get an offramp wallet", async () => { - const mockedOfframpWallet = { + const mockedOfframpWallet: OfframpWallet = { id: "ow_000000000000", external_id: "your_external_id", instance_id: "in_000000000000", - customer_id: "re_000000000000", + receiver_id: "re_000000000000", bank_account_id: "ba_000000000000", network: "tron", address: "TALJN9zTTEL9TVBb4WuTt6wLvPqJZr3hvb", diff --git a/src/resources/wallets/offramp.ts b/src/resources/wallets/offramp.ts index 35ca46f..30b7384 100644 --- a/src/resources/wallets/offramp.ts +++ b/src/resources/wallets/offramp.ts @@ -18,7 +18,7 @@ export type OfframpWallet = { id: string; external_id: string; instance_id: string; - customer_id: string; + receiver_id: string; bank_account_id: string; network: OfframpWalletNetwork; address: string;