> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.onesignal.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create or update alias

> Add or update aliases on an existing user when you already know one of that user's aliases, such as an external_id, onesignal_id, or custom alias. Updates the identity object only.

## Overview

Add or update aliases on a user by providing one alias that already exists within OneSignal. An alias is a `key : value` pair: the key is the `alias_label` such as `external_id` or a custom label like `crm_user_id`, and the value is that user's ID in your own system.

This endpoint updates the `identity` object only. It does not create users, and it does not modify Subscriptions, Tags, or other user properties. The `onesignal_id` is read-only. To create a user with Subscriptions and properties, use the [Create user](/reference/create-user) API instead.

<Note>
  Sending an `alias_label` the user already has replaces that label's value. Sending an `alias_label` the user does not have adds a new alias, and aliases you leave out of the request stay unchanged. A label and value pair can belong to only one user in the app, so claiming a pair that another user already holds fails with `409 Conflict`.
</Note>

***

## How to use this API

Identify the user with two **path parameters**:

* `alias_label`: the type of alias you know, for example `external_id`, `onesignal_id`, or a custom alias label.
* `alias_id`: the value of that alias.

The path identifies the user that exists within OneSignal. The request body sets the new aliases you want to add or update. Inside the `identity` object, each key is an alias label and each value is that user's ID for the label:

```json theme={null}
{
  "identity": {
    "external_id": "user_123",
    "crm_user_id": "XYZ789"
  }
}
```

That request sets two aliases on the user you named in the path. Changes take effect immediately.

<Tip>
  Set the `external_id` before you add custom aliases. The `external_id` is what links a user's push, email, and SMS Subscriptions into one user record. Your frontend SDK sets it through the `login` method when a user signs in.
</Tip>

If you only know a Subscription ID and no alias, use the [Create alias (by subscription)](/reference/create-alias-by-subscription) API instead.

### Alias conflicts

A conflict depends on who owns an alias, not on whether the alias already exists. Labels themselves are shared: every user in the app can have an `external_id`. What must be unique is the label and value pair, and that pair identifies exactly one user:

* **The user in the path already has the label.** OneSignal updates the value. Changing `crm_user_id` from `111` to `222` on that user succeeds.
* **A different user already holds that label and value.** The request fails with `409 Conflict` and nothing on either user changes. Sending `crm_user_id: 222` fails when another user already has `crm_user_id: 222`.

This endpoint never moves an alias between users and never merges the two records. A conflict returns error code `user-2` with the title `One or more Aliases claimed by another User`. Each conflicting label appears in `errors[].meta`, mapped to the alias ID another user already holds:

```json theme={null}
{
  "errors": [
    {
      "code": "user-2",
      "title": "One or more Aliases claimed by another User",
      "meta": {
        "external_id": "user_123"
      }
    }
  ]
}
```

<Warning>
  A 409 usually means the target user already exists. To attach the current Subscription to that existing user, use the [Transfer Subscription](/reference/transfer-subscription) API rather than retrying this endpoint.
</Warning>

### Limits

Each user supports one `external_id` plus up to **10 custom aliases**. Alias keys (`alias_label`) and values (`alias_id`) are each limited to **128 characters**.

***

## FAQ

### What happens if I add a custom alias to a user with no External ID?

The alias attaches to a single user record instead of to the person, and you cannot fix it afterward by repeating the call. Without an `external_id`, OneSignal treats each Subscription as its own user with its own `onesignal_id`, so one person's web, mobile, email, and SMS Subscriptions are four separate records. Adding a custom alias to one record does not reach the other three, and adding the same alias value to a second record returns `409 Conflict` because that value already identifies the first. Set the `external_id` through your SDK's `login` method before you add custom aliases. See [Users](/docs/en/users) and [Aliases](/docs/en/aliases) for details.

### What happens if the alias label already exists?

