For AI agents: markdown of this page — /docs-content-en/apps/create.md documentation index — /llms.txt

Create an application

POST /v1/apps

Registers an OAuth application in your Bitrix24 account and creates a paired API key. Send a flat request body without a wrapper.

Request fields (body)

Field Type Required Description
Fieldtitle Typestring Requiredyes DescriptionApplication name, from 1 to 255 characters, not whitespace only
Fieldscopes Typearray Requiredyes DescriptionSet of application scopes — access to Bitrix24 account data, at least one. To publish the application later, the set must include placement. The platform vibe:* scopes are listed only when the request comes from a key with a fixed rights set. In every other case the paired key receives them automatically — see "Known specifics". The list — Scopes
Fielddescription Typestring Requiredno DescriptionApplication description, up to 2000 characters
FieldappUrl Typestring Requiredno DescriptionApplication URL: an address on the platform or a public https:// address outside it (no localhost, private network or credentials). An empty string is saved as null
FieldredirectUris Typearray Requiredno DescriptionOAuth redirect URLs. Defaults to the authorization completion URL and http://localhost
Fieldmode Typestring Requiredno DescriptionAccess mode of the paired key: READONLY or READWRITE. Defaults to the narrowest of the account policy mode, the calling key mode and the Cowork/Code seat mode, if the calling key belongs to a seat, as a project deploy key does. A read-only key that omits this field therefore gets an application in its own mode. An explicitly requested mode with broader permissions than the seat mode is not rejected: the paired key is issued in the seat mode. The create response does not name the mode: read the actual mode from data.accessMode of GET /v1/me called with the paired key
Fieldmobile Typeboolean Requiredno DescriptionThe application supports the Bitrix24 mobile client — the account receives this flag at registration. Defaults to false. Can only be set at creation, see "Known specifics"

handlerUrl is set by the platform — it is not accepted in the body. OAuth callbacks and opening a placement go through it. For the difference between appUrl and handlerUrl, see the Applications section.

Examples

The JavaScript examples run in Node.js on Linux/macOS. They save the one-time key to an app.key file in a separate private directory inside the home directory and print only the file path and application id. Move the key to your secret store.

curl — personal key

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/apps \
  -H "X-Api-Key: YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Sales dashboard",
    "scopes": ["crm", "user", "placement"],
    "appUrl": "https://app-abc12345.vibecode.bitrix24.com"
  }'

curl — OAuth application

Terminal
curl -X POST https://vibecode.bitrix24.com/v1/apps \
  -H "X-Api-Key: YOUR_APP_KEY" \
  -H "Authorization: Bearer USER_SESSION_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Sales dashboard",
    "scopes": ["crm", "user", "placement"],
    "appUrl": "https://app-abc12345.vibecode.bitrix24.com"
  }'

JavaScript — personal key

javascript
import { mkdtemp, writeFile } from 'node:fs/promises'
import { homedir } from 'node:os'
import { join } from 'node:path'

const keyDir = await mkdtemp(join(homedir(), '.vibecode-app-'))
const res = await fetch('https://vibecode.bitrix24.com/v1/apps', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_API_KEY',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    title: 'Sales dashboard',
    scopes: ['crm', 'user', 'placement'],
    appUrl: 'https://app-abc12345.vibecode.bitrix24.com',
  }),
})

const { data } = await res.json()
// The key is returned only once; only the owner can read the file
const keyPath = join(keyDir, 'app.key')
await writeFile(keyPath, data.rawKey, { mode: 0o600, flag: 'wx' })
console.log('App ID:', data.id)
console.log('Key file:', keyPath)

JavaScript — OAuth application

javascript
import { mkdtemp, writeFile } from 'node:fs/promises'
import { homedir } from 'node:os'
import { join } from 'node:path'

