# Creates a new Multitenant Mobile App Identifier with an initial version.

`POST /api/configuration/multitenant-mobile-app-identifier`

- **Success:** `201 Created`
- **Products:** Gladstone Admin Areas (`ADMIN`)
- **Features:** not restricted by feature

HTML version: [Creates a new Multitenant Mobile App Identifier with an initial version.](https://support.gladstonesoftware.cloud/reference/configuration/creates-a-new-multitenant-mobile-app-identifier-with-an-initial-version)

## Overview

Registers a multi-tenant mobile app — one app binary in the App Store or Play Store that many Gladstone operators share — and creates its first version in the same call. Each version is issued a secret that the app uses to sign requests, so this call is how an app build gets the credential it needs before it can look up which operator's environment a user belongs to.

Two records are created together, all-or-nothing: the **identifier**, keyed on the app's bundle id, and one **version** under it holding the version string and a generated `appSecret`. If either cannot be saved, neither is kept and you get a `500` — there is no partially created identifier to clean up.

You do not choose either identifier. The response is `201 Created` carrying the identifier `id`, the values you sent, and a one-element `versions` array containing the new version's `id` and its `appSecret`. There is no usable `Location` header; read the ids from the body.

`appBundleId` is unique across the whole system, compared case-insensitively. A second call with the same bundle id — in any casing — returns `409 Conflict` rather than creating a duplicate or overwriting the first, so a blind retry after a timeout is safe but tells you nothing about whether the original succeeded. Confirm with [Retrieve all multitenant mobile app identifiers](retrieves-all-multitenant-mobile-app-identifiers.md), which also returns the secrets, so a secret lost in a dropped response can be recovered rather than rotated.

Note what this call does **not** do. It creates the app registration only; it does not connect it to any operator environment — for that, use [Update an environment's multitenant mobile app identifier versions](updates-an-environments-multitenant-mobile-app-identifier-versions.md). Adding a second version to an existing identifier is a different endpoint (`POST /api/configuration/multitenant-mobile-app-identifier/{id}/version`) that accepts only the `api` scope, so partner integrations can create identifiers and read them back but cannot add versions to, update or delete them afterwards.

## Endpoint

`POST {base_url}/api/configuration/multitenant-mobile-app-identifier`

`{base_url}` is the environment URL issued to your integration. See [Authenticating with Gladstone APIs](../../guides/authenticating-with-gladstone-apis.md).

## Request body

| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `appBundleId` | string | Yes | The mobile app's platform bundle identifier — the iOS bundle id or Android application id, such as `com.example.leisure`. This is the natural key: it is what a running app sends when it asks which environment a PIN or company name belongs to, and how the platform finds the secrets to check the signature. Maximum 255 characters, must not be blank (`AppBundleId is required.`) and must contain no whitespace anywhere (`AppBundleId cannot contain whitespace.`). Unique across all identifiers, compared case-insensitively, so `com.example.app` clashes with `COM.EXAMPLE.APP`. The casing you send is the casing stored. |
| `oneSignalAppId` | string | Yes | The app's OneSignal application id, used to address push notifications to installs of this app. Required — there is no way to register an identifier without one — and maximum 50 characters. Stored and returned as sent; not validated against OneSignal, so a typo surfaces only when notifications fail to arrive. |
| `appVersion` | string | Yes | The version string of the first app build being registered, in dotted numeric form: `major.minor`, optionally with `.build` and `.revision`, for example `1.0` or `2.4.1.3`. Numeric parts only — anything else, including `v1.0`, `1.0.0-beta` and a bare `1`, fails with `AppVersion must be in a valid format (major.minor[.build[.revision]]). Provided: {value}`. Maximum 50 characters, must not be blank and must contain no whitespace. This matters beyond bookkeeping: when an app signs a request it sends its own version, and the platform accepts signatures made with the secret of any registered version **less than or equal to** that version — so older builds keep working while newer ones get their own secret. Register each shipped build as its own version. |

## Sample request

```bash title="cURL"
curl -X POST '{base_url}/api/configuration/multitenant-mobile-app-identifier' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '{
  "appBundleId": "com.example.leisure",
  "oneSignalAppId": "b2f7f966-d8cc-11e4-bed1-df8f05be55ba",
  "appVersion": "1.0"
}'
```

## Response fields

| Field | Type | Description |
|-------|------|-------------|
| `id` | integer (int32) | The identifier's system-generated id. Use it in the per-identifier routes — adding a version, updating and deleting all address the identifier as `{id}`, not by bundle id. |
| `appBundleId` | string | The bundle id as stored, echoed back unchanged from the request. |
| `oneSignalAppId` | string | The OneSignal application id as stored, echoed back unchanged from the request. |
| `versions` | array of object | The identifier's versions. On this call it always contains exactly one entry — the initial version built from `appVersion`. Later calls to the add-version endpoint extend it, and [Retrieve all multitenant mobile app identifiers](retrieves-all-multitenant-mobile-app-identifiers.md) returns the full set when asked with `includeVersions=true`. |
| `versions[].id` | integer (int32) | The version's system-generated id, separate from the identifier's `id`. It is the `{id}` used by the version update and delete routes. |
| `versions[].appVersion` | string | The version string as stored, echoed back unchanged from the request. |
| `versions[].appSecret` | string | The HMAC signing secret generated for this version — 256 cryptographically random characters drawn from `A–Z`, `a–z`, `0–9`, `-` and `_`. You cannot supply or choose it, and there is no rotate operation: to change it, delete the version and create it again. Embed it in the matching app build; the app signs the raw JSON body of an environment-search request with HMAC-SHA256 and sends the result Base64-encoded in the `X-HMAC-Signature` header. Treat it as a credential — it is returned in full by the identifier GET endpoints, so anyone who can call those can sign as the app. |

## Sample response

```json title="201 Created"
{
  "id": 12,
  "appBundleId": "com.example.leisure",
  "oneSignalAppId": "b2f7f966-d8cc-11e4-bed1-df8f05be55ba",
  "versions": [
    {
      "id": 34,
      "appVersion": "1.0",
      "appSecret": "qX3n_Tk9…"
    }
  ]
}
```

## Errors

Checks run in a fixed order: request validation, then the whitespace checks, then the duplicate bundle id check, and only then the version format check. A request that is both a duplicate and badly versioned therefore returns `409`, not `400`.

| Status | Meaning | When |
|--------|---------|------|
| `400` | Required field missing | `AppBundleId is required.`, `OneSignalAppId is required.` or `AppVersion is required.`. A missing or null field is caught by request validation and returned as a validation problem document (`application/problem+json`) with an `errors` map keyed by field name, before any other check. |
| `400` | Field too long | `AppBundleId cannot exceed 255 characters.`, `OneSignalAppId cannot exceed 50 characters.` or `AppVersion cannot exceed 50 characters.`. Also returned as a validation problem document. |
| `400` | Whitespace in `appBundleId` | `AppBundleId cannot contain whitespace.` — any whitespace character anywhere in the value, not just leading or trailing. The value is not trimmed for you. |
| `400` | Whitespace in `appVersion` | `AppVersion cannot contain whitespace.` — same rule as above. |
| `400` | Version string not parseable | `AppVersion must be in a valid format (major.minor[.build[.revision]]). Provided: {value}` — the value is not a dotted numeric version. A bare major number such as `1` fails this check. |
| `409` | Bundle id already registered | `An identifier already exists with AppBundleId '{value}'.` — matched case-insensitively against every existing identifier. Fetch the existing record instead of retrying. |
| `403` | Forbidden | The access token does not satisfy this endpoint's requirements — the `ADMIN` product and the `cr.co` permission — or its scope is neither `api` nor `partner`. |
| `500` | Internal server error | The identifier and its first version could not be saved, so nothing was created; or another unexpected server-side failure. The response body is empty. |
