Skip to content

Add customer.* webhook events and customer_id list filters (wave 1) - #61

Merged
ericviana merged 2 commits into
mainfrom
eric/customers-webhooks-and-shims
Jul 27, 2026
Merged

Add customer.* webhook events and customer_id list filters (wave 1)#61
ericviana merged 2 commits into
mainfrom
eric/customers-webhooks-and-shims

Conversation

@ericviana

@ericviana ericviana commented Jul 27, 2026

Copy link
Copy Markdown
Member

What

This is wave 1 of the receivers -> customers migration for blindpay-node: only changes the currently deployed API already accepts. Nothing here requires blindpay-v2 PR #1799 (still open, not deployed) to be merged first.

  • WebhookEvents: add customer.new, customer.update, customer.delete as new union members. Mark receiver.new, receiver.update, receiver.delete @deprecated in place with a trailing comment (a plain string-literal union can't carry per-member JSDoc). The receiver.* members are NOT removed, they still fire in production today.
  • ListPayinsInput / ListPayoutsInput: add an optional customer_id filter alongside the existing receiver_id. customer_name is intentionally NOT added to these two, the deployed API does not accept it there.
  • customers resource list(): delete the customer_id/customer_name -> receiver_id/receiver_name query-param translation shim. The deployed GET /v1/instances/{instance_id}/customers endpoint already accepts customer_id and customer_name natively, so the translation is now a no-op that was only adding indirection.
  • CLAUDE.md: fix three stale doc examples (UpdateReceiverInput/DeleteReceiverInput naming table, an offramp-wallet example still showing /receivers/{receiver_id} paths, a create-customer example still POSTing to /receivers) that no longer match what the code actually does.

Also fixes: pre-existing wrong response field names

Independent of the receivers -> customers migration, a previous change had optimistically renamed some response field declarations to customer_id ahead of the server. The deployed API still sends receiver_id in these places today, so the SDK was declaring a key that never arrives on the wire (Python/Swift hard-fail on this; TypeScript silently resolves to undefined). Confirmed each one directly against the deployed openapi.json before fixing:

  • ListBlockchainWalletsResponse / GetBlockchainWalletResponse / CreateBlockchainWalletResponse: schema BlockchainWalletOut has receiver_id in required, no customer_id.
  • OfframpWallet (used by list/get offramp wallet responses): schema OfframpWallet has receiver_id in required, no customer_id.
  • GetLimitIncreaseRequestsResponse: schema GetReceiverLimitIncreaseOut has receiver_id, no customer_id.
  • The nested Owner type on customers.get() / customers.list() for business customers: schema ReceiverOut.owners[] nests receiver_id, no customer_id.

These are safe to land in wave 1 (unlike the wave 2 renames below) because they align the SDK with what production sends today, not with the not-yet-deployed rename. Request-side customer_id path/body params are untouched, they're correct since routes are already /customers/.... receiver_amount, currency_type, and the bank-accounts owner-id field are untouched too, the latter is a separate pre-existing type-accuracy issue (deployed BankAccountOut has neither receiver_id nor customer_id) left out of scope here.

Test fixtures updated to match, and the offramp wallet fixtures now carry explicit type annotations so a regression of this exact bug fails check-types.

Why this is safe today

I diffed the deployed API's openapi.json against this PR's changes before making them:

  • customer.new/customer.update/customer.delete are already in the deployed WebhookEndpointIn.events enum and the API has been dual-emitting both receiver.* and customer.* events since June.
  • GET /v1/instances/{instance_id}/payins and .../payouts already list customer_id as an accepted query parameter (verified per-endpoint; customer_name is only accepted on .../customers, not on payins/payouts).
  • GET /v1/instances/{instance_id}/customers already lists both customer_id and customer_name as accepted query parameters, alongside the legacy receiver_id/receiver_name.

Purely additive, purely deprecation-marking, plus the response-field corrections above. No existing consumer's code path changes shape.

What is deliberately NOT in this PR (wave 2)

The remaining 11 receiver_* -> customer_* body/response field renames (customer_local_amount, customer_wallet_address, customer_network, customer_token, customer_invite_redirect_url, receiver_id on payin/payout/transfer responses, etc.), the RECEIVERS_* -> CUSTOMERS_* error-code/message renames, and removal of receiver.* from the webhook enum, are all held back on purpose. The deployed API does not accept the renamed field names yet, they only land once blindpay-v2 PR #1799 ships. Shipping those today would break every current SDK user sending receiver_* fields.

Build output

$ bun install --frozen-lockfile
Checked 247 installs across 293 packages (no changes)

$ bun run check-types
tsc --noEmit
(no errors)

$ bun run lint:check
Checked 50 files in 31ms. No fixes applied.

$ bun run test
 Test Files  20 passed (20)
      Tests  97 passed (97)

Version

Bumped package.json to 4.1.0 (minor, additive) and added a changeset (.changeset/customer-webhooks-and-list-filters.md), matching this repo's existing convention (e.g. #54 bumped package.json directly alongside its changeset in the same PR). Not bumped again for the field-name fixes added on top, they're covered by the same wave-1 minor bump.

https://claude.ai/code/session_01F1stiNzuNtJXoXtiW9ZCbs

…e 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
@BernardoSM

BernardoSM commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues
Licenses 0 0 0 0 0 issues
Code Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

…ds 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
@ericviana

Copy link
Copy Markdown
Member Author

Added a second commit on top of the wave 1 work: fixed four response field declarations that had been renamed to customer_id optimistically, ahead of the server-side rename (blindpay-v2 PR #1799, still open and not deployed). The deployed API sends receiver_id in these places today, so the SDK was declaring a key that never arrives on the wire.

Confirmed each one directly against the deployed openapi.json before touching anything:

  • ListBlockchainWalletsResponse / GetBlockchainWalletResponse / CreateBlockchainWalletResponse -> schema BlockchainWalletOut has receiver_id in required, no customer_id.
  • OfframpWallet (list/get offramp wallet responses) -> schema OfframpWallet has receiver_id in required, no customer_id.
  • GetLimitIncreaseRequestsResponse -> schema GetReceiverLimitIncreaseOut has receiver_id, no customer_id.
  • Nested Owner type on customers.get()/customers.list() for business customers -> schema ReceiverOut.owners[] nests receiver_id, no customer_id.

These are wave-1 safe: they align the SDK with what production sends today, they don't depend on PR #1799 shipping. Request-side customer_id path/body params are untouched (correct, matches the already-deployed /customers/... routes). receiver_amount and currency_type are untouched everywhere. I also checked the bank-accounts owner-id field, the deployed BankAccountOut schema has neither receiver_id nor customer_id, so that's a separate pre-existing type-accuracy issue, left out of scope here.

Updated the test fixtures for all four, and added explicit type annotations to the offramp wallet fixtures so a regression of this exact bug would fail check-types (verified by reverting the fix locally and re-running bun run check-types, it correctly failed).

bun install --frozen-lockfile && bun run check-types && bun run lint:check && bun run test all pass (97 tests). No version bump beyond the existing wave-1 4.1.0.

@ericviana
ericviana merged commit 535cec6 into main Jul 27, 2026
9 checks passed
@ericviana
ericviana deleted the eric/customers-webhooks-and-shims branch July 27, 2026 16:04
ericviana added a commit that referenced this pull request Jul 27, 2026
…ase responses (#62)

The wave 1 PR (#61) also merged a second commit that reverted BlockchainWalletOut,
OfframpWallet and GetLimitIncreaseRequestsResponse to receiver_id, reasoning from the
declared zod schema in openapi.json alone. That schema is not what leaves the socket:
addCustomerIdMiddleware (apps/api/src/shared/helpers/customer-migration.ts) rewrites
every customer-route response, adding customer_id wherever receiver_id is present, at
both the top level and inside a response's data array. It does not recurse into nested
object/array properties such as owners[].

So customer_id is already on the wire today for these three response shapes, and it
is also the field name the post-#1799 spec declares once the server-side rename ships.
Restore customer_id as required for the three affected shapes.

The nested Owner.owners[] element is the one place the middleware does not reach today:
declare both receiver_id and customer_id there as optional, since receiver_id is what
production actually sends there now and customer_id is what it will send after #1799.

Bump to 4.1.1 (patch: corrects a type declaration, no feature).

Claude-Session: https://claude.ai/code/session_01F1stiNzuNtJXoXtiW9ZCbs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants