---
openapi: 3.0.3
info:
  title: Braze REST API – Rename external ID
  description: |
    Renames users' primary external IDs and deprecates prior IDs.
    Up to 50 rename objects are allowed per request.
  version: 1.0.0
  contact:
    name: Braze Support
    url: https://www.braze.com/docs/braze_support/
  license:
    name: Braze Documentation
    url: https://www.braze.com/docs/api/home/
servers:
  - url: https://rest.iad-01.braze.com
    description: US-01
  - url: https://rest.iad-02.braze.com
    description: US-02
  - url: https://rest.iad-03.braze.com
    description: US-03
  - url: https://rest.iad-04.braze.com
    description: US-04
  - url: https://rest.iad-05.braze.com
    description: US-05
  - url: https://rest.iad-06.braze.com
    description: US-06
  - url: https://rest.iad-07.braze.com
    description: US-07
  - url: https://rest.iad-08.braze.com
    description: US-08
  - url: https://rest.us-10.braze.com
    description: US-10
  - url: https://rest.fra-01.braze.eu
    description: EU-01
  - url: https://rest.fra-02.braze.eu
    description: EU-02
  - url: https://rest.au-01.braze.com
    description: AU-01
  - url: https://rest.id-01.braze.com
    description: ID-01
  - url: https://rest.jp-01.braze.com
    description: JP-01
  - url: https://rest.kr-01.braze.com
    description: KR-01
security:
  - BearerAuth: []
paths:
  /users/external_ids/rename:
    post:
      summary: Rename external ID
      description: |
        Renames user external IDs. Requires `users.external_ids.rename` permission.

        ## Prerequisites

        To use this endpoint, you'll need an [API key]({{site.baseurl}}/api/basics) with the `users.external_ids.rename` permission.

        ## Rate limit

        We apply a rate limit of 1,000 requests per minute to this endpoint, as documented in [API rate limits]({{site.baseurl}}/api/api_limits/).

        ## Request body

        ```
        Content-Type: application/json
        Authorization: Bearer YOUR-REST-API-KEY
        ```

        ```json
        {
          "external_id_renames" : (required, array of external ID rename objects)
        }
        ```

        ## Request parameters

        | Parameter | Required | Data Type | Description |
        | --------- | ---------| --------- | ----------- |
        | `external_id_renames` | Required | Array of external identifier rename objects | View request example and the following limitations for the structure of the external identifier rename object. |

        Note the following:

        - The `current_external_id` must be the user's primary ID, and cannot be a deprecated ID. If the value passed as `current_external_id` is itself a deprecated ID on the profile, the call will fail. Before retrying a failed rename, use the [`/users/export/ids` endpoint]({{site.baseurl}}/api/endpoints/export/user_data/post_users_identifier/) to confirm which ID is currently primary.
        - The `new_external_id` must not already be in use as either a primary ID or a deprecated ID. Attempting to rename to an ID that is already stored as a deprecated ID returns a "new_external_id is already in use" error.
        - The `current_external_id` and `new_external_id` cannot be the same.

        ## Example request

        ```
        curl --location --request POST 'https://rest.iad-01.braze.com/users/external_ids/rename' \
        --header 'Content-Type: application/json' \
        --header 'Authorization: Bearer YOUR-REST-API-KEY' \
        --data-raw '{
          "external_id_renames" :[
            {
              "current_external_id": "existing_external_id",
              "new_external_id" : "new_external_id"
            }
          ]
        }'
        ```

        ## Response

        The response will confirm all successful renames, as well as unsuccessful renames with any associated errors. Error messages in the `rename_errors` field will reference the index of the object in the array of the original request.

        ```
        {
          "message" : (string) status message,
          "external_ids" : (array of strings) successful rename operations,
          "rename_errors": (array of arrays) <minor error message>
        }
        ```

        The `message` field will return `success` for any valid request. More specific errors are captured in the `rename_errors` array. The `message` field returns an error in the case of:

        - Invalid API key
        - Empty `external_id_renames` array
        - `external_id_renames` array with more than 50 objects
        - Rate limit hit (more than 1,000 requests per minute)

        ## How renaming works

        When you call this endpoint, it assigns a new primary `external_id` to a user profile while simultaneously converting the previous primary `external_id` into a deprecated external ID. After a successful rename, the user profile holds exactly one primary `external_id` (the new value) and one deprecated external ID (the old value).

        Subsequent rename calls on the same profile are permitted: each rename creates an additional deprecated external ID, so a profile may accumulate one primary `external_id` and multiple deprecated external IDs over time. However, the `new_external_id` value must not already exist on any Braze profile, either as a primary or a deprecated external ID.

        The endpoint does not log data points and does not affect MAU counts. All historical user data—events, purchases, attributes, campaign engagement—remains attached to the same profile.

        ## Bulk migrations

        For migrations involving large user populations, batch users into groups of up to 50 and send each batch as a separate API call. The endpoint is subject to a rate limit of 1,000 requests per minute. At maximum batch size (50 objects per request), this allows up to 50,000 user renames per minute.

        Each rename object in the batch is processed independently. A failure on one object does not block the others in the same request. The response body distinguishes successful renames (listed in the `external_ids` array) from failed ones (listed in the `rename_errors` array with an index reference back to the position of the failing object in the request array).

        When running bulk migrations:

        1. Iterate through the full user population in batches of up to 50 pairs.
        2. On each response, inspect both `external_ids` (success) and `rename_errors` (failure) to identify any users that need to be retried.
        3. Collect failed objects and schedule retry batches separately. Common failure causes include the `new_external_id` already being in use, or the `current_external_id` being a deprecated ID rather than a primary ID.
        4. Log successes and failures against your own records so the migration state is tracked outside Braze.

        ## Verifying the current external ID

        During a migration, you may need to confirm which external ID is the active primary identifier on a given profile—for example, when determining whether a specific user has already been migrated, or when troubleshooting a failed rename. Use the [`/users/export/ids` endpoint]({{site.baseurl}}/api/endpoints/export/user_data/post_users_identifier/) for this purpose.

        The export endpoint resolves both primary and deprecated external IDs to the same underlying profile and always returns the current primary `external_id` in the response. This means you can query any known identifier for a user—old or new—and the response will contain the canonical primary ID. This is a reliable way to determine migration state.

        To check only the external ID (rather than pulling the full profile), pass `fields_to_export` with only the `external_id` field.

        ## Recommended migration workflow

        For most migration use cases, the recommended sequence is:

        1. **Test in staging** — Run the full rename-and-verify flow against a development or staging workspace before touching production.
        2. **Rename in batches** — Use the `/users/external_ids/rename` endpoint in batches of up to 50, handling `rename_errors` on each response and queuing failed pairs for retry.
        3. **Verify** — After each batch (or at the end of migration), spot-check profiles using `/users/export/ids` to confirm the expected primary `external_id` is set.
        4. **Maintain the deprecation window** — Keep deprecated external IDs active for as long as any system (including legacy app versions in the field) may still reference the old IDs. Don't rush this step.
        5. **Remove deprecated IDs** — Once all systems are confirmed to be using the new IDs, use [`/users/external_ids/remove`]({{site.baseurl}}/api/endpoints/user_data/external_id_migration/post_external_ids_remove) in batches of up to 50 to clean up.

        If you're also migrating your SDK integration (for example, changing the value passed to `changeUser`), coordinate the API-side rename with the app release schedule so that the new external ID is in use on both the server and the client before deprecated IDs are removed.

        ## Frequently asked questions

        ### Does this impact MAU?

        No, because the number of users stays the same, they have a new `external_id`.

        ### Does user behavior change historically?

        No, because the user is still the same, and all their historical behavior is still connected to them.

        ### Can it be run on development or staging workspaces?

        Yes. In fact, we highly recommend running a test migration on a staging or development workspace, and ensuring everything has gone smoothly before executing on production data.

        ### Does this log data points?

        This feature does not log data points.

        ### What is the recommended deprecation period?

        We have no hard limit on how long you can keep deprecated external IDs around, but we highly recommend removing them after there is no longer a need to reference users by the deprecated ID.

        ### How many deprecated external IDs can a profile have?

        A user profile can hold one primary `external_id` and any number of deprecated external IDs accumulated through successive rename operations. There is no documented cap on the number of deprecated IDs a single profile can hold, but Braze recommends removing them as soon as they are no longer needed.

        ## Response status codes

        The following table lists the responses for this endpoint, the error message you may receive, and how to resolve it.

        | Status code | Meaning | Error message | How to resolve |
        | --- | --- | --- | --- |
        | `201 Created` | The request succeeded and the external IDs were queued for renaming. | `success` | No action needed. |
        | `400 Bad Request` | The `external_id_renames` value isn't an array. | `'external_id_renames' must be an array.` | Pass `external_id_renames` as an array of objects. |
        | `400 Bad Request` | The `external_id_renames` array is empty. | `'external_id_renames' must include at least one element.` | Include at least one rename object. |
        | `400 Bad Request` | More than 50 renames were provided. | `Max input length exceeded for 'external_id_renames'.` | Send 50 or fewer renames per request. |
        | `401 Unauthorized` | The REST API key is missing, malformed, or sent to the wrong REST endpoint. | `Invalid API key` | Send the key as `Authorization: Bearer YOUR_REST_API_KEY` to the correct [REST endpoint]({{site.baseurl}}/api/basics/#endpoints). For more causes, see [Errors and responses]({{site.baseurl}}/api/errors/#fatal-errors). |
        | `403 Access Denied` | The REST API key doesn't have the required permission. | `Access Denied` | Use a REST API key that has the `users.external_ids.rename` permission. |
        | `429 Rate Limited` | You exceeded the rate limit for this endpoint. | `Over rate limit` | Slow your request rate and retry with exponential backoff. See [API rate limits]({{site.baseurl}}/api/api_limits/). |
        | `5XX Internal Server Error` | An unexpected error occurred on the Braze server. | `Internal Server Error` | Retry with exponential backoff. If the error persists, contact [Support]({{site.baseurl}}/braze_support/). |
      operationId: renameExternalIds
      tags: [User data]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RenameExternalIdsRequest'
            examples:
              rename:
                value:
                  external_id_renames:
                    - current_external_id: existing_external_id
                      new_external_id: new_external_id
      responses:
        "200":
          description: Request processed.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RenameExternalIdsResponse'
        "400":
          description: Invalid request.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        "401":
          description: Unauthorized.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      bearerFormat: REST API key
  schemas:
    ExternalIdRename:
      type: object
      required: [current_external_id, new_external_id]
      properties:
        current_external_id:
          type: string
        new_external_id:
          type: string
    RenameExternalIdsRequest:
      type: object
      required: [external_id_renames]
      properties:
        external_id_renames:
          type: array
          minItems: 1
          maxItems: 50
          items:
            $ref: '#/components/schemas/ExternalIdRename'
    RenameExternalIdsResponse:
      type: object
      properties:
        message:
          type: string
          example: success
        external_ids:
          type: array
          items:
            type: string
        rename_errors:
          type: array
          items:
            type: array
            items:
              type: string
    ErrorResponse:
      type: object
      properties:
        message:
          type: string
