> For the complete documentation index, see [llms.txt](https://user.netmera.com/netmera-developer-guide/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://user.netmera.com/netmera-developer-guide/api-documentation/rest-api/user-and-device-management.md).

# User & Device Management

The Netmera API enables server-side management of users, devices, notifications, tags, categories, and profile attributes. You can register users, manage permissions, send notifications, retrieve user data, and manage opt-ins/opt-outs for email and SMS.

#### General Limitations

* **Maximum 1000 unique external IDs per request**: If you need to send data for more than 1000 users, split the request into multiple batches to ensure performance and reliability.

## User Registration

**Endpoint:** `POST /rest/3.0/registerUsers`\
Registers users and their devices from your backend, or updates existing ones. Each entry in the array is one registration: a device identified by its push token and platform, or an SMS-only user identified by MSISDN. Optional tags and profile attributes are applied to the user in the same request.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/registerUsers \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "extId": "id_in_my_system_1",     // Recommended: External user ID in your system
      "platform": "IOS",                // Required unless msisdn is sent: IOS, ANDROID, ...
      "deviceToken": "my_device_token", // Push token of the device
      "email": "john.brown@example.com",
      "msisdn": "+905xxxxxxxxx",
      "tags": ["Sport"],                // Optional: Tags to add to the user
      "profile": {                      // Optional: Profile attributes to set
        "name": "John",
        "age": 34
      }
    }
  ]'
```

#### Body Parameters

<table><thead><tr><th width="202">Parameter</th><th width="120">Required</th><th>Description</th></tr></thead><tbody><tr><td>platform</td><td>Conditional</td><td>Platform of the device (e.g. <code>IOS</code>, <code>ANDROID</code>). Required unless <code>msisdn</code> is provided; a registration with only <code>msisdn</code> is stored as an SMS user.</td></tr><tr><td>msisdn</td><td>Conditional</td><td>Phone number of the user, including the country code.</td></tr><tr><td>deviceToken</td><td>No</td><td>Push token of the device.</td></tr><tr><td>extId</td><td>No</td><td>External user ID in your system. Needed to target the user in other endpoints.</td></tr><tr><td>email</td><td>No</td><td>Email address of the user.</td></tr><tr><td>tags</td><td>No</td><td>Array of tag names to add to the user.</td></tr><tr><td>profile</td><td>No</td><td>Key-value object of profile attributes. Keys must match attributes defined in the panel.</td></tr></tbody></table>

{% hint style="info" %}
Registrations are processed asynchronously. An entry that fails validation is skipped without affecting the others; the request is rejected only when every entry is invalid or when no registration is sent.
{% endhint %}

## User Deletion

### Delete Users

**Endpoint:** `POST /rest/3.0/deleteUsers`\
Deletes the given users from your application. The users stop being tracked and are no longer targeted by campaigns. This is the standard deletion; to erase all data of the users irreversibly use `deleteUsersPermanently` below.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/deleteUsers \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "extIds": [  // Required: Array of external user IDs to delete
      "externalid"
    ]
  }'
```

### Delete Users Permanently

**Endpoint:** `POST /rest/3.0/deleteUsersPermanently`\
Permanently deletes all user data from Netmera. This includes all associated devices, profile attributes, and event history.

> This operation is irreversible and requires prior activation through the Netmera panel.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/deleteUsersPermanently \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "extIds": [  // Required: Array of external user IDs to delete
      "externalid"
    ]
  }'
```

{% hint style="warning" %}
`deleteUsers` and `deleteUsersPermanently` take the same request body but differ in effect: `deleteUsers` removes the users from your application, whereas `deleteUsersPermanently` additionally erases every piece of data stored about them and cannot be undone. If permanent deletion has not been activated for your application, `deleteUsersPermanently` behaves like `deleteUsers`.
{% endhint %}

## Push Notification Permission Management

### Enable Push

**Endpoint:** `POST /rest/3.0/enablePush`\
Use to opt-in all devices of a user or a specific device for push notifications.

Opt-in by external user ID:

```json
curl -X POST https://restapi.netmera.com/rest/3.0/enablePush \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "extId": "id_in_my_system_1"  // Required: User ID to opt-in
  }'