const keyDir = await mkdtemp(join(homedir(), '.vibecode-app-'))
const res = await fetch('https://vibecode.bitrix24.com/v1/apps', {
  method: 'POST',
  headers: {
    'X-Api-Key': 'YOUR_APP_KEY',
    'Authorization': 'Bearer USER_SESSION_TOKEN',
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    title: 'Sales dashboard',
    scopes: ['crm', 'user', 'placement'],
    appUrl: 'https://app-abc12345.vibecode.bitrix24.com',
  }),
})

const { data } = await res.json()
// The key is returned only once; only the owner can read the file
const keyPath = join(keyDir, 'app.key')
await writeFile(keyPath, data.rawKey, { mode: 0o600, flag: 'wx' })
console.log('App ID:', data.id)
console.log('Key file:', keyPath)

Response fields

Field Type Description
Fieldsuccess Typeboolean Descriptiontrue on successful creation
Fielddata Typeobject DescriptionThe created application and its paired authorization key
Fielddata.id Typestring DescriptionApplication identifier
Fielddata.title Typestring DescriptionName
Fielddata.description Typestring | null DescriptionDescription
Fielddata.scopes Typestring[] DescriptionSet of application scopes — exactly what you passed in the body. The paired key's scopes depend on the calling key's permissions — see "Known specifics"
Fielddata.handlerUrl Typestring DescriptionHandler URL, set by the platform
Fielddata.appUrl Typestring | null DescriptionApplication URL
Fielddata.redirectUris Typestring[] DescriptionOAuth redirect URLs
Fielddata.bitrixClientId Typestring | null DescriptionOAuth client identifier in the Bitrix24 account
Fielddata.authorId Typestring DescriptionAuthor identifier in the Vibecode platform. To identify the Bitrix24 employee, use data.authorBitrixUserId only when data.authorBitrixUserIdSource: "member"
Fielddata.authorBitrixUserId Typeinteger | null DescriptionBitrix24 employee id in the same format as id in GET /v1/users. Treat it as the author's confirmed id only when the source is member. null when the id is unknown
Fielddata.authorBitrixUserIdSource Typestring | null DescriptionWhere the id came from: member — the author's confirmed membership of the account; snapshot — a value saved when the application was created: it is unconfirmed and may point to a different employee after ownership changes; null — no id
Fielddata.portalId Typestring DescriptionIdentifier of the Bitrix24 account linked to the key. GET /v1/me returns the same field
Fielddata.createdAt Typestring DescriptionCreation date, ISO 8601
Fielddata.updatedAt Typestring DescriptionModification date, ISO 8601
Fielddata.placements Typestring[] DescriptionPlacement codes. The array is empty for a new application
Fielddata.catalogStatus Typestring DescriptionCatalog status. For a new application — always PRIVATE
Fielddata.publishedAt Typestring | null DescriptionPublication date, ISO 8601. For a new application — null
Fielddata.placementResizeEnabled Typeboolean DescriptionWhether to adjust the placement iframe height to fit the application content. For a new application — false. Enable it through Update an application
Fielddata.mobile Typeboolean DescriptionThe "Supports BitrixMobile" flag the platform actually sent to the Bitrix24 account at registration. false when the field was not sent or the flag could not be applied, in which case the reason arrives in warnings
Fielddata.hosting Typestring DescriptionWhere the app runs: platform — on a platform server linked to the app or owned by this Bitrix24 account, or on that server's verified own domain, or the app has no address at all, platform-orphan — an address on a platform subdomain with no live server behind it, external — any other address, including a platform server of another Bitrix24 account. Computed on every read
Fielddata.externalHost Typestring | null DescriptionHost name from appUrl (punycode) for external, otherwise null. An external row can also carry null when the address has no host: the string does not parse as a URL or its scheme has no host
Fielddata.externalRegistrable Typestring | null DescriptionRegistrable domain of the host by the public suffix list, private suffixes included: example.co.uk for shop.example.co.uk, the whole address for an IP address. null whenever externalHost is null
Fielddata.rawKey Typestring DescriptionReady-to-use authorization key vibe_app_… for the X-Api-Key header. Returned only at creation — neither application data nor the list shows it. Save it immediately. If the key is lost, recreate the application
Fieldwarnings Typestring[] DescriptionReturned only when there is something to report. Two reasons: a title that lost its non-ASCII characters on the way in (see "Known specifics"), or a method of registering the application on the account that cannot pass the mobile flag. In the second case, the line starts with mobile:; turn the flag on in the application card on the Bitrix24 account. The application is still created and the response stays 201