It depends on which user owns it, not on the fact that it exists. If the user you identified in the path already has that label, OneSignal updates its value, and aliases you do not include in the request are left unchanged. If the alias value belongs to a different user, the request fails with `409 Conflict` and nothing changes. See [Alias conflicts](#alias-conflicts).

### How do I attach a Subscription to a user that already owns the alias?

Use the [Transfer Subscription](/reference/transfer-subscription) API, which reassigns a Subscription to a different user in the same app. This endpoint cannot do it, because claiming an alias that another user already owns returns `409 Conflict`. Transfer Subscription needs the `subscription_id` and exactly one alias identifying the target user.

### Can I set the `onesignal_id` with this API?

No. OneSignal generates the `onesignal_id` when the user record is created, and it is read-only. You can use it as the `alias_label` to identify the user, but you cannot assign or change its value.

### How do I remove an alias?

Use the [Delete alias](/reference/delete-alias) API. The `onesignal_id` cannot be removed.

***

## Related pages

<Columns cols={2}>
  <Card title="Users" icon="users" href="/docs/en/users">
    How OneSignal ID and External ID identify a user across devices.
  </Card>

  <Card title="Aliases" icon="tag" href="/docs/en/aliases">
    Set and manage custom aliases with the SDK or the REST API.
  </Card>

  <Card title="Create user" icon="user-plus" href="/reference/create-user">
    Create a user with Subscriptions and properties, not just aliases.
  </Card>

  <Card title="Delete alias" icon="trash" href="/reference/delete-alias">
    Remove a single alias without deleting the user.
  </Card>

  <Card title="Transfer Subscription" icon="arrow-right-arrow-left" href="/reference/transfer-subscription">
    Move a Subscription to a user that already owns the alias.
  </Card>
</Columns>

***


## OpenAPI

````yaml PATCH /apps/{app_id}/users/by/{alias_label}/{alias_id}/identity
openapi: 3.1.0
info:
  title: api.onesignal.com
  version: '11.6'
servers:
  - url: https://api.onesignal.com
security:
  - {}
paths:
  /apps/{app_id}/users/by/{alias_label}/{alias_id}/identity:
    patch:
      summary: Create or update alias
      description: >-
        Create or update one or more user aliases when you already know an
        existing alias. This API upserts the user's identity object: it adds new
        aliases or updates existing ones.
      operationId: create-alias
      parameters:
        - name: app_id
          in: path
          description: >-
            Your OneSignal App ID in UUID v4 format. See [Keys &
            IDs](/docs/en/keys-and-ids).
          schema:
            type: string
          required: true
        - name: alias_label
          in: path
          description: >-
            The alias name or key to locate the user. Most commonly set as
            `external_id` but can be the `onesignal_id` or a [custom
            alias](/docs/en/aliases).
          schema:
            type: string
          required: true
        - name: alias_id
          in: path
          description: The specific identifier for the given alias to identify the user.
          schema:
            type: string
          required: true
        - name: Authorization
          in: header
          description: >-
            Your App API key with prefix `Key `. See [Keys &
            IDs](/docs/en/keys-and-ids).
          required: true
          schema:
            type: string
            default: Key YOUR_APP_API_KEY
      requestBody:
        content:
          application/json:
            schema:
              type: object
              properties:
                identity:
                  type: object
                  description: One or more aliases to be created for this user.
                  properties:
                    external_id:
                      type: string
                      description: >-
                        The main user ID to identify the user. See
                        [Users](/docs/en/users).
                      default: test_external_id
                    custom_alias_label:
                      type: string
                      description: >-
                        A custom alias of the user. Replace `custom_alias_label`
                        with your own alias key, and set the value to that
                        user's ID for the key. See [Aliases](/docs/en/aliases).
                      example: the-users-custom-alias-id
                    onesignal_id:
                      type: string
                      description: >-
                        The OneSignal ID of the user. Generated by OneSignal and
                        read-only. See [Users](/docs/en/users).
                      readOnly: true
                  additionalProperties:
                    type: string
                    description: >-
                      Any additional custom alias, provided as an alias key and
                      value. See [Aliases](/docs/en/aliases).
      responses:
        '200':
          description: '200'
          content:
            application/json:
              schema:
                type: object
                properties:
                  identity:
                    type: object
                    properties:
                      onesignal_id:
                        type: string
                        description: >-
                          The OneSignal ID of the user. See
                          [Users](/docs/en/users).
                        example: OneSignal-ID-in-UUID-v4-format
                      custom_alias_label:
                        type: string
                        description: >-
                          A custom alias of the user. See
                          [Aliases](/docs/en/aliases).
                        example: the-users-custom-alias-id
        '400':
          description: '400'
          content:
            application/json:
              examples:
                Result:
                  value:
                    errors:
                      - code: request-1
                        title: Invalid UUID
                        meta:
                          onesignal_id: '123'
              schema:
                type: object
                properties:
                  errors:
                    type: array
                    items:
                      type: object
                      properties:
                        code:
                          type: string
                          example: request-1
                        title:
                          type: string
                          example: Invalid UUID
                        meta:
                          type: object
                          properties:
                            onesignal_id:
                              type: string
                              example: '123'
        '404':
          description: '404'
          content:
            application/json:
              examples:
                Result:
                  value:
                    errors:
                      - code: internal error code
                        title: example error title
                        meta: {}
              schema:
                $ref: '#/components/schemas/StructuredErrorResponse'
        '409':
          description: >-
            Conflict. One or more aliases in the request are already claimed by
            a different user. Updating a label the same user already has is not
            a conflict; it updates the value. No aliases change on either user,
            and the alias is not moved between them. `errors[].meta` maps each
            conflicting alias label to the alias ID that another user already
            holds. To attach a Subscription to the user that already owns the
            alias, use the [Transfer
            Subscription](/reference/transfer-subscription) API.
          content:
            application/json:
              examples:
                Result:
                  value:
                    errors:
                      - code: user-2
                        title: One or more Aliases claimed by another User
                        meta:
                          external_id: user_123
              schema:
                $ref: '#/components/schemas/AliasConflictResponse'
        '429':
          description: '429'
          content:
            application/json:
              examples:
                Result:
                  value:
                    errors:
                      - code: Rate Limit Exceeded
                        title: Example error title
                        meta: {}
              schema:
                $ref: '#/components/schemas/StructuredErrorResponse'
          headers:
            Retry-After:
              description: >-
                Number of seconds to wait before retrying the request. Always
                emitted on 429 responses.
              schema:
                type: integer
                minimum: 0
        '503':
          description: >-
            Service temporarily unavailable. Retry after a short backoff. The
            body may be empty or non-JSON in some failure modes.
          headers:
            Retry-After:
              description: >-
                Number of seconds to wait before retrying the request. This
                header is optional and may be absent when a proxy or load
                balancer generates the 503.
              schema:
                type: integer
                minimum: 0
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/BasicErrorResponse'
              example:
                errors:
                  - Service temporarily unavailable
      deprecated: false
      x-codeSamples:
        - lang: typescript
          label: Node.js SDK
          source: |-
            import Onesignal from '@onesignal/node-onesignal';

            const configuration = Onesignal.createConfiguration({
                restApiKey: 'YOUR_REST_API_KEY',
            });
            const apiInstance = new Onesignal.DefaultApi(configuration);

            // string
            const appId: string = "YOUR_APP_ID";
            // string
            const aliasLabel: string = "external_id";
            // string
            const aliasId: string = "YOUR_USER_EXTERNAL_ID";
            // UserIdentityBody
            const userIdentityBody: Onesignal.UserIdentityBody = {
                identity: {
                  "key": "key_example",
                },
              };

            try {
              const response = await apiInstance.createAlias(appId, aliasLabel, aliasId, userIdentityBody);
              console.log(response);
            } catch (e) {
              if (e instanceof Onesignal.ApiException) {
                // `e.errorMessages` flattens any error-envelope shape to a `string[]`;
                // the raw parsed body remains on `e.body`.
                console.error("createAlias failed: HTTP " + e.code, e.errorMessages);
              } else {
                throw e;
              }
            }
        - lang: python
          label: Python SDK
          source: >-
            import onesignal

            from onesignal.api import default_api

            from onesignal.models import *

            from pprint import pprint


            # See configuration.py for a list of all supported configuration
            parameters.

            # Some of the OneSignal endpoints require ORGANIZATION_API_KEY token
            for authorization, while others require REST_API_KEY.

            # We recommend adding both of them in the configuration page so that
            you will not need to figure it out yourself.

            configuration = onesignal.Configuration(
                rest_api_key = "YOUR_REST_API_KEY", # App REST API key required for most endpoints
                organization_api_key = "YOUR_ORGANIZATION_API_KEY" # Organization key is only required for creating new apps and other top-level endpoints
            )



            # Enter a context with an instance of the API client

            with onesignal.ApiClient(configuration) as api_client:
                # Create an instance of the API class
                api_instance = default_api.DefaultApi(api_client)
                app_id = "YOUR_APP_ID" 
                alias_label = "external_id" 
                alias_id = "YOUR_USER_EXTERNAL_ID" 
                user_identity_body = UserIdentityBody(
                    identity=IdentityObject(
                        key="key_example",
                    ),
                ) 

                try:
                    api_response = api_instance.create_alias(app_id, alias_label, alias_id, user_identity_body)
                    pprint(api_response)
                except onesignal.ApiException as e:
                    print("Exception when calling DefaultApi->create_alias: %s\n" % e)
                    print("Status Code: %s" % e.status)
                    print("Response Body: %s" % e.body)
        - lang: php
          label: PHP SDK
          source: >-
            <?php

            require_once(__DIR__ . '/vendor/autoload.php');



            // Configure Bearer authorization: rest_api_key

            $config = onesignal\client\Configuration::getDefaultConfiguration()
                                                            ->setRestApiKeyToken('YOUR_REST_API_KEY')
                                                            ->setOrganizationApiKeyToken('YOUR_ORGANIZATION_API_KEY');



            $apiInstance = new onesignal\client\Api\DefaultApi(
                // If you want use custom http client, pass your client which implements `GuzzleHttp\ClientInterface`.
                // This is optional, `GuzzleHttp\Client` will be used as default.
                new GuzzleHttp\Client(),
                $config
            );

            $app_id = 'YOUR_APP_ID'; // string

            $alias_label = 'external_id'; // string

            $alias_id = 'YOUR_USER_EXTERNAL_ID'; // string

            $user_identity_body = new
            \onesignal\client\model\UserIdentityBody(); //
            \onesignal\client\model\UserIdentityBody


            try {
                $result = $apiInstance->createAlias($app_id, $alias_label, $alias_id, $user_identity_body);
                print_r($result);
            } catch (\onesignal\client\ApiException $e) {
                echo 'Exception when calling DefaultApi->createAlias: ', $e->getMessage(), PHP_EOL;
                echo 'Status Code: ', $e->getCode(), PHP_EOL;
                // getErrorMessages() flattens any error-envelope shape to a string[];
                // the raw body remains on getResponseBody().
                echo 'Error Messages: ', implode(', ', $e->getErrorMessages()), PHP_EOL;
                echo 'Response Body: ', $e->getResponseBody(), PHP_EOL;
            } catch (\Exception $e) {
                echo 'Exception when calling DefaultApi->createAlias: ', $e->getMessage(), PHP_EOL;
            }
        - lang: go
          label: Go SDK
          source: |-
            package main

            import (
                "context"
                "fmt"
                "os"

                "github.com/OneSignal/onesignal-go-api/v5"
            )

            func main() {
                appId := "YOUR_APP_ID" // string | 
                aliasLabel := "external_id" // string | 
                aliasId := "YOUR_USER_EXTERNAL_ID" // string | 
                userIdentityBody := *onesignal.NewUserIdentityBody() // UserIdentityBody | 

                configuration := onesignal.NewConfiguration()
                apiClient := onesignal.NewAPIClient(configuration)

                restAuth := context.WithValue(context.Background(), onesignal.RestApiKey, "YOUR_REST_API_KEY") // App REST API key required for most endpoints

                resp, r, err := apiClient.DefaultApi.CreateAlias(restAuth, appId, aliasLabel, aliasId).UserIdentityBody(userIdentityBody).Execute()

                if err != nil {
                    fmt.Fprintf(os.Stderr, "Error when calling `DefaultApi.CreateAlias``: %v\n", err)
                    fmt.Fprintf(os.Stderr, "Full HTTP response: %v\n", r)
                    if apiErr, ok := err.(*onesignal.GenericOpenAPIError); ok {
                        // ErrorMessages() flattens any error-envelope shape to a []string;
                        // the raw body remains on Body().
                        fmt.Fprintf(os.Stderr, "Error Messages: %v\n", apiErr.ErrorMessages())
                        fmt.Fprintf(os.Stderr, "Response Body: %s\n", apiErr.Body())
                    }
                }
                // response from `CreateAlias`: UserIdentityBody
                fmt.Fprintf(os.Stdout, "Response from `DefaultApi.CreateAlias`: %v\n", resp)
            }
        - lang: ruby
          label: Ruby SDK
          source: >-
            require 'onesignal'

            # setup authorization

            OneSignal.configure do |config|
              # Configure Bearer authorization: rest_api_key
              config.rest_api_key = 'YOUR_REST_API_KEY'

            end


            api_instance = OneSignal::DefaultApi.new

            app_id = 'YOUR_APP_ID' # String | 

            alias_label = 'external_id' # String | 

            alias_id = 'YOUR_USER_EXTERNAL_ID' # String | 

            user_identity_body = OneSignal::UserIdentityBody.new #
            UserIdentityBody | 


            begin
              
              result = api_instance.create_alias(app_id, alias_label, alias_id, user_identity_body)
              p result
            rescue OneSignal::ApiError => e
              puts "Error when calling DefaultApi->create_alias: #{e}"
              puts "Status Code: #{e.code}"
              # `e.error_messages` flattens any error-envelope shape to an Array<String>;
              # the raw body remains on `e.response_body`.
              puts "Error Messages: #{e.error_messages}"
              puts "Response Body: #{e.response_body}"
            end
        - lang: java
          label: Java SDK
          source: |-
            // Import classes:
            import com.onesignal.client.ApiClient;
            import com.onesignal.client.ApiException;
            import com.onesignal.client.Configuration;
            import com.onesignal.client.auth.*;
            import com.onesignal.client.model.*;
            import com.onesignal.client.api.DefaultApi;

            public class Example {
              public static void main(String[] args) {
                ApiClient defaultClient = Configuration.getDefaultApiClient();
                defaultClient.setBasePath("https://api.onesignal.com");
                
                // Configure HTTP bearer authorization: rest_api_key
                HttpBearerAuth rest_api_key = (HttpBearerAuth) defaultClient.getAuthentication("rest_api_key");
                rest_api_key.setBearerToken("YOUR_REST_API_KEY");

                DefaultApi apiInstance = new DefaultApi(defaultClient);
                String appId = "YOUR_APP_ID"; // String | 
                String aliasLabel = "external_id"; // String | 
                String aliasId = "YOUR_USER_EXTERNAL_ID"; // String | 
                UserIdentityBody userIdentityBody = new UserIdentityBody(); // UserIdentityBody | 
                try {
                  UserIdentityBody result = apiInstance.createAlias(appId, aliasLabel, aliasId, userIdentityBody);
                  System.out.println(result);
                } catch (ApiException e) {
                  System.err.println("Exception when calling DefaultApi#createAlias");
                  System.err.println("Status code: " + e.getCode());
                  // getErrorMessages() flattens any error-envelope shape to a List<String>;
                  // the raw body remains on getResponseBody().
                  System.err.println("Error messages: " + e.getErrorMessages());
                  System.err.println("Reason: " + e.getResponseBody());
                  System.err.println("Response headers: " + e.getResponseHeaders());
                  e.printStackTrace();
                }
              }
            }
        - lang: csharp
          label: C# SDK
          source: |-
            using System;
            using System.Collections.Generic;
            using System.Diagnostics;
            using OneSignalApi.Api;
            using OneSignalApi.Client;
            using OneSignalApi.Model;

            namespace Example
            {
                public class CreateAliasExample
                {
                    public static void Main()
                    {
                        Configuration config = new Configuration();
                        config.BasePath = "https://api.onesignal.com";
                        // Configure Bearer token for authorization: rest_api_key
                        config.AccessToken = "YOUR_REST_API_KEY";

                        var apiInstance = new DefaultApi(config);
                        var appId = "YOUR_APP_ID";  // string | 
                        var aliasLabel = "external_id";  // string | 
                        var aliasId = "YOUR_USER_EXTERNAL_ID";  // string | 
                        var userIdentityBody = new UserIdentityBody(); // UserIdentityBody | 

                        try
                        {
                            UserIdentityBody result = apiInstance.CreateAlias(appId, aliasLabel, aliasId, userIdentityBody);
                            Debug.WriteLine(result);
                        }
                        catch (ApiException  e)
                        {
                            Debug.Print("Exception when calling DefaultApi.CreateAlias: " + e.Message );
                            Debug.Print("Status Code: "+ e.ErrorCode);
                            // e.ErrorMessages flattens any error-envelope shape to an IReadOnlyList<string>;
                            // the raw body remains on e.ErrorContent.
                            Debug.Print("Error Messages: " + string.Join(", ", e.ErrorMessages));
                            Debug.Print("Response Body: " + e.ErrorContent);
                            Debug.Print(e.StackTrace);
                        }
                    }
                }
            }
        - lang: rust
          label: Rust SDK
          source: |-
            use onesignal_rust_api::apis::configuration::Configuration;
            use onesignal_rust_api::apis::default_api;

            use onesignal_rust_api::models;


            #[tokio::main]
            async fn main() {
                let mut configuration = Configuration::new();
                configuration.rest_api_key_token = Some("YOUR_REST_API_KEY".to_string());


                // Realistic values are pulled from the spec's `example:` fields where present.
                let app_id: &str = "YOUR_APP_ID";
                let alias_label: &str = "external_id";
                let alias_id: &str = "YOUR_USER_EXTERNAL_ID";
                let user_identity_body: models::UserIdentityBody = todo!();

                match default_api::create_alias(&configuration, app_id, alias_label, alias_id, user_identity_body).await {
                    Ok(resp) => println!("{:?}", resp),
                    Err(e @ onesignal_rust_api::apis::Error::ResponseError(_)) => {
                        // `e.error_messages()` flattens any error-envelope shape to a Vec<String>;
                        // the raw response remains on the ResponseError variant.
                        eprintln!("create_alias failed: {:?}", e.error_messages());
                    }
                    Err(e) => eprintln!("create_alias failed: {:?}", e),
                }
            }
components:
  schemas:
    StructuredErrorResponse:
      type: object
      properties:
        errors:
          type: array
          items:
            $ref: '#/components/schemas/StructuredErrorItem'
    AliasConflictResponse:
      type: object
      description: >-
        Returned at HTTP 409 by the alias endpoints when one or more aliases in
        the request are already claimed by a different user. The request is
        rejected in full: no alias is created, updated, or moved between users.
        Note that `meta` is a flat map of alias label to alias ID, not a nested
        object.
      properties:
        errors:
          type: array
          items:
            type: object
            required:
              - code
              - title
            properties:
              code:
                type: string
                description: 'Stable error code identifier. For this response: `user-2`.'
                example: user-2
              title:
                type: string
                description: Human-readable conflict description.
                example: One or more Aliases claimed by another User
              meta:
                type: object
                description: >-
                  Maps each conflicting alias label to the alias ID that another
                  user already holds. To attach a Subscription to that existing
                  user, use the [Transfer
                  Subscription](/reference/transfer-subscription) API instead of
                  retrying.
                additionalProperties:
                  type: string
                example:
                  external_id: user_123
    BasicErrorResponse:
      type: object
      properties:
        errors:
          type: array
          items:
            type: string
          description: One or more human-readable error messages.
        success:
          type: boolean
          description: >-
            Present (and `false`) on some endpoints (notifications, templates,
            segments). Not emitted by every endpoint.
        reference:
          type: array
          items:
            type: string
          description: >-
            Documentation URL fragments related to the error. Only emitted by
            the API-key auth error helpers.
    StructuredErrorItem:
      type: object
      required:
        - code
        - title
      properties:
        code:
          type: string
          description: >-
            Stable error-code identifier. Use this for programmatic branching in
            your integration.
        title:
          type: string
          description: >-
            Human-readable error message intended for logs and operator-facing
            surfaces.
        meta:
          type: object
          additionalProperties: true
          description: >-
            Optional extra details for this error. Properties depend on `code`.
            For create-user `Conflict`, `meta.conflicting_aliases` maps each
            colliding alias label to the alias ID already bound to another user.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.