```

Opt-in by device token:

```json
curl -X POST https://restapi.netmera.com/rest/3.0/enablePush \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "deviceToken": "my_device_token"  // Required: Device token to opt-in
  }'
```

### Disable Push

**Endpoint:** `POST /rest/3.0/disablePush`\
Use to opt-out all devices of a user or a single device from receiving push notifications.

Opt-out by external user ID:

```json
curl -X POST https://restapi.netmera.com/rest/3.0/disablePush \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "extId": "id_in_my_system_1"  // Required: User ID to opt-out
  }'
```

Opt-out by device token:

```json
curl -X POST https://restapi.netmera.com/rest/3.0/disablePush \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "deviceToken": "my_device_token"  // Required: Device token to opt-out
  }'
```

## Tag Management

### Add Tags to Users

**Endpoint:** `POST /rest/3.0/tagUsers`\
Adds one or more tags to the specified users.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/tagUsers \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "tag": "Sport",                // Required: Tag name
    "extIds": ["id1", "id2"]       // Required: Array of external user IDs
  }'
```

### Remove Tags from Users

**Endpoint:** `POST /rest/3.0/untagUsers`\
Removes a tag from the specified users.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/untagUsers \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "tag": "Sport",                // Required: Tag name to remove
    "extIds": ["id1", "id2"]       // Required: User IDs to remove tag from
  }'
```

## Category Preferences

Message categories can be scoped to specific channels in the panel, and a user's opt-in preference is stored per category and channel. The endpoints below accept an optional channel parameter that selects which channel the call applies to.

{% hint style="info" %}
The channel parameter is optional everywhere. Omit it and the call behaves exactly as it did before channel-based preferences were introduced, so existing integrations keep working without changes.
{% endhint %}

#### Channel Values

`MOBILE`, `SMS`, `EMAIL`, `WHATS_APP`, `IN_APP`, `WEB` — case-insensitive.

Categories created before channel selection was introduced carry no channel information and are treated as valid on all channels.

### Set Category Preferences

**Endpoint:** POST `/rest/3.0/setCategoryPreferences`\
Enables or disables category-specific opt-in preferences for a user. Send `channel` to update a single category and channel pair; omit it to apply the change to every channel of the category.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/setCategoryPreferences \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "extId": "id1",           // Required
      "category": "Shopping",   // Required
      "enable": true,           // Required: true to opt-in, false to opt-out
      "channel": "EMAIL"        // Optional: applies the change to this channel only
    },
    {
      "extId": "id1",
      "category": "Promotion",
      "enable": false           // No channel: applies to all channels of the category
    }
  ]'
```

#### Body Parameters

<table><thead><tr><th width="202">Parameter</th><th width="120">Required</th><th>Description</th></tr></thead><tbody><tr><td>extId</td><td>Yes</td><td>External user ID in your system.</td></tr><tr><td>category</td><td>Yes</td><td>Name of the message category. An entry naming a category that does not exist is skipped.</td></tr><tr><td>enable</td><td>Yes</td><td><code>true</code> to opt-in, <code>false</code> to opt-out.</td></tr><tr><td>channel</td><td>No</td><td>Applies the change only to this category and channel pair. When omitted, the change is applied to every channel of the category.</td></tr></tbody></table>

{% hint style="warning" %}
An entry with an unrecognized `channel` value is skipped and logged; the remaining entries in the request are still processed. A typo therefore leaves that preference unchanged rather than silently updating every channel of the category.
{% endhint %}

### Get Category Preferences

**Endpoint:** GET `/rest/3.0/getCategoryPreferences`\
Retrieves category-based opt-in preferences for a specific user, with a per-channel breakdown.

```json
curl -G "https://restapi.netmera.com/rest/3.0/getCategoryPreferences" \
  -H "X-netmera-api-key: your_rest_api_key" \
  --data-urlencode 'externalId=exid' \
  --data-urlencode 'categoryIds=56' \
  --data-urlencode 'channels=MOBILE,EMAIL'
```

#### Query Parameters

<table><thead><tr><th width="202.373046875">Parameter</th><th width="120.7965087890625">Required</th><th>Description</th></tr></thead><tbody><tr><td>externalId</td><td>Yes</td><td>External user ID in your system</td></tr><tr><td>pushPermitted</td><td>No</td><td>Filters results by push permission status (true / false)</td></tr><tr><td>categoryIds</td><td>No</td><td>Returns only these category IDs. When omitted, all categories are returned.</td></tr><tr><td>channels</td><td>No</td><td>Returns the permission state of these channels only. When omitted, every channel of the category is returned. Categories that support none of the requested channels are left out of the response.</td></tr></tbody></table>

Both list parameters accept either repeated parameters (`?channels=MOBILE&channels=EMAIL`) or a comma-separated value (`?channels=MOBILE,EMAIL`). Repeated values are collapsed automatically.

**Sample response**

```json
{
  "categoryPreferences": {
    "Shopping": true,
    "Promotion": false
  },
  "categoryChannelPreferences": {
    "Shopping": {
      "MOBILE": true,
      "EMAIL": false
    },
    "Promotion": {
      "MOBILE": false,
      "EMAIL": false
    }
  }
}
```

<details>

<summary>Response fields</summary>

* **categoryPreferences:**
  * Unchanged field, kept for backward compatibility. `true` when the user is permitted on any of the category's own channels. The `channels` filter does not affect this value, so existing integrations read the same result they read before.
* **categoryChannelPreferences:**
  * Per-channel permission state, keyed by category name and then by channel.

</details>

## Profile Attribute Management

### Get Profile Attributes

**Endpoint:** `GET /rest/3.0/getProfileAttributes`\
Retrieves the current profile attributes of a user.

```json
curl -G https://restapi.netmera.com/rest/3.0/getProfileAttributes \
  -H "X-netmera-api-key: your_rest_api_key" \
  --data-urlencode 'extId=id1'  // Required: User ID to fetch attributes for
```

**Sample response**

```json
{
  "extId": "id1",
  "profile": {
    "name": "John",
    "surname": "Brown",
    "dateOfBirth": 624315600000,
    "email": "john.brown@example.com",
    "age": 34,
    "isSubscribed": true
  }
}
```

### Set Profile Attributes

**Endpoint:** `POST /rest/3.0/setProfileAttributes`\
Sets or updates profile attributes for the given users.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/setProfileAttributes \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "extId": "id1",             // Required
      "profile": {"age": 24}      // Required: Key-value object of profile attributes
    },
    {
      "extId": "id2",
      "profile": {"name": "John"}
    }
  ]'
```

### Unset Profile Attributes

**Endpoint:** `POST /rest/3.0/unsetProfileAttributes`\
Removes specified keys from the user's profile.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/unsetProfileAttributes \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "extId": "id1",             // Required
      "profile": ["age"]          // Required: Array of attribute keys to remove
    },
    {
      "extId": "id2",
      "profile": ["age", "name"]
    }
  ]'
```

### Push Values to Array Attributes

**Endpoint:** `POST /rest/3.0/pushProfileAttributes`\
Appends values to array-type profile attributes.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/pushProfileAttributes \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "extId": "id1",  // Required: External user ID
      "profile": {
        "lastVisitedCategories": ["shopping"]  // Required: Attribute must be an array
      }
    }
  ]'
```

### Pull Attributes from Array Attributes

**Endpoint:** `POST /rest/3.0/pullProfileAttributes`\
Removes values from array-type profile attributes.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/pullProfileAttributes \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "extId": "id1",  // Required
      "profile": {
        "lastVisitedCategories": ["promotion"]  // Required: Must match array-type keys
      }
    }
  ]'
```

### Delete Profile Attributes from All Users

**Endpoint:** `POST /rest/3.0/deleteProfileAttributes`\
Deletes specified profile keys from all users.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/deleteProfileAttributes \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "profile": ["name", "age"]  // Required: List of attribute keys to delete for all users
    }
  ]'
```

## Device Management

### Get Devices for a User

**Endpoint:** `GET /rest/3.0/getDevices`\
Returns all devices associated with a user, optionally filtered by push permission.

```json
curl -G https://restapi.netmera.com/rest/3.0/getDevices \
  -H "X-netmera-api-key: your_rest_api_key" \
  --data-urlencode 'extId=id1'  // Required: User ID to fetch devices for
  --data-urlencode 'pushPermitted=true'  // Optional: true = only opt-in devices, false = all devices
```

**Sample response**

```json
{
  "extId": "id1",
  "devices": [
    {
      "platform": "ANDROID",
      "token": "a1b2c3d4e5f6g7",
      "deviceModel": "Pixel 5",
      "pushPermitted": true
    }
  ]
}
```

### Get All Device Tokens

**Endpoint:** `GET /rest/3.0/getDeviceTokens`\
Fetches device tokens in a paginated format.

```json
curl -G https://restapi.netmera.com/rest/3.0/getDeviceTokens \
  -H "X-netmera-api-key: your_rest_api_key" \
  --data-urlencode 'max=10'     // Optional: Default is 10
  --data-urlencode 'offset=0'   // Optional: Default is 0
```

**Sample Response**

```json
{
  "nextPage": "https://restapi.netmera.com/rest/3.0/getDeviceTokens?offset=10&max=10",
  "total": 158329,
  "devices": [
    {
      "platform": "ANDROID",
      "token": "abc123pushToken",
      "deviceModel": "Pixel 6",
      "pushPermitted": true
    }
  ]
}
```

**Pagination**\
To retrieve the next batch of device tokens, use the `nextPage` value directly:

```json
curl -G 'https://restapi.netmera.com/rest/3.0/getDeviceTokens?offset=10&max=10' \
  -H "X-netmera-api-key: your_rest_api_key"
```

## Test Devices

**Endpoint:** `POST /rest/3.0/addTesters`\
Registers the devices of the given users as test devices, so that they can receive test messages sent from the panel. Identify each user by `extId`; all of that user's devices are added. If only `email` is sent, the user is looked up by email address instead.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/addTesters \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "extId": "id_in_my_system_1",       // External user ID (preferred)
      "email": "john.brown@example.com"   // Used to find the user when extId is not sent
    }
  ]'
```

{% hint style="info" %}
Each entry must contain `extId` or `email`. Entries that match no existing user are ignored.
{% endhint %}

## Segments

### Get Segments

**Endpoint:** `GET /rest/3.0/getSegments`\
Lists all segments defined in the panel.

```json
curl -X GET https://restapi.netmera.com/rest/3.0/getSegments \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -H "X-netmera-os: IOS"  // Required: Platform header (IOS or ANDROID)
```

**Sample Response**

```json
{
  "list": [
    {
      "id": 5883,
      "name": "segment",
      "userCount": 16572,
      "type": "PRODUCT_SEGMENT"
    }
  ]
}
```

### Get Segment Users

**Endpoint:** `GET /rest/3.0/getSegmentUsers?id=`\
Fetches users in the specified segment ID.

```json
curl -X GET 'https://restapi.netmera.com/rest/3.0/getSegmentUsers?id=SEGMENT_ID' \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -H "X-netmera-os: IOS"  // Required
```

#### **Sample Response**

```json
{
  "name": "brand",
  "list": [
    {
      "externalId": "user_123",
      "msisdn": "905312345678",
      "email": "user@example.com"
    }
  ]
}
```

## Segment Management

Segments can be created, read, updated and deleted from your backend. A segment created through the REST API appears in the panel like any other segment, with its Targeting Rules filled from the request, and can be targeted by `sendBulkNotification` and `sendBulkEmail` once its calculation is complete.

#### Rules That Apply to Every Segment Request

* **The name must be unique.** A segment with a name that already exists in the application is rejected with error code 5025 (`Segment name must be unique`).
* **A condition is required.** A request without any condition is rejected as an invalid parameter (`Empty criteria`).
* **The segment limit of the package applies.** When the application has reached the segment limit of its package, the request is rejected with error code 5022 (`Service usage quota is exceeded`).
* **Calculation is asynchronous.** A new or updated segment is saved with `segmentStatus` set to `WAITING_CALCULATE` and becomes `READY` when the calculation finishes. Follow the status with `getSegment` and wait for `READY` before using the segment as a target.
* **Some fields are set by the server.** `createdBy` is always `REST`. The status, the user count, the segment type and the business unit are not read from the request. The REST API creates standard (condition-based) segments only.
* **Relative dates use `timeZone`.** Relative dates in behavior conditions, such as "today" or "last 7 days", are evaluated in the `timeZone` of the request (IANA format, for example `Europe/Istanbul`). When it is omitted, the time zone of the application is used.

#### Segment Response

Every segment endpoint returns the same object:

