لوكلاء AI: ملف markdown لهذه الصفحة — /docs-content-en/keys-auth/oauth.md فهرس التوثيق — /llms.txt
مقالات التوثيق متاحة حاليًا باللغة الإنجليزية فقط.
Application user authorization
OAuth authorization flow for applications that act on behalf of different users of a Bitrix24 account. The user signs in through Bitrix24, the application receives a session token, and then calls the API on their behalf. An AI agent that is given the authorization key completes this whole flow on its own — following the steps below.
Scope: not required | Base URL: https://vibecode.bitrix24.com/v1 | Authorization: the authorization key in the app_key parameter
When user authorization is needed
The vibe_app_ authorization key acts on behalf of the specific user who installed the application. To tell the platform which user to call Bitrix24 as, the request carries a session token in the Authorization: Bearer header. The token is issued at the end of this flow.
The personal vibe_api_ key acts on behalf of its own owner and does not use a session token. For background scenarios without a user at the screen, use it instead of the authorization key — Passing the key.
The /v1/oauth/* endpoints do not read the X-Api-Key header. They take the authorization key as the app_key parameter: in the query string of GET /v1/oauth/authorize and GET /v1/oauth/poll, in the body of POST /v1/oauth/token and POST /v1/oauth/placement-session. The exception is POST /v1/oauth/revoke: it needs no authorization key and accepts only the session token in the Authorization: Bearer header.
Step 1. Send the user to authorization
Generate a state — a cryptographically random string of 16–512 characters. This protects against cross-site request forgery per RFC 6749 §10.12: the client generates the value, and on return checks that the same value came back. The server cannot generate state for you.
Build a link to GET /v1/oauth/authorize and redirect the user's browser to it. This is a top-level navigation, not a background request — the Bitrix24 consent page sends an X-Frame-Options header and does not open inside an iframe.
const state = crypto.randomUUID()
// Save state in the user's session to check it on return
const url = new URL('https://vibecode.bitrix24.com/v1/oauth/authorize')
url.searchParams.set('app_key', 'YOUR_APP_KEY')
url.searchParams.set('state', state)
url.searchParams.set('redirect_uri', 'https://myapp.example.com/callback')
window.location.href = url.toString()
The platform checks app_key and redirect_uri and redirects the user to the Bitrix24 authorization page. After authorization, Bitrix24 returns the user to the application's registered handler, and the platform then returns them to your redirect_uri.
Step 2. Handle the return to `redirect_uri`
After authorization, the platform redirects the browser to your redirect_uri with a one-time code and your state:
https://myapp.example.com/callback?code=VIBE_AUTH_CODE&state=YOUR_STATE
Compare state with the value you saved in step 1. If the values do not match, abort authorization. For the errors this address can carry, see What arrives at redirect_uri.
Step 3. Exchange the code for a session token
From the application server, exchange code for a session token. The redirect_uri in the body must exactly match the one from step 1.
cURL
curl -X POST https://vibecode.bitrix24.com/v1/oauth/token \
-H "Content-Type: application/json" \
-d '{
"app_key": "YOUR_APP_KEY",
"code": "VIBE_AUTH_CODE",
"redirect_uri": "https://myapp.example.com/callback"
}'
JavaScript
const res = await fetch('https://vibecode.bitrix24.com/v1/oauth/token', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
app_key: 'YOUR_APP_KEY',
code: 'VIBE_AUTH_CODE',
redirect_uri: 'https://myapp.example.com/callback',
}),
})
const { access_token, user, expires_in } = await res.json()
// Save access_token — it is valid for 24 hours
{
"success": true,
"access_token": "vibe_session_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"user": { "id": "42", "name": "John Smith", "email": "user@example.com" },
"expires_in": 86400
}
The code is one-time and valid for 5 minutes.
Step 4. Call the API with the session token
Every API request carries two headers — the authorization key in X-Api-Key and the session token in Authorization: Bearer:
cURL
curl https://vibecode.bitrix24.com/v1/deals \
-H "X-Api-Key: YOUR_APP_KEY" \
-H "Authorization: Bearer USER_SESSION_TOKEN"
JavaScript
const res = await fetch('https://vibecode.bitrix24.com/v1/deals', {
headers: {
'X-Api-Key': 'YOUR_APP_KEY',
'Authorization': 'Bearer USER_SESSION_TOKEN',
},
})
const { data } = await res.json()
The session inherits the application's rights in the Bitrix24 account. Without the Authorization: Bearer header, endpoints that require a user context return 401 TOKEN_MISSING.
What arrives at `redirect_uri`
The platform returns the user to redirect_uri with the result in query parameters. Your state always comes back — check it first.
| Parameter | When it arrives | Value |
|---|---|---|
Parametercode |
When it arrivesAuthorization succeeded | ValueOne-time code to exchange for a token. Valid for 5 minutes |
Parameterstate |
When it arrivesAlways | ValueThe state value passed in step 1 |
Parametererror |
When it arrivesAuthorization failed | ValueReason for the denial — one of the values below |
error values:
error |
What happened |
|---|---|
errortoken_exchange_failed |
What happenedBitrix24 did not issue tokens for the authorization code |
errorinvalid_domain |
What happenedThe Bitrix24 account domain failed validation |
errorprofile_fetch_failed |
What happenedFailed to read the user profile in the Bitrix24 account |
errorapp_unavailable |
What happenedThe application is no longer available: its author erased their data |
Some failures at the return step arrive as a JSON response with status 400 or 500 instead of in the address: until the platform knows redirect_uri, it cannot redirect to it. These are MISSING_PARAMS, INVALID_STATE, and APP_CONFIG_ERROR from the Errors table.
`redirect_uri` rule
redirect_uri must exactly match one of the addresses registered for the application — scheme, host, port, and path all match. You can add a return address in the application card in the Authorization Keys section — the "Redirect URI (OAuth)" field.
A new application has the address http://localhost registered by default — without a port and with the path /. The match is exact, so http://localhost:3000/callback does not match it. For the return to go to your address — http://localhost:3000/callback or https://myapp.example.com/callback — first add it in the application card. Otherwise GET /v1/oauth/authorize returns 400 INVALID_REDIRECT_URI.
If redirect_uri is omitted, the platform uses the built-in return page — Authorization without your own redirect_uri.
Authorization without your own `redirect_uri`
When redirect_uri is omitted, the platform returns the user to the built-in /oauth/complete page and creates the session token on its side. The application retrieves the result by polling GET /v1/oauth/poll with the same state.
cURL
curl "https://vibecode.bitrix24.com/v1/oauth/poll?app_key=YOUR_APP_KEY&state=YOUR_STATE"
JavaScript
const url = `https://vibecode.bitrix24.com/v1/oauth/poll?app_key=YOUR_APP_KEY&state=${state}`
const res = await fetch(url)
const result = await res.json()
if (result.status === 'complete') {
// result.access_token, result.user, result.expires_in
}
Until the user completes sign-in, the response carries status pending:
{ "success": true, "status": "pending" }
After sign-in, the same request returns the session token. The result is one-time — the next poll with this same state returns pending again:
{
"success": true,
"status": "complete",
"access_token": "vibe_session_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"user": { "id": "42", "name": "John Smith", "email": "user@example.com" },
"expires_in": 86400
}
The result is kept for 10 minutes. Retrieve it right after the user returns to the sign-in page.
Session token: lifetime and revocation
The session token is valid for 24 hours and is not refreshed — the expires_in field in the response equals 86400. There is no renewal mechanism. After expiry, calls on behalf of the user return 401 INVALID_SESSION — this is the signal to go through authorization again: GET /v1/oauth/authorize → POST /v1/oauth/token.
To revoke a token early, call POST /v1/oauth/revoke with the session token in the Authorization: Bearer header. Revocation deletes the user's session, and further calls with this token are rejected.
cURL
curl -X POST https://vibecode.bitrix24.com/v1/oauth/revoke \
-H "Authorization: Bearer USER_SESSION_TOKEN"
JavaScript
const res = await fetch('https://vibecode.bitrix24.com/v1/oauth/revoke', {
method: 'POST',
headers: { 'Authorization': 'Bearer USER_SESSION_TOKEN' },
})
const { revoked } = await res.json()
{ "success": true, "revoked": true }
The revoked field equals false if there was no longer an active session with that token. The response arrives with status 200 in both cases.
Application on your own server
A separate tab for sign-in is not always available — for example, the application opens as a placement inside Bitrix24, and the consent page does not open inside an iframe. If the application is hosted on your own server rather than behind Black Hole, the current user's session token is obtained in one of two ways — depending on whose handler receives the placement.
Vibecode application, appUrl — your server. The placement arrives at the Vibecode handler, and the platform redirects the browser to <appUrl>/?code=<one-time code>&placement=...&member_id=.... Your server exchanges the code for a session token using the same POST /v1/oauth/token request with the body { app_key, code, redirect_uri }, where redirect_uri exactly equals the configured appUrl. The session token does not reach the browser — the address holds only a one-time code, useless without app_key. Edit the appUrl field in the application card in the Authorization Keys section.
Your own Bitrix24 application with its own handler. The placement arrives directly at your server with a Bitrix24 user token. Exchange it for a session token with a server request — POST /v1/oauth/placement-session.
cURL
curl -X POST https://vibecode.bitrix24.com/v1/oauth/placement-session \
-H "Content-Type: application/json" \
-d '{
"app_key": "YOUR_APP_KEY",
"access_token": "BITRIX24_USER_TOKEN",
"member_id": "MEMBER_ID",
"domain": "mycompany.bitrix24.com"
}'
JavaScript
const res = await fetch('https://vibecode.bitrix24.com/v1/oauth/placement-session', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
app_key: 'YOUR_APP_KEY',
access_token: 'BITRIX24_USER_TOKEN',
member_id: 'MEMBER_ID',
domain: 'mycompany.bitrix24.com',
}),
})
const { access_token, user, expires_in } = await res.json()
{
"success": true,
"access_token": "vibe_session_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"user": { "id": "42", "name": "John Smith", "email": "user@example.com" },
"expires_in": 86400
}
domain must match the application's Bitrix24 account. The refresh_token and expires_in fields in the body are optional and describe the Bitrix24 user token: expires_in — how long it is valid, 3600 seconds by default. Without an active refresh_token, the Bitrix24 token is not refreshed automatically — after it expires, reopen the placement. The session token itself, as in the main flow, is valid for 24 hours.
For the full lifecycle of an embedded application and handler examples in Node, Python, and Go, see Authorization in a Black Hole app.
Endpoint reference
| Method | Path | Description |
|---|---|---|
| MethodGET | Path/v1/oauth/authorize |
DescriptionStart authorization — redirect to the Bitrix24 sign-in page |
| MethodGET | Path/v1/oauth/callback |
DescriptionReceive the Bitrix24 response. Called by Bitrix24, not the application |
| MethodPOST | Path/v1/oauth/token |
DescriptionExchange the code for a session token |
| MethodGET | Path/v1/oauth/poll |
DescriptionRetrieve the authorization result without your own redirect_uri |
| MethodPOST | Path/v1/oauth/placement-session |
DescriptionSession token for an application on your own server |
| MethodPOST | Path/v1/oauth/revoke |
DescriptionRevoke the session token |
Errors
The authorization key is checked on entry to GET /v1/oauth/authorize, POST /v1/oauth/token, and POST /v1/oauth/placement-session:
| HTTP | Code | Description |
|---|---|---|
| HTTP401 | CodeINVALID_APP_KEY |
Descriptionapp_key not found or it is not an authorization key |
| HTTP401 | CodeKEY_INACTIVE |
DescriptionAuthorization key revoked |
| HTTP401 | CodeKEY_EXPIRED |
DescriptionAuthorization key has expired |
| HTTP403 | CodeOWNER_BLOCKED |
DescriptionThe key owner is blocked or no longer active on the portal |
GET /v1/oauth/authorize:
| HTTP | Code | Description |
|---|---|---|
| HTTP400 | CodeINVALID_REQUEST |
DescriptionMissing required state or it is outside the 16–512 character range |
| HTTP400 | CodeINVALID_REDIRECT_URI |
Descriptionredirect_uri is not registered for the application |
| HTTP500 | CodeNO_PORTAL |
DescriptionThe application is not linked to a portal |
GET /v1/oauth/callback (called by Bitrix24):
| HTTP | Code | Description |
|---|---|---|
| HTTP400 | CodeMISSING_PARAMS |
DescriptionBitrix24 returned without code or state |
| HTTP400 | CodeINVALID_STATE |
Descriptionstate not found or expired after 20 minutes. error.details.reason holds NOT_FOUND or EXPIRED |
| HTTP500 | CodeAPP_CONFIG_ERROR |
DescriptionThe application has no OAuth credentials set |
POST /v1/oauth/token:
| HTTP | Code | Description |
|---|---|---|
| HTTP400 | CodeINVALID_REQUEST |
DescriptionThe body has no code or redirect_uri |
| HTTP400 | CodeINVALID_CODE |
DescriptionCode not found or belongs to another application |
| HTTP400 | CodeCODE_EXPIRED |
DescriptionThe code has expired — 5 minutes |
| HTTP400 | CodeCODE_ALREADY_USED |
DescriptionThe code has already been exchanged for a token |
| HTTP400 | CodeREDIRECT_URI_MISMATCH |
Descriptionredirect_uri does not match the one passed to GET /v1/oauth/authorize |
| HTTP500 | CodeTOKEN_NOT_FOUND |
DescriptionUser token not found |
GET /v1/oauth/poll:
| HTTP | Code | Description |
|---|---|---|
| HTTP400 | CodeINVALID_REQUEST |
Descriptionstate shorter than 16 characters or no app_key |
| HTTP401 | CodeINVALID_APP_KEY |
Descriptionapp_key not found or it is not an authorization key |
POST /v1/oauth/placement-session:
| HTTP | Code | Description |
|---|---|---|
| HTTP400 | CodeINVALID_REQUEST |
DescriptionThe body has no access_token, member_id, or domain |
| HTTP400 | CodeDOMAIN_MISMATCH |
Descriptiondomain does not match the application's portal |
| HTTP401 | CodeUSER_AUTH_REQUIRED |
DescriptionThe Bitrix24 user token did not confirm authorization on the portal |
| HTTP403 | CodeAPP_UNAVAILABLE |
DescriptionThe application is no longer available: its author erased their data |
| HTTP500 | CodeNO_PORTAL |
DescriptionThe application is not linked to a portal |
POST /v1/oauth/revoke:
| HTTP | Code | Description |
|---|---|---|
| HTTP400 | CodeMISSING_TOKEN |
DescriptionNo Authorization: Bearer with a session token was passed |
API calls with a session token:
| HTTP | Code | Description |
|---|---|---|
| HTTP401 | CodeTOKEN_MISSING |
DescriptionThe authorization key was sent without Authorization: Bearer to an endpoint that needs a user context |
| HTTP401 | CodeINVALID_SESSION |
DescriptionThe session token has expired, been revoked or is unknown. Authorize again: GET /v1/oauth/authorize → POST /v1/oauth/token |
Full list of common API errors — Errors.
Error response example
400 — required state not passed:
{
"success": false,
"error": {
"code": "INVALID_REQUEST",
"message": "state: Required"
}
}