---
title: "Create an app"
method: POST
path: "/clients/{id_client}/apps"
tags: ["apps"]
---

# Create an app

`POST /clients/{id_client}/apps`

## Request

- Base URL: `https://api.planvortex.com/v1.0.0`
- URL: `https://api.planvortex.com/v1.0.0/clients/{id_client}/apps`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (client credentials, token URL `https://api.planvortex.com/v1.0.0/oauth/token`)

Creates the app and its credentials.

**How many apps fit is decided by the plan**, not by a fixed limit of one: 1 on free, 2 on basic, 5 on pro and 10 on custom. Going over answers `ERROR_CODE_1308`, which carries `limit` and `used` in its `data`. Deleting an app frees its slot immediately, because only live apps count. If you just need to rotate credentials, update the existing app rather than creating another.

**The user's email must be verified** (`ERROR_CODE_546`). This is the only route in `/apps` that asks for it: an app is a key to the whole public API, and this is what stops a throwaway address from minting them in bulk. Reading, updating and deleting an app you already own do not check it.

**The identifier has a format** (error 533): lowercase letters, numbers, `.`, `-` and `_`, starting with a letter or a number, 3 to 64 characters. Pick it carefully — it cannot be changed later (error 547).

Every URL is validated: a bad entry in `allowed_domains` answers 531, in `redirect_urls` 532, and a bad `webhook_url` answers 535.

**The secret is not in the response.** Read it with `GET /clients/{id_client}/apps/{id_app}/secret`.

**Needs a USER token, not an app token.** With client credentials it answers `ERROR_CODE_512`. **No plan is required**: apps are on all four plans, the free one included — what the plan decides is how many you can hold at once and how fast you can call.

## Path parameters

- `id_client` string, required

## Request body

- AppsClientAppInput
  - `name` string, required: A name for the app. Cannot be blank (error 525).
  - `keycloak_client_idenfifier` string, required: The app's `client_id`, which is what you send to `POST /oauth/token`. **Format is enforced** (error 533): lowercase letters, numbers, `.`, `-` and `_`, starting with a letter or a number, between 3 and 64 characters — `shop-integration`, not `Shop Integration`. Keycloak itself accepts anything, so an identifier with a space in it used to create an app that could never get a token. It also has to be unique across PlanVortex (error 534), and **it cannot be changed once the app exists** (error 547): it is what your integration authenticates with, so renaming it would lock out everything already using the old one. Send it unchanged on an update, or leave it out. The spelling of the field is historical and kept for compatibility.
  - `allowed_domains` string[]: Origins allowed to call the API with this app's identity. Every entry has to be a valid URL (error 531).
  - `redirect_urls` string[]: URLs the connect flow is allowed to come back to. A `redirect_uri` that is not in this list is refused when issuing a temporal connect token (error 532).
  - `webhook_url` string: Where PlanVortex posts events. Has to be a valid URL (error 535). See `ClientApp.webhook_url` for what arrives and how it is signed.

## Response `200`

The created app

- object
  - `client_app` AppsClientApp, required: An app: the credentials a third-party integration authenticates with. **The secret is not here.** It lives in Keycloak and is read with `GET /clients/{id_client}/apps/{id_app}/secret`, which needs a user token.
    - `_id` string, required
    - `id_client` string, required
    - `id_user` string: The user who created the app, when it was created from the panel.
    - `name` string, required
    - `keycloak_client_idenfifier` string, required: The app's `client_id`. Fixed for the life of the app: it cannot be changed once created (error 547).
    - `allowed_domains` string[], required
    - `redirect_urls` string[], required
    - `webhook_url` string: Where PlanVortex posts events, when one is configured. **The body is an array of changes, not a single object**, and it carries two signature headers computed with this app's secret over the **raw** body: `x-hub-signature` (`sha1=<hex>`) and `x-hub-signature-256` (`sha256=<hex>`). Verify against the bytes you received — parsing the JSON and re-serialising it changes them and the signature will not match. The events delivered today are `new_account`, `change_state_account`, `messages`, `messaging_postbacks`, `messaging_seen`, `messaging_error`, `comments`, `integration_error`, `ai_plan_generated` and `ai_plan_failed`. The payload is documented in the `comments` specification. **Every** app of the client that has a `webhook_url` receives every event, not only the app whose call caused it: with two apps, each event arrives twice. Delivery is best effort: PlanVortex does not retry a webhook that fails.
    - `creation_date` string, date-time, required
    - `deleted` boolean, required: Deleting an app marks it instead of removing it — the Keycloak client is gone, so it can no longer get a token — and the listing filters those out.
    - `deleted_date` string, date-time

## Other responses

- `400`: Error. Classify by `code` in the body, never by the HTTP status.

---

[API](https://skmtc.dev/planvortex/apis/planvortex-api.md) · [All operations](https://skmtc.dev/planvortex/apis/planvortex-api/llms.txt) · [OpenAPI document](https://skmtc.dev/planvortex/apis/planvortex-api/revisions/abedbbc1cd52?raw)