```json
{
  "segmentStatus": "WAITING_CALCULATE",
  "userCount": null,
  "createdBy": "REST",
  "name": "Email contains example",
  "id": 5,
  "status": "ACTIVE"
}
```

<table><thead><tr><th width="202">Field</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>ID of the segment. Use it in <code>getSegment</code>, <code>updateSegment</code> and <code>changeSegmentStatus</code>.</td></tr><tr><td>name</td><td>Name of the segment. Use it as the <code>segment</code> value of a <code>target</code> object.</td></tr><tr><td>status</td><td><code>ACTIVE</code> or <code>DELETED</code>.</td></tr><tr><td>segmentStatus</td><td>Calculation state: <code>WAITING_CALCULATE</code> while the user list is being computed, <code>READY</code> when it can be targeted, <code>WAITING_DELETED</code> after a delete request.</td></tr><tr><td>userCount</td><td>Number of users in the segment after the last completed calculation; <code>null</code> when the segment has never been calculated.</td></tr><tr><td>createdBy</td><td><code>REST</code> for segments created through the API.</td></tr></tbody></table>

### Create Segment

**Endpoint:** `POST /rest/3.0/createSegment`\
Creates a segment from one or more conditions and starts its calculation. The request body has the same shape as the segment definition saved by the panel.

#### Body Parameters

<table><thead><tr><th width="256">Parameter</th><th width="120">Required</th><th>Description</th></tr></thead><tbody><tr><td>segment.segmentName</td><td>Yes</td><td>Name of the segment. Must be unique in the application.</td></tr><tr><td>segment.segmentDescription</td><td>No</td><td>Description shown in the panel.</td></tr><tr><td>segment.tagConditionType, segment.profileConditionType, segment.responseConditionType, segment.behaviorConditionType</td><td>No</td><td><code>AND</code> or <code>OR</code>. How the condition of that type is joined with the conditions before it.</td></tr><tr><td>profile</td><td>No</td><td>A profile attribute condition. <code>expression.code</code> is the code of the attribute, <code>expression.operator</code> the comparison (for example <code>CONTAINS</code>, <code>EQUALS</code>) and <code>expression.values</code> the values to compare with.</td></tr><tr><td>tags</td><td>No</td><td>A tag condition. <code>code</code> is the ID of the tag.</td></tr><tr><td>behaviors, behaviorAggregations</td><td>No</td><td>Behavior (event) conditions.</td></tr><tr><td>response</td><td>No</td><td>A campaign response condition.</td></tr><tr><td>pushEnabled</td><td>No</td><td>A push permission condition.</td></tr><tr><td>targetJson</td><td>No</td><td>A condition tree for segments that combine several conditions in AND/OR groups. A group has <code>type</code> <code>GROUP</code>, a <code>logic</code> that joins its members and a <code>conditions</code> list; each condition carries a <code>type</code> such as <code>CHANNEL</code> or <code>PROFILE</code>.</td></tr><tr><td>timeZone</td><td>No</td><td>IANA time zone used for relative dates in behavior conditions. Defaults to the time zone of the application.</td></tr></tbody></table>

At least one condition must be sent in `profile`, `tags`, `behaviors`, `behaviorAggregations`, `response`, `pushEnabled` or `targetJson`.

Segment from a single profile attribute condition:

```json
curl -X POST https://restapi.netmera.com/rest/3.0/createSegment \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "segment": {
      "segmentName": "Email contains example"   // Required: unique segment name
    },
    "profile": {
      "type": "PROFILE",
      "expression": {
        "code": "email",                        // Profile attribute code
        "operator": "CONTAINS",
        "values": ["example"]
      }
    }
  }'
```

Segment from a group of conditions (email permission and a profile attribute):

```json
curl -X POST https://restapi.netmera.com/rest/3.0/createSegment \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "segment": {
      "segmentName": "Email opt-in and email contains example"
    },
    "targetJson": {
      "id": "group_1",
      "type": "GROUP",
      "logic": "AND",
      "conditions": [
        {
          "type": "CHANNEL",
          "operator": "EQUALS",
          "value": "EMAIL",
          "logic": "AND"
        },
        {
          "type": "PROFILE",
          "operator": "EQUALS",
          "value": "email",
          "expression": {
            "code": "email",
            "operator": "CONTAINS",
            "values": ["example"]
          },
          "logic": "AND"
        }
      ]
    },
    "timeZone": "Europe/Istanbul"
  }'
```