Response example

rawKey is shown with a truncated secret — this is not a working value.

JSON
{
  "success": true,
  "data": {
    "id": "33c4d5e6-f7a8-49b0-1234-5c6d7e8f9012",
    "title": "Sales dashboard",
    "description": null,
    "scopes": ["crm", "user", "placement"],
    "handlerUrl": "https://vibecode.bitrix24.com/v1/bitrix-handler",
    "appUrl": "https://app-abc12345.vibecode.bitrix24.com",
    "redirectUris": [
      "https://vibecode.bitrix24.com/oauth/complete",
      "http://localhost"
    ],
    "bitrixClientId": "local.7c3d4e5f6a7b80.55556666",
    "authorId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "authorBitrixUserId": 42,
    "authorBitrixUserIdSource": "member",
    "portalId": "8b1f0e2a-3c4d-5e6f-7a8b-9c0d1e2f3a4b",
    "createdAt": "2026-06-24T09:12:45.781Z",
    "updatedAt": "2026-06-24T09:12:45.781Z",
    "placements": [],
    "catalogStatus": "PRIVATE",
    "publishedAt": null,
    "placementResizeEnabled": false,
    "mobile": false,
    "hosting": "platform",
    "externalHost": null,
    "externalRegistrable": null,
    "rawKey": "vibe_app_local_7c3d4e5f6a7b80_55556666_…_6666"
  }
}

Error response example

400 — validation failed:

JSON
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "title: Required"
  }
}

Errors

