# Create a private integrator for the current tenant Provision a tenant-private integration plus its first API…

`POST /api/configuration/operator/integrators/private`

- **Success:** `201 Created`
- **Products:** Operator (`FOH`)
- **Features:** Front of House Generic Access (`FOHGENERIC`)

HTML version: [Create a private integrator for the current tenant Provision a tenant-private integration plus its first API…](https://support.gladstonesoftware.cloud/reference/configuration/create-a-private-integrator-for-the-current-tenant-provision-a-tenant-private-in)

## Overview

Creates a tenant specific private integrator and its first API key in one call. Unlike a global integrator, this integrator belongs to the calling tenant's environment: no other tenant can see it, and it never appears in the Gladstone-curated global catalogue.

**The full plaintext API key is in the `apiKey` field of this response and is returned exactly once.** Gladstone keeps only a hash of it plus an 8-character prefix, so no endpoint and no support process can recover it. Write it to your secret store before you do anything else with the response. If you lose it, your only route back is to renew the key, which invalidates the old one.

The tenant is taken from your token, not from the request body. If the environment cannot be resolved from your token the call fails with `400` and nothing is created.

**This call is not idempotent, and names are not unique.** A retry after a timeout creates a second integrator with a second live API key — both of which work. Before retrying, list your private integrators and check whether the first attempt succeeded.

The integrator is created with `isGlobal` false, `isActive` true and `environmentId` set to your environment; none of those are settable.

## Endpoint

`POST {base_url}/api/configuration/operator/integrators/private`

`{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 |
|-------|------|----------|-------------|
| `name` | string | Yes | The display name of the integrator, for your own identification within the tenant. Maximum 255 characters, and it may not be empty or whitespace-only. Names are **not** unique — nothing stops you creating two integrators called the same thing, so track them by `id`. |
| `description` | string | No | Free-text note about what this integration is for. No length limit. Omit it, or send null, to leave it blank. |
| `origin` | string | Yes | The exact web origin your integration calls from, for example `https://app.example.com`. Maximum 255 characters, may not be empty. Enforced at token-issue time: a token request that sends an `Origin` header must match this value or it is rejected with `401`. The comparison ignores case and a trailing slash and is otherwise exact — no wildcards, and only one origin per integrator. A token request with no `Origin` header skips the check, so this protects browser callers rather than server-to-server ones. |
| `logoUrl` | string | No | Absolute URL of a logo for the integration. Maximum 500 characters. Omit it, or send null, to have none. Stored as given; never fetched or validated. |
| `isBillable` | boolean | No | Whether usage under this integrator is chargeable. Becomes a `billable` claim on every token issued from its key. Defaults to `true` for private integrators when omitted — the opposite of the global default. |
| `allowedScopes` | string | No | The features this integrator may reach, as a **JSON array encoded into a string** — for example `"[\"FOHGENERIC\"]"`. Use the feature codes listed in the `features` line of each endpoint page. At token-issue time each entry in the array becomes both a features claim and a products claim; API routes require both, so an integrator with no scopes receives a token that every route rejects with `403`. If the string is not valid JSON, nothing reports it and the resulting token carries no scope claims at all — you get no error here and no error at token issue, only `403`s afterwards. Omitting the field has the same effect. |
| `canRead` | boolean | No | Whether this integrator may perform read operations. Becomes a `can_read` claim on every token issued from its key. Defaults to `false` when omitted. Send `true` explicitly. |
| `canWrite` | boolean | No | Whether this integrator may perform write operations. Becomes a `can_write` claim on every token issued from its key. Defaults to `false` when omitted, as with `canRead`. |
| `canDelete` | boolean | No | Whether this integrator may perform delete operations. Becomes a `can_delete` claim on every token issued from its key. Defaults to `false` when omitted, as with `canRead`. |
| `rateLimitPerMinute` | integer (int32) | No | The per-minute request budget recorded against the integrator. Must be `0` or greater; a negative value is rejected with `400`. Defaults to `0` when omitted. |
| `rateLimitPerHour` | integer (int32) | No | The per-hour request budget recorded against the integrator. Must be `0` or greater; a negative value is rejected with `400`. Defaults to `0` when omitted. |

## Sample request

```bash title="cURL"
curl -X POST '{base_url}/api/configuration/operator/integrators/private' \
  -H 'Authorization: Bearer {accessToken}' \
  -H 'X-Use-SSO: true' \
  -H 'Content-Type: application/json' \
  -d '{
  "name": "string",
  "description": "string",
  "origin": "string",
  "logoUrl": "string",
  "isBillable": true,
  "allowedScopes": "string",
  "canRead": true,
  "canWrite": true,
  "canDelete": true,
  "rateLimitPerMinute": 1,
  "rateLimitPerHour": 1
}'
```

## Response fields

| Field | Type | Description |
|-------|------|-------------|
| `apiKey` | string | The full plaintext API key, in the form `gsk_sV_` followed by 32 random characters. **Returned exactly once, here.** Only a hash and the first 8 characters are kept, so this value cannot be recovered by any means — persist it to your secret store immediately. A fresh key can only be obtained by renewing, which revokes this one. |
| `apiKeyPrefix` | string | The first 8 characters of `apiKey`, safe to display and log. This is the only part of the key that later reads return, so use it to identify which key a system is holding without exposing the secret. |
| `id` | integer (int32) | The integer identifier the system allocated for the new integrator. You cannot choose it. Use it to update, list, renew the key for, or delete this integrator. |
| `name` | string | The integrator's display name as stored — echoes what you sent. |
| `description` | string | The description as stored, or null if you did not send one. |
| `origin` | string | The allowed origin as stored. Token requests carrying a mismatched `Origin` header are rejected with `401`. |
| `logoUrl` | string | The logo URL as stored, or null if you did not send one. |
| `isGlobal` | boolean | Always `false` here — the integrator is private to your tenant and invisible to others and to the global catalogue. |
| `isBillable` | boolean | Whether usage under this integrator is chargeable — echoes what you sent, or `true` if you omitted it. Carried as a `billable` claim on every token issued from its key. |
| `environmentId` | integer (int32) | The identifier of your tenant's environment, taken from your token rather than from the request. It is what binds the integrator to your tenant, and what allows tokens minted from its key to carry tenant claims and reach tenant-scoped endpoints. |
| `allowedScopes` | string | The scope string as stored. Check it is a valid JSON array before you rely on the key: an unparseable or empty value produces tokens with no features or products claims, which every API route rejects with `403`. |
| `canRead` | boolean | Whether the integrator may read — echoes what you sent, or `false` if omitted. Carried as a `can_read` claim on tokens issued from its key. |
| `canWrite` | boolean | Whether the integrator may write — echoes what you sent, or `false` if omitted. Carried as a `can_write` claim. |
| `canDelete` | boolean | Whether the integrator may delete — echoes what you sent, or `false` if omitted. Carried as a `can_delete` claim. |
| `rateLimitPerMinute` | integer (int32) | The per-minute budget as stored — echoes what you sent, or `0` if omitted. |
| `rateLimitPerHour` | integer (int32) | The per-hour budget as stored — echoes what you sent, or `0` if omitted. |
| `isActive` | boolean | Always `true` on creation. It becomes `false` when the integrator is deleted — a deleted integrator is kept but marked inactive, and its active key is revoked. |
| `createdOn` | string (date-time) | UTC timestamp the integrator was created, set for you. |
| `modifiedOn` | string (date-time) | UTC timestamp of the last update. Null on a freshly created integrator. |

## Sample response

```json title="201 Created"
{
  "apiKey": "string",
  "apiKeyPrefix": "string",
  "id": 12345,
  "name": "string",
  "description": "string",
  "origin": "string",
  "logoUrl": "string",
  "isGlobal": true,
  "isBillable": true,
  "environmentId": 12345,
  "allowedScopes": "string",
  "canRead": true,
  "canWrite": true,
  "canDelete": true,
  "rateLimitPerMinute": 1,
  "rateLimitPerHour": 1,
  "isActive": true,
  "createdOn": "2026-08-14T19:00:00Z",
  "modifiedOn": "2026-08-14T19:00:00Z"
}
```

## Errors

| Status | Meaning | When |
|--------|---------|------|
| `400` | Bad request | `name` or `origin` is missing, empty or whitespace-only, or a rate limit is negative. The body is a validation problem document (`application/problem+json`) with an `errors` map naming the offending field. |
| `400` | Bad request | The tenant environment could not be resolved from your token — the body is the plain string `Could not resolve tenant environment.` Nothing is created. |
| `500` | Internal server error | An unexpected server-side failure. The body is empty. Because the integrator and its key are created in two separate steps, treat a `500` as "state unknown" and list your private integrators before retrying. |
