---
title: '"Receivers" to "Customers" migration: final notice and complete guide'
description: 'Final call: /receivers paths will start answering 301, receiver.* webhooks will stop firing, and receiver_* fields plus receiver error codes and messages are renamed. Full field-by-field guide and an AI migration prompt inside.'
date: 2026-07-27
category: Migration
categoryType: update
isChangelog: true
---

The transition period announced in the [June 4 changelog](/changelog/2026-06-04-customers-rename) has ended, and the final release that makes "customers" the only name across the BlindPay platform is about to roll out. This post is the complete guide to everything that changes.

**Timing: the renames below are not live yet.** They ship with the final release, which will be announced on this changelog. Until then, the `receiver_*` request fields remain the accepted shape on the current API (sending `customer_*` request fields today is rejected). Use this guide, and the AI prompt at the end, to prepare your migration diff now, and deploy it when the release goes live.

<!--more-->

## Endpoints

Every legacy `/receivers` path now returns `301 Moved Permanently` pointing at its `/customers` equivalent.

| Was | Now |
| --- | --- |
| `/v1/instances/{id}/receivers[/...]` | `/v1/instances/{id}/customers[/...]` |
| `/v1/e/instances/{id}/receivers[/...]` | `/v1/e/instances/{id}/customers[/...]` |
| `/v1/instances/{id}/limits/receivers/{id}` | `/v1/instances/{id}/limits/customers/{id}` |
| `/v1/instances/{id}/external-receiver-token` | `/v1/instances/{id}/external-customer-token` |

This covers the customer collection and item routes plus every sub-resource: bank accounts, wallets, blockchain wallets, virtual accounts, offramp wallets, limits, limit increases, and RFIs. The `/e/` external invite-flow variants redirect too.

Note that `external-receiver-token` is renamed even though it has no `/receivers/` path segment.

## Webhooks

`receiver.new`, `receiver.update`, and `receiver.delete` no longer fire. Subscribe to `customer.new`, `customer.update`, and `customer.delete` instead; they carry the same payloads. The dual-emit behavior from the transition window is gone, so you now receive exactly one event per action.

## Renamed fields

| Was | Now | Where |
| --- | --- | --- |
| `receiver_id` | `customer_id` | Payins, payouts, quotes, transfers, bank accounts, wallets, blockchain wallets, virtual accounts, offramp wallets, limit increases, RFIs, TOS. Also the path segment and list filter. |
| `receiver_name` | `customer_name` | Customer list filter query parameter |
| `receiver_local_amount` | `customer_local_amount` | Quotes, payouts |
| `receiver_wallet_address` | `customer_wallet_address` | Transfers, transfer quotes |
| `receiver_network` | `customer_network` | Transfers, transfer quotes |
| `receiver_token` | `customer_token` | Transfers, transfer quotes |
| `receiver_invite_redirect_url` | `customer_invite_redirect_url` | Instance settings |
| `receiver_rfi_emails_enabled` | `customer_rfi_emails_enabled` | Instance settings |
| `receivers_amount` | `customers_amount` | `GET /v1/instances`, the count of customers on the instance |
| `receiver_type` | `customer_type` | RFI |
| `receiver_kyc_status` | `customer_kyc_status` | RFI |
| `receiver_aiprise_session_id` | `customer_aiprise_session_id` | RFI |
| `receiver_aiprise_user_profile_id` | `customer_aiprise_user_profile_id` | RFI |

### The one field that keeps its name

`receiver_amount` is **not** renamed. It stays `receiver_amount` on quotes, payins, payouts, and transfers, because there it means "the amount the receiving side gets", a leg of the transaction rather than a reference to the customer resource.

Do not confuse it with `receivers_amount` (plural), which **is** renamed to `customers_amount`. That one is the customer count on an instance.

This is the single reason a blind find-and-replace of `receiver` to `customer` will break your integration.

## Renamed error codes

The `code` field on error responses is a stable identifier meant for programmatic handling, and eleven of them changed prefix.