HTTP Code Description
HTTP400 CodeVALIDATION_ERROR DescriptionBody validation failed: missing or whitespace-only title, empty scopes, invalid appUrl
HTTP400 CodeREST_REGISTRATION_FAILED DescriptionBitrix24 refused to register the application or its webhook and gave no reason. The response carries error.details.incidentCode — a six-character support reference that locates the log entry. Quote it when you contact support
HTTP400 CodeAPP_URL_UNSAFE DescriptionAn address outside the platform must be a public https:// address: no http://, no localhost or private network, no credentials in the URL
HTTP402 CodeINT_TARIFF_REQUIRED DescriptionThe route checks account access to the platform before anything else. The account is on a free Bitrix24 plan, so creation never starts. The response carries userMessage, the ways to resolve the refusal in error.alternatives, and the current and required plans with the upgrade link in error.details. The same code appears in the 403 row below, and these are different checks: 402 comes from the account access check that runs before any work starts, 403 from the refusal to issue the paired key
HTTP402 CodeINT_VIBE_PLUS_REQUIRED DescriptionAccount access is narrowed to a paid Vibe+ plan, and an ordinary commercial Bitrix24 plan is no longer enough. An account on a trial plan keeps its trial access and does not receive this code
HTTP403 CodeINFRA_DISABLED_FOR_PORTAL Descriptionscopes explicitly asks for vibe:infra and the Bitrix24 account administrator has turned server management off. The application is not created. A request without that scope goes through on such an account as well: the scope the platform appends by default is dropped there silently
HTTP403 CodeKEY_STORAGE_SCOPE_REQUIRES_PERSONAL_KEY DescriptionStorage can be granted only to a key of personal origin. Create a new personal key with storage access.
HTTP403 CodeINFRA_FORBIDDEN_FOR_COWORK_KEY DescriptionThe call was made with a Cowork/Code key — such a key works with data only and cannot perform write operations. To issue a key that can, see Project key for deploy
HTTP403 CodeAPP_CREATION_RESTRICTED DescriptionThe portal policy does not allow the caller to create applications: creation is disabled entirely, limited to Bitrix24 account administrators, or restricted to a list the caller is not on. A Bitrix24 account administrator grants the right. More — Creation rights
HTTP403 CodeKEY_POLICY_READONLY_REQUIRED DescriptionThe portal policy allows only READONLY, but READWRITE was requested
HTTP403 CodeWRITE_BLOCKED_READONLY_KEY DescriptionThe request comes from a key in read-only mode, and the body explicitly asks for mode: "READWRITE": the paired key never receives broader permissions than the calling key. Without the mode field such a key gets an application in its own mode. The Cowork/Code seat mode does not produce this code: a request for broader permissions than the seat allows results in a key with the seat's access mode. More — Access mode
HTTP403 CodeINT_TARIFF_REQUIRED DescriptionThe account gets access through its Bitrix24 plan, and issuing the paired authorization key requires a commercial plan. error.details.upgradeUrl is not returned. The same code with 402 above is a different check — it runs earlier and concerns account access rather than key issuance
HTTP403 CodePORTAL_TARIFF_UNREADABLE DescriptionThe account plan could not be read, so the response does not name the plan that is missing: error.details.requiredTariffs is empty and the button points to support. Buying a plan does not clear this refusal
HTTP403 CodeSCOPE_GRANT_REQUIRES_CONSENT DescriptionThe request comes from a key with a fixed rights set and declares a platform vibe:* scope that the calling key itself does not hold. The list of such scopes arrives in error.details.unconsented. The rights set is fixed for a partner key, for a Cowork project key, for a personal key issued by the Vibecode dashboard form (it grants exactly the rights that were ticked) and for a key issued with exactScopes: true — Management keys. Scopes confirmed to the partner key on the consent page can be declared. How to identify such a key — "Known specifics"
HTTP403 CodeSCOPE_GRANT_REQUIRES_CONSENT DescriptionThe request comes from a partner key and declares vibe:ai: a partner key does not pass this scope on to the paired key, even when it holds the scope itself. error.details.unconsented carries ["vibe:ai"]
HTTP403 CodeCONNECTOR_APP_INSTALL_FORBIDDEN DescriptionA Bitrix24 account administrator has barred this employee from installing applications. It arrives where the application is installed by the connector module: on a self-hosted account, and on a cloud account once that issuance path is enabled for it. Retrying does not change the state; the right is granted by an account administrator. More — Creation rights
HTTP409 CodeKEY_LIMIT_REACHED DescriptionThe key limit has been reached. error.details carries the quota state: limit — how many keys are allowed, used — how many are taken, requested — how many new keys the operation needs. Without scope, this is the per-user account limit; scope: "portal" means the platform-wide account cap, which a Bitrix24 account administrator cannot raise. used also counts application authorization keys and keys the platform issued itself, so the total can exceed what the key list in the dashboard shows — more
HTTP409 CodeCONNECTOR_MODULE_NOT_INSTALLED DescriptionThe connector module is not installed on the Bitrix24 account — an application cannot be installed and a paired key cannot be issued through it. The state is permanent; retrying without installing the module will not help
HTTP409 CodeB24_USER_DELETED DescriptionThe Bitrix24 employee who owns the paired key is no longer active on the account. The state is permanent: neither a retry nor freeing a key slot helps — the employee has to be restored on the account, or the application created under another user
HTTP422 CodeAPP_HOST_NOT_ALLOWED_HERE DescriptionappUrl points at a named address of the platform app domain (<name>.<domain>, not app-…) whose name is not bound to a server of your Bitrix24 account. Nothing is stored and Bitrix24 is not called. A name of your own server is accepted and stored as that server's app-… address
HTTP502 CodeCONNECTOR_APP_INSTALL_FAILED DescriptionThe connector module failed to install the application for another reason. Neither the application nor the paired key is created, and the request can be retried
HTTP502 CodeCONNECTOR_REST_UNAVAILABLE DescriptionThe account's plan or trial period is active, but Bitrix24 refused to issue the paired key. It arrives where the other connector codes do — when the application is installed by the connector module. The original refusal reason arrives in error.details.reason, and error.details.retryable: true says the state is transient — retry the request, and contact support if it repeats. The response carries no upgrade offer: there is nothing to buy
HTTP502 CodeCONNECTOR_PLAN_REQUIRED DescriptionBitrix24 refused to issue the paired key: the account plan does not include Vibecode. This error is returned when no plan purchase can be offered: the account is self-hosted, is already on a paid plan, or belongs to an unrecognised region. The human-readable cause arrives in error.userMessage. The state is permanent: retrying will not help until the Bitrix24 plan changes. Where access is sold, the same refusal arrives as 402 with a plan paywall code from the table above

