> For the complete documentation index, see [llms.txt](https://docs.tylt.money/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tylt.money/tylt-embedded-wallet-service/user-management/create-user.md).

# Create User

Creates a new end user or links an existing user to the merchant.

### Endpoint

```http
POST /whitelabel/users/signup
```

### Request Headers

```http
x-tlp-apikey: <api-key>
x-tlp-signature: <hmac-signature>
Content-Type: application/json
```

### Request Parameters

<table><thead><tr><th>Field</th><th>Type</th><th width="154" align="right">Required</th><th>Description</th></tr></thead><tbody><tr><td><code>emailId</code></td><td>String</td><td align="right">Yes</td><td>Valid email address of the end user</td></tr><tr><td><code>externalUserId</code></td><td>String</td><td align="right">Yes</td><td>Unique user identifier assigned by the merchant</td></tr><tr><td><code>firstName</code></td><td>String</td><td align="right">No</td><td>End user’s first name</td></tr><tr><td><code>lastName</code></td><td>String</td><td align="right">No</td><td>End user’s last name</td></tr><tr><td><code>countryOfResidence</code></td><td>String</td><td align="right">Yes</td><td>The ISO 3166-1 alpha-3 code representing the country in which the individual currently and ordinarily resides. Eg. "CAN", "DEU","USA"</td></tr><tr><td><code>countryOfCitizenship</code></td><td>String</td><td align="right">No</td><td>The ISO 3166-1 alpha-3 code representing the country in which the individual currently a citizen.Eg. "CAN", "DEU","USA"</td></tr><tr><td><code>countryOfBirth</code></td><td>String</td><td align="right">No</td><td>The ISO 3166-1 alpha-3 code representing the country in which the individual was born.Eg. "CAN", "DEU","USA"</td></tr><tr><td><code>taxId</code></td><td>String</td><td align="right">No</td><td>Tax Id of the end user</td></tr><tr><td><code>dob</code></td><td>String</td><td align="right">Yes</td><td>Date of birth of the user in YYYY-MM-DD</td></tr><tr><td><code>phone</code></td><td>String</td><td align="right">No</td><td>End user’s telephone number, preferably in international format</td></tr><tr><td><code>timestamp</code></td><td>String</td><td align="right">Yes</td><td>Unix epoch milliseconds; allowed skew is ±120 seconds</td></tr><tr><td><code>nonce</code></td><td>String</td><td align="right">Yes</td><td>Unique random request string to prevent replay intercept actions</td></tr></tbody></table>

### Request Body

```json
{
  "emailId": "joe@example.com",
  "externalUserId": "user-10021",
  "firstName": "Joe",
  "lastName": "Doe",
  "dob": "2001-04-25",
  "countryOfResidence": "BRA",
  "countryOfCitizenship": "BRA",
  "countryOfBirth": "BRA",
  "taxId":"3100299900",
  "phone": "+14165550123",
  "timestamp": 1784793600000,
  "nonce": "9d8934143ca747ebaaba3100f15cb4a1"
}
```

### Code Snippet

{% tabs %}
{% tab title="JavaScript (Axios)" %}

```javascript
import crypto from 'crypto';
import axios from 'axios';

const apiKey = 'your-api-key';
const apiSecret = 'your-api-secret';

const requestBody = {
    emailId: 'joe@example.com',
    externalUserId: 'u-xyz-987',
    firstName: 'Joe',
    lastName: 'Doe',
    dob: '2001-04-25',
    countryOfResidence: 'BRA',
    countryOfCitizenship: 'BRA',  // optional
    countryOfBirth: 'BRA', // optional
    taxId:'23100299900' // optional
    phone: '+919999999999', // optional
    timestamp: Date.now(),
    nonce: crypto.randomBytes(8).toString('hex')
};

const raw = JSON.stringify(requestBody);
const signature = crypto.createHmac('sha256', apiSecret).update(raw).digest('hex');

const headers = {
    'x-tlp-apikey': apiKey,
    'x-tlp-signature': signature,
    'Content-Type': 'application/json'
};

axios.post('https://api.tylt.money/whitelabel/users/signup', raw, { headers })
    .then(response => console.log(response.data))
    .catch(error => console.error('Error:', error.response ? error.response.data : error.message));
```

{% endtab %}
{% endtabs %}

### Successful Response

#### New User Created

**HTTP Status**

```http
201 Created
```

**Response**

```json
{
  "msg": "User provisioned successfully.",
  "data": {
    "endUserId": 1012,
    "externalUserId": "user-10021",
    "ownerCode": "merchant-code",
    "kycStatus": "incomplete",
    "userUUID": "550e8400-e29b-41d4-a716-446655440000",
    "totpSecret": "JBSWY3DPEHPK3PXP",
    "totpQrCodeString": "otpauth://totp/..."
  }
}
```

### Response Fields

| Field              | Description                                                                    |
| ------------------ | ------------------------------------------------------------------------------ |
| `endUserId`        | Tylt end-user ID                                                               |
| `externalUserId`   | Merchant-provided user identifier                                              |
| `ownerCode`        | Merchant or wallet-program code                                                |
| `kycStatus`        | Current overall KYC status                                                     |
| `userUUID`         | Tylt user UUID, returned only when the user is first created                   |
| `totpSecret`       | Two-factor authentication secret, returned only when the user is first created |
| `totpQrCodeString` | Authenticator enrollment URI, returned only when the user is first created     |

### Two-Factor Authentication Enrollment

The `totpSecret` and `totpQrCodeString` must be securely provided to the end user so that they can enroll an authenticator application.

The merchant must:

1. Store the credentials securely.
2. Never expose the secret in logs or analytics systems.
3. Display the QR code or enrollment information only to the relevant end user.
4. Require the end user to complete two-factor authentication enrollment before initiating transactions.

The TOTP credentials are returned only during the first successful user creation.

### Existing User Response

When the same user already exists with the same `externalUserId`, the request is treated as idempotent.

**HTTP Status**

```http
200 OK
```

**Response**

```json
{
  "msg": "User already exists.",
  "data": {
    "endUserId": 1012,
    "externalUserId": "user-10021",
    "ownerCode": "merchant-code",
    "kycStatus": "approved"
  }
}
```

Credentials such as `totpSecret`, `totpQrCodeString`, and `userUUID` are not returned again.

### User-Linking Behaviour

Where a user already exists under the merchant but is not yet mapped to the submitted `externalUserId`, Tylt may link the user to the merchant’s identifier.

A successful linking request returns:

```http
200 OK
```

No new TOTP credentials are returned.

### Possible Errors

| HTTP Status | Message                                                                      | Description                                         |
| ----------- | ---------------------------------------------------------------------------- | --------------------------------------------------- |
| `400`       | Validation error                                                             | One or more required fields are missing or invalid  |
| `401`       | `Api Key authentication failed!`                                             | API key or signature is missing or invalid          |
| `403`       | `API key or owner is inactive.`                                              | API access or merchant account is inactive          |
| `403`       | `IP not whitelisted.`                                                        | Request originated from an unauthorized IP address  |
| `409`       | `externalUserId is already mapped to a different user.`                      | The external ID is already assigned to another user |
| `409`       | `Email already registered under this owner with a different externalUserId.` | The email and external ID conflict                  |
| `429`       | Rate-limit error                                                             | Too many user-provisioning requests                 |

***