| Was | Now |
| --- | --- |
| `RECEIVERS_NOT_FOUND` | `CUSTOMERS_NOT_FOUND` |
| `RECEIVERS_ALREADY_APPROVED` | `CUSTOMERS_ALREADY_APPROVED` |
| `RECEIVERS_KYC_NOT_APPROVED` | `CUSTOMERS_KYC_NOT_APPROVED` |
| `RECEIVERS_ONBOARDING_INCOMPLETE` | `CUSTOMERS_ONBOARDING_INCOMPLETE` |
| `RECEIVERS_INVALID_DATA` | `CUSTOMERS_INVALID_DATA` |
| `RECEIVERS_INVALID_PHONE` | `CUSTOMERS_INVALID_PHONE` |
| `RECEIVERS_INVALID_TAX_ID` | `CUSTOMERS_INVALID_TAX_ID` |
| `RECEIVERS_ID_DOCUMENT_INVALID` | `CUSTOMERS_ID_DOCUMENT_INVALID` |
| `RECEIVERS_COUNTRY_NOT_SUPPORTED` | `CUSTOMERS_COUNTRY_NOT_SUPPORTED` |
| `RECEIVERS_ENHANCED_KYC_REQUIRED` | `CUSTOMERS_ENHANCED_KYC_REQUIRED` |
| `RECEIVERS_BUSINESS_ENHANCED_NOT_AVAILABLE` | `CUSTOMERS_BUSINESS_ENHANCED_NOT_AVAILABLE` |

## Renamed error messages

The legacy `message` slug also changed on the errors below. If you branch on `message` rather than `code`, update both.

| Was | Now |
| --- | --- |
| `receiver_not_found` | `customer_not_found` |
| `receiver_id_not_found` | `customer_id_not_found` |
| `receiver_already_approved` | `customer_already_approved` |
| `receiver_kyc_not_approved` | `customer_kyc_not_approved` |
| `receiver_not_approved` | `customer_not_approved` |
| `receiver_limit_not_found` | `customer_limit_not_found` |
| `receiver_already_has_limit_increase_in_review` | `customer_already_has_limit_increase_in_review` |
| `vm_invalid_receiver_data` | `vm_invalid_customer_data` |
| `vm_receiver_country_not_supported` | `vm_customer_country_not_supported` |
| `vm_unsupported_receiver_type` | `vm_unsupported_customer_type` |
| `zenus_business_va_requires_business_receiver` | `zenus_business_va_requires_business_customer` |
| `beneficiary_name_must_match_receiver_for_first_party` | `beneficiary_name_must_match_customer_for_first_party` |
| `blockchain_wallet_does_not_belong_to_receiver` | `blockchain_wallet_does_not_belong_to_customer` |
| `sender_and_receiver_networks_must_be_the_same` | `sender_and_customer_networks_must_be_the_same` |
| `sender_and_receiver_tokens_must_be_the_same` | `sender_and_customer_tokens_must_be_the_same` |

Two `message` slugs are deliberately unchanged, because they describe the receiving leg of a transaction rather than the customer resource: `otc_only_supported_for_sender_without_cover_fees_and_receiver_with_cover_fees`, and every slug involving `receiver_amount`. The `currency_type` request enum also keeps its `sender` and `receiver` values.

## Deprecation headers are gone

`/receivers` responses no longer carry `Deprecation: true`, `Sunset`, or `Link: rel="successor-version"`. If you set up the CI smoke test suggested in the June changelog, it now passes trivially instead of catching anything. Replace it with a check that asserts no `301` responses from BlindPay.

## IDs are unchanged

Customer IDs keep the `re_` prefix, and customer records keep both `id` and its canonical alias `customer_id`. No data migration is needed. Do not rewrite stored IDs.

## Migrate your code with an AI agent

If you use an AI coding agent (Claude Code, Codex, Cursor, Windsurf, or similar), paste the prompt below into it from the root of your integration. It contains every rename from this changelog and the June announcement, including the exceptions that make a naive find-and-replace unsafe.