The full list of general API errors — Errors.

Known specifics

  • The platform reports non-ASCII characters lost from the title. A value sent without explicit UTF-8 serialization arrives with question marks instead of letters: the bytes are lost on the client side, before the request is sent. Windows PowerShell sends values this way. The application is still created as is, and the response carries warnings naming the field — the title becomes the Bitrix24 catalog card name on publish, so fix it right away. A ready-to-run call with UTF8.GetBytes — Windows / PowerShell and UTF-8.
  • A pair is created: an application and an API key. Registration creates an application record and a paired authorization key in one step. This affects deletion: the key frees a slot in the per-user limit only after deleting the application.
  • Application scopes and paired-key scopes are different sets. In scopes you declare access to Bitrix24 account data — crm, user, placement. If the calling key's rights set is not fixed, the paired key receives that set plus platform scopes: vibe:infra — servers and agents, vibe:ai — model calls, vibe:search — web search and research, vibe:storage — object storage (only when the calling key has PERSONAL origin). The authorization key can then create servers through POST /v1/infra/servers right away, even though vibe:infra was never in the create body. The data.scopes field of the response shows the application declaration, not the key's effective set — read the effective set from data.scopes of GET /v1/me called with that key.
  • A key with a fixed rights set grants the paired key exactly what was declared. The rights set is fixed for a partner key, for a Cowork project key, for a personal key issued by the Vibecode dashboard form and for a key issued with exactScopes: true — Management keys. An application created with such a key gets a paired key holding exactly what is listed in scopes — no platform scopes are added to it. If you need infrastructure, list vibe:infra in scopes at creation time, otherwise POST /v1/infra/servers answers 403 INFRA_SCOPE_REQUIRED. The same goes for vibe:ai, vibe:search and vibe:storage. You can only declare a right that the calling key itself holds, otherwise the answer is 403 SCOPE_GRANT_REQUIRES_CONSENT. Adding the right later through updating the application does not work: vibe:* propagation never reaches a key with a fixed set.
  • Storage requires a personal key origin. If the caller key was issued to an application or has unknown origin, vibe:storage is omitted from paired-key defaults. An explicit vibe:storage in scopes returns 403 KEY_STORAGE_SCOPE_REQUIRES_PERSONAL_KEY, even if the caller already holds it. Create a new personal key with storage access.
  • You can identify a key with a fixed rights set before creating the application. Call GET /v1/me with the key you plan to create the application with. The set is fixed when data.scopes matches what was ticked when the key was issued. Other keys carry vibe:ai and vibe:search in the set even when those were not ticked.
  • The application opens in the account interface. Registration tells the Bitrix24 account that the application has an interface, so after creation it appears in the account menu, and with mobile: true in the mobile client as well. The mode is set at creation time and updating the application does not change it. An application that never appeared in the menu is registered as API-only: turn the API-only mode off in the application card on the Bitrix24 account, or create the application again.
  • The mobile flag can only be set at creation. PATCH /v1/apps/:id does not accept mobile — a sent value is ignored, see Update an application. For an existing application, turn the flag on in the application card on the Bitrix24 account, or create the application again.

See also