**Sample response**

```json
{
  "segmentStatus": "WAITING_CALCULATE",
  "userCount": null,
  "createdBy": "REST",
  "name": "Email contains example",
  "id": 5,
  "status": "ACTIVE"
}
```

{% hint style="info" %}
The segment is created in the `WAITING_CALCULATE` state and `userCount` is `null` until the first calculation completes. A send request that targets a segment which has never been calculated is rejected; read the segment with `getSegment` and wait for `segmentStatus` `READY`.
{% endhint %}

### Get Segment

**Endpoint:** `GET /rest/3.0/getSegment?id=`\
Returns a single segment with its calculation state and user count. Use it to follow the calculation after `createSegment` or `updateSegment`.

```json
curl -X GET 'https://restapi.netmera.com/rest/3.0/getSegment?id=5' \
  -H "X-netmera-api-key: your_rest_api_key"
```

**Sample response**

```json
{
  "segmentStatus": "READY",
  "userCount": 20,
  "createdBy": "REST",
  "name": "Email contains example",
  "id": 5,
  "status": "ACTIVE"
}
```

A request with an `id` that matches no segment is rejected with error code 5042 (`Segment not found`).

### Update Segment

**Endpoint:** `POST /rest/3.0/updateSegment`\
Replaces the conditions of an existing segment and calculates it again. The request body is the same as `createSegment`, with the ID of the segment in `segment.segmentId`. The update response returns `createdBy` as `null`; the segment keeps its original value.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/updateSegment \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "segment": {
      "segmentId": 5,                           // Required: ID of the segment
      "segmentName": "Email contains example"   // Required
    },
    "profile": {
      "type": "PROFILE",
      "expression": {
        "code": "email",
        "operator": "CONTAINS",
        "values": ["example.com"]
      }
    }
  }'
```

**Sample response**

```json
{
  "segmentStatus": "WAITING_CALCULATE",
  "userCount": null,
  "createdBy": null,
  "name": "Email contains example",
  "id": 5,
  "status": "ACTIVE"
}
```

The Targeting Rules of the segment in the panel are updated to match the request. Read the segment again with `getSegment` to see the result of the new calculation. While the segment is being recalculated, a send request that targets it uses the user list of the previous calculation.

### Change Segment Status

**Endpoint:** `POST /rest/3.0/changeSegmentStatus?id=&status=`\
Deletes a segment or reopens a deleted one. The request has no body.

#### Query Parameters

<table><thead><tr><th width="202">Parameter</th><th width="120">Required</th><th>Description</th></tr></thead><tbody><tr><td>id</td><td>Yes</td><td>ID of the segment.</td></tr><tr><td>status</td><td>Yes</td><td><code>DELETED</code> to delete the segment, <code>ACTIVE</code> to reopen a deleted segment. No other value is accepted.</td></tr></tbody></table>

Delete a segment:

```json
curl -X POST 'https://restapi.netmera.com/rest/3.0/changeSegmentStatus?id=5&status=DELETED' \
  -H "X-netmera-api-key: your_rest_api_key"
```

**Sample response**

```json
{
  "segmentStatus": "WAITING_DELETED",
  "userCount": 20,
  "createdBy": "REST",
  "name": "Email contains example",
  "id": 5,
  "status": "DELETED"
}
```

Reopen a deleted segment:

```json
curl -X POST 'https://restapi.netmera.com/rest/3.0/changeSegmentStatus?id=5&status=ACTIVE' \
  -H "X-netmera-api-key: your_rest_api_key"