```text
Migrate this codebase off the deprecated BlindPay "receivers" API surface and onto
"customers". This is a rename of the API surface only: resource IDs are unchanged.

Work through the steps in order and show me a diff before applying anything.

STEP 1 - Find every call site.
Search the whole repo (including tests, fixtures, recorded HTTP cassettes, env
files, OpenAPI/Postman collections, infrastructure config, and docs) for:
  receivers, receiver_, receiverId, RECEIVERS_, "receiver." (webhook event names)

STEP 2 - Rewrite URL paths.
  /v1/instances/{id}/receivers               -> /v1/instances/{id}/customers
  /v1/e/instances/{id}/receivers             -> /v1/e/instances/{id}/customers
  /v1/instances/{id}/limits/receivers/{rid}  -> /v1/instances/{id}/limits/customers/{rid}
  /v1/instances/{id}/external-receiver-token -> /v1/instances/{id}/external-customer-token
Apply to all sub-resources as well: bank-accounts, wallets, blockchain-wallets,
virtual-accounts, offramp-wallets, limit-increase, rfi.
The old paths still answer 301, so nothing breaks immediately, but every call
costs an extra round-trip until this is done.

STEP 3 - Rename request and response fields.
  receiver_id                      -> customer_id
  receiver_name                    -> customer_name
  receiver_local_amount            -> customer_local_amount
  receiver_wallet_address          -> customer_wallet_address
  receiver_network                 -> customer_network
  receiver_token                   -> customer_token
  receiver_invite_redirect_url     -> customer_invite_redirect_url
  receiver_rfi_emails_enabled      -> customer_rfi_emails_enabled
  receivers_amount                 -> customers_amount
  receiver_type                    -> customer_type
  receiver_kyc_status              -> customer_kyc_status
  receiver_aiprise_session_id      -> customer_aiprise_session_id
  receiver_aiprise_user_profile_id -> customer_aiprise_user_profile_id

STEP 4 - CRITICAL EXCEPTION. Do NOT rename receiver_amount.
receiver_amount (singular) is still called receiver_amount on quotes, payins,
payouts, and transfers. It means "amount the receiving side gets", not a
reference to the customer resource. Renaming it will break those calls.
Note the near-miss: receivers_amount (plural, the customer count on
GET /v1/instances) IS renamed to customers_amount. Handle each explicitly.
Never run a blanket s/receiver/customer/ over this repo.

STEP 5 - Rename webhook event names and handlers.
  receiver.new    -> customer.new
  receiver.update -> customer.update
  receiver.delete -> customer.delete
Payloads are identical. Also update the subscription list in the BlindPay
dashboard; a code-only change will leave your endpoint silent.
The old dual-emit is gone, so if a handler was de-duplicating receiver.* against
customer.* for the same action, that workaround can be removed.

STEP 6 - Rename error codes checked in code.
Any comparison against the code field on an error response:
  RECEIVERS_NOT_FOUND -> CUSTOMERS_NOT_FOUND, and the same CUSTOMERS_ prefix
  swap for ALREADY_APPROVED, KYC_NOT_APPROVED, ONBOARDING_INCOMPLETE,
  INVALID_DATA, INVALID_PHONE, INVALID_TAX_ID, ID_DOCUMENT_INVALID,
  COUNTRY_NOT_SUPPORTED, ENHANCED_KYC_REQUIRED,
  BUSINESS_ENHANCED_NOT_AVAILABLE.

STEP 6b - Rename error message slugs checked in code.
If you branch on the message field instead of code, these changed too:
  receiver_not_found            -> customer_not_found
  receiver_id_not_found         -> customer_id_not_found
  receiver_already_approved     -> customer_already_approved
  receiver_kyc_not_approved     -> customer_kyc_not_approved
  receiver_not_approved         -> customer_not_approved
  receiver_limit_not_found      -> customer_limit_not_found
  receiver_already_has_limit_increase_in_review
    -> customer_already_has_limit_increase_in_review
  vm_invalid_receiver_data      -> vm_invalid_customer_data
  vm_receiver_country_not_supported -> vm_customer_country_not_supported
  vm_unsupported_receiver_type  -> vm_unsupported_customer_type
  zenus_business_va_requires_business_receiver
    -> zenus_business_va_requires_business_customer
  beneficiary_name_must_match_receiver_for_first_party
    -> beneficiary_name_must_match_customer_for_first_party
  blockchain_wallet_does_not_belong_to_receiver
    -> blockchain_wallet_does_not_belong_to_customer
  sender_and_receiver_networks_must_be_the_same
    -> sender_and_customer_networks_must_be_the_same
  sender_and_receiver_tokens_must_be_the_same
    -> sender_and_customer_tokens_must_be_the_same
Leave unchanged: any slug mentioning receiver_amount, and
otc_only_supported_for_sender_without_cover_fees_and_receiver_with_cover_fees.

STEP 6c - Do NOT change the currency_type request value.
currency_type still accepts 'sender' and 'receiver'. Sending 'customer' is
invalid. This pairs with the receiver_amount exception in step 4.

STEP 7 - Update the SDK accessor, if you use an official SDK.
  blindpay.receivers.*  ->  blindpay.customers.*
  blindpay.receivers.bankAccounts.create(...)
    ->  blindpay.customers.bankAccounts.create(...)
Method signatures are identical; only the accessor renames. Bump to the latest
release of your current major, which added customers with no import changes.

STEP 8 - Do NOT touch stored IDs.
Customer IDs keep the re_ prefix (re_abc123456789). Leave every stored ID,
fixture, and database value exactly as it is. There is no data migration.

STEP 9 - Verify.
  - grep the repo: no receivers, receiver_id, or receiver.* webhook names left
  - confirm receiver_amount is still present and untouched wherever it was before
  - run the test suite, and update fixtures or cassettes that assert on renamed
    fields
  - check logs for 301 responses from api.blindpay.com; each one is a path you
    missed

Report anything ambiguous rather than guessing.
```

