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
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
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
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
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.
{
"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:
{
"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
warningsnaming the field — the title becomes the Bitrix24 catalog card name on publish, so fix it right away. A ready-to-run call withUTF8.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
scopesyou 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 throughPOST /v1/infra/serversright away, even thoughvibe:infrawas never in the create body. Thedata.scopesfield of the response shows the application declaration, not the key's effective set — read the effective set fromdata.scopesofGET /v1/mecalled 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 inscopes— no platform scopes are added to it. If you need infrastructure, listvibe:infrainscopesat creation time, otherwisePOST /v1/infra/serversanswers403 INFRA_SCOPE_REQUIRED. The same goes forvibe:ai,vibe:searchandvibe:storage. You can only declare a right that the calling key itself holds, otherwise the answer is403 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:storageis omitted from paired-key defaults. An explicitvibe:storageinscopesreturns403 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/mewith the key you plan to create the application with. The set is fixed whendata.scopesmatches what was ticked when the key was issued. Other keys carryvibe:aiandvibe:searchin 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: truein 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
mobileflag can only be set at creation.PATCH /v1/apps/:iddoes not acceptmobile— 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.