```

A reopened segment counts against the segment limit of the package again and is calculated again, so it returns to `WAITING_CALCULATE` until the calculation completes.

{% hint style="warning" %}
A segment that is used by an active campaign cannot be deleted. A deleted segment cannot be targeted: `sendBulkNotification` and `sendBulkEmail` requests that name it are rejected with `400`. Reopen the segment, or update the integration to target another segment, before sending.
{% endhint %}

### Segment Checks Before a Send

`sendBulkNotification` and `sendBulkEmail` check every segment named in `target.segment` before the campaign is created. The request is accepted only when each segment can be targeted:

<table><thead><tr><th width="400">Segment state</th><th>Result</th></tr></thead><tbody><tr><td><code>ACTIVE</code> and <code>segmentStatus</code> <code>READY</code></td><td>Accepted.</td></tr><tr><td><code>ACTIVE</code>, <code>WAITING_CALCULATE</code>, calculated before (<code>userCount</code> is set)</td><td>Accepted. The user list of the previous calculation is used.</td></tr><tr><td><code>ACTIVE</code>, <code>WAITING_CALCULATE</code>, never calculated (<code>userCount</code> is <code>null</code>)</td><td>Rejected with <code>400</code>: <code>Segment '&#x3C;name>' is still being calculated; retry when its segmentStatus is READY</code></td></tr><tr><td><code>DELETED</code>, <code>WAITING_DELETED</code>, or no segment with that name</td><td>Rejected with <code>400</code>: <code>Segment '&#x3C;name>' is not active</code></td></tr></tbody></table>

When `target.segment` is a list or an operator object, the request is rejected if any of the segments fails the check. No campaign is created for a rejected request.

## User Count

**Endpoint:** `GET /rest/3.0/count-user`\
Returns the number of users in your application that currently have push permission. No parameters are required.

```json
curl -X GET https://restapi.netmera.com/rest/3.0/count-user \
  -H "X-netmera-api-key: your_rest_api_key"
```

**Sample response**

```json
{
  "count": 184523
}
```

## Promotion Codes

**Endpoint:** `POST /rest/3.0/promotion/add`\
Assigns promotion codes to users. All codes in one request belong to the same promotion, identified by `name`; the promotion is created automatically the first time the name is used. Assigned codes can then be placed into messages sent to that user.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/promotion/add \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "SummerSale",          // Required: Promotion name
    "expiryDate": 1767225600000,   // Optional: Expiry date of the promotion, epoch milliseconds
    "codes": [                     // Required: One entry per user
      {
        "extId": "id1",            // Required: External user ID
        "code": "SUMMER-1A2B"      // Required: Promotion code assigned to this user
      },
      {
        "extId": "id2",
        "code": "SUMMER-3C4D"
      }
    ]
  }'
```

#### Body Parameters

<table><thead><tr><th width="202">Parameter</th><th width="120">Required</th><th>Description</th></tr></thead><tbody><tr><td>name</td><td>Yes</td><td>Name of the promotion. Codes sent with the same name are grouped under the same promotion.</td></tr><tr><td>expiryDate</td><td>No</td><td>Expiry date of the promotion as an epoch timestamp in milliseconds. Used when the promotion is first created.</td></tr><tr><td>codes</td><td>Yes</td><td>Array of <code>{ "extId", "code" }</code> objects, one per user.</td></tr></tbody></table>

## Email Management

### Enable Email Permission

**Endpoint:** `POST /rest/3.0/enableMail`

```json
curl -X POST https://restapi.netmera.com/rest/3.0/enableMail \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "extIds": ["your_extId"]  // Required: List of external user IDs
    // "emailList": ["your-email"]  // Optional alternative to extIds
  }'
```

### Disable Email Permission

**Endpoint:** `POST /rest/3.0/disableMail`

```json
curl -X POST https://restapi.netmera.com/rest/3.0/disableMail \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "extIds": ["your_extId"]  // Required or use emailList
    // "emailList": ["your-email"]
  }'
```

### Update Email Address

**Endpoint:** `POST /rest/3.0/updateEmail`

```json
curl -X POST https://restapi.netmera.com/rest/3.0/updateEmail \
  -H "X-netmera-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "extId": "your_extId",  // Required
    "email": "your_email"    // Required
  }'
```

### Remove Email Address

**Endpoint:** `POST /rest/3.0/removeEmail`\
Removes the email address of a user. Only the email field is cleared: the user, its external ID, devices, profile attributes and email permission state are kept. A new address can be set later with `updateEmail`.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/removeEmail \
  -H "X-netmera-api-key: your_rest_api_key" \
  -H "Content-Type: application/json" \
  -d '{
    "extId": "your_extId"  // Required: External user ID of the user
  }'