## What you need to do

1. Update API paths from `/receivers` to `/customers`, including the `/e/` invite-flow variants and `external-customer-token`.
2. Rename request and response fields per the table above, leaving `receiver_amount` alone.
3. Update error handling: `code` comparisons from `RECEIVERS_*` to `CUSTOMERS_*`, and `message` comparisons from `receiver_*` to `customer_*`.
4. Subscribe to `customer.*` webhooks in the dashboard and update your handlers.
5. Update your SDK to the latest release of its current major, then switch `receivers.*` calls to `customers.*`.
6. Leave stored IDs untouched. The `re_` prefix is permanent.

If you completed the migration before July 3, 2026, the only new work is step 3, the error `code` and `message` renames, which ship with this release.

## Verification checklist

- Logs show zero `301` responses from `api.blindpay.com`, since each one is an unmigrated path
- Logs show zero requests to `/v1/instances/*/receivers*` and `/v1/e/instances/*/receivers*`
- No code path reads `response.receiver_id` or any other renamed field
- `receiver_amount` is still read correctly on quotes, payins, payouts, and transfers
- No code path compares an error `code` against a `RECEIVERS_*` value, or an error `message` against a `receiver_*` slug
- Requests still send `currency_type: 'receiver'`, not `'customer'`
- Webhook handlers process `customer.new`, `customer.update`, and `customer.delete`, and the dashboard subscription lists them
- SDK updated, with no remaining `receivers.*` accessor calls
- Saved dashboard URLs use `/customers/`

## FAQ

### What breaks if I do nothing?

Requests keep working through the `301` redirects, at the cost of an extra round-trip per call, as long as your HTTP client follows redirects (browsers, `curl`, `requests`, `axios`, the official SDKs, and Postman all do by default). Three things break outright: `receiver.*` webhook subscriptions go silent, code that reads a renamed `receiver_*` response field gets `undefined`, and error handling that branches on a `RECEIVERS_*` `code` or a `receiver_*` `message` stops matching.

### Do I need to migrate my stored IDs?

No. All IDs keep the `re_` prefix, and both `id` and `customer_id` carry the same value. We renamed the API surface, not the underlying resource.

### Why does `receiver_amount` keep its name?

Because it does not refer to the customer resource. On a quote or a payment it means the amount landing on the receiving side, the counterpart to `sender_amount`. Renaming it to `customer_amount` would have made it read like a property of the customer record.

### Will a find-and-replace work?

Not safely. `receiver_amount` must survive untouched while `receivers_amount` must change, and stored `re_` IDs must not be rewritten. Use the agent prompt above, which encodes those exceptions, or do the renames field by field.

### How do I report issues with the migration?

Use the feedback button in the dashboard (under your profile dropdown) or email support@blindpay.com with `migration` in the subject. Including the request ID from the response (`x-blindpay-request-id` header) speeds up the lookup.