```

A successful request returns `200` with an empty body. When `extId` is missing or `null`, the request is rejected with `400` and the plain-text body `Please provide an external id.`; no user is changed.

{% hint style="info" %}
The request is processed asynchronously: `200` means the request has been queued, and the change is applied to the user within seconds. A request whose `extId` matches no user in your application, or that targets a user whose email is already empty, also returns `200` and changes nothing.
{% endhint %}

{% hint style="warning" %}
Sending an empty value to `updateEmail` does not remove the email address: the request returns `200` but the stored address is kept. `unsetProfileAttributes` does not remove it either, since email is a system field rather than a profile attribute. Use `removeEmail` to clear the address.
{% endhint %}

## SMS Management

### Enable SMS Permission

**Endpoint:** `POST /rest/3.0/enableSms`

```json
curl -X POST https://restapi.netmera.com/rest/3.0/enableSms \
  -H "X-netmera-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "extIds": ["your-extId"]  // Required or use msisdnList
    // "msisdnList": ["your-msisdn"]
  }'
```

### Disable SMS Permission

**Endpoint:** `POST /rest/3.0/disableSms`

```json
curl -X POST https://restapi.netmera.com/rest/3.0/disableSms \
  -H "X-netmera-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "extIds": ["your-extId"]  // Required or use msisdnList
    // "msisdnList": ["your-msisdn"]
  }'
```

### Update MSISDN

**Endpoint:** `POST /rest/3.0/updateMsisdn`

```json
curl -X POST https://restapi.netmera.com/rest/3.0/updateMsisdn \
  -H "X-netmera-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "extId": "your_extId",   // Required
    "msisdn": "your_msisdn"  // Required
  }'
```

### Remove MSISDN

**Endpoint:** `POST /rest/3.0/removeMsisdn`\
Removes the MSISDN of a user. Only the MSISDN field is cleared: the user, its external ID, devices, profile attributes and SMS permission state are kept. A new number can be set later with `updateMsisdn`.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/removeMsisdn \
  -H "X-netmera-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "extId": "your_extId"  // Required: External user ID of the user
  }'
```

Responses and processing are the same as `removeEmail`: `200` with an empty body when queued, `400` with `Please provide an external id.` when `extId` is missing.

{% hint style="warning" %}
Sending an empty value to `updateMsisdn` does not remove the number; an empty value is stored instead. Use `removeMsisdn` to clear the field.
{% endhint %}

## WhatsApp Management

Netmera provides REST endpoints to enable, disable, and update WhatsApp permissions for users based on `extId` or `msisdn`.

### Enable WhatsApp Permission

Enables WhatsApp permission for one or more users. You can pass **either** `extIds` or `msisdnList`.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/enableWhatsApp \
  -H "X-netmera-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "extIds": ["your-extId"],
    "wpNumbers": ["+1234567890", "+1234567891"]
  }'
```

### Disable WhatsApp Permission

Disables WhatsApp permission for one or more users. You can pass **either** `extIds` or `msisdnList`.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/disableWhatsApp \
  -H "X-netmera-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "extIds": ["your-extId"],
    "wpNumbers": ["+1234567890"]
  }'
```

### Update WhatsApp Number

Updates the WhatsApp number of a WhatsApp user. Both `extId` and `msisdn` fields are required.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/updateWhatsApp \
  -H "X-netmera-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "extId": "your_extId",
    "wpNumbers": ["+1234567890", "+1234567891"]
  }'
```

### Remove WhatsApp Number

**Endpoint:** `POST /rest/3.0/removeWhatsApp`\
Removes the WhatsApp number of a user. Only the WhatsApp number field is cleared: the user, its external ID, devices, profile attributes and WhatsApp permission state are kept. A new number can be set later with `updateWhatsApp`.

```json
curl -X POST https://restapi.netmera.com/rest/3.0/removeWhatsApp \
  -H "X-netmera-api-key: your-api-key" \
  -H "Content-Type: application/json" \
  -d '{
    "extId": "your_extId"  // Required: External user ID of the user
  }'
```

Responses and processing are the same as `removeEmail`: `200` with an empty body when queued, `400` with `Please provide an external id.` when `extId` is missing.

{% hint style="warning" %}
Sending an empty value to `updateWhatsApp` does not remove the number; an empty value is stored instead. Use `removeWhatsApp` to clear the field.
{% endhint %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://user.netmera.com/netmera-developer-guide/api-documentation/rest-api/user-and-device-management.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
