لوكلاء AI: ملف markdown لهذه الصفحة — /docs-content-en/applications/long-method.md فهرس التوثيق — /llms.txt
مقالات التوثيق متاحة حاليًا باللغة الإنجليزية فقط.
A long-running method in a business process
Difficulty: advanced | Scopes: bizproc | Stack: cURL / JavaScript (Node.js, Express)
A recipe for an application whose work does not fit inside the call window: the application takes the job from a paused business-process activity in Bitrix24, answers straight away and delivers the result in a separate request once it is done. The business process sleeps on its step for all that time, and the Bitrix24 account keeps its state. A long-running method under a solution contract works differently — there the reply address and a single-use token arrive as call headers, and the platform delivers the outcome to Bitrix24 itself: Deliver the result of a long-running method.
What you will need
- An application with the external API switched on and a key issued. The switch and the key issuance live in the application card, and the conditions and refusals are described on the Application external API page. The business process calls the application with that key.
- A platform key for the reply —
vibe_api_…with thebizprocscope, in read-and-write mode, bound to the same Bitrix24 account. The requirements are covered below. - A pausable business-process activity in the Bitrix24 account. It calls the application, puts its own
eventTokeninto the request body and falls asleep until the event. Registering such an activity —/v1/bizproc-activities. - Node.js 18 or newer for the examples.
Answer with your own platform key, not with the key the application was called with. The external API key is locked to an allow-list of routes: exactly one platform address is open to it — forwarding the call to the application. On POST /v1/workflows/event and POST /v1/workflows/activity-log it gets 403 APP_API_KEY_OUT_OF_SCOPE before the handler, and no setting changes that.
A platform key vibe_api_… works here — the same one the solution already uses to call the Vibecode API. The platform neither issues such a key nor injects it into the application. A person creates it in the API Keys section and hands it to the application through an environment variable at deploy time. The variable name is yours to choose, VIBE_API_KEY by convention: the application reads it from its own environment and sends the value in the X-Api-Key header. There is no key created automatically "for the server", and a 401 MISSING_API_KEY from the application means the variable never arrived. Three requirements apply to the key:
| Requirement | What happens otherwise |
|---|---|
RequirementThe bizproc scope |
What happens otherwise403 SCOPE_DENIED |
| RequirementRead-and-write mode | What happens otherwise403 WRITE_BLOCKED_READONLY_KEY. The response carries the address of the page where the mode is switched |
| RequirementA bound Bitrix24 account | What happens otherwise401 TOKEN_MISSING. An application authorization key vibe_app_… presented without an employee session token gets the same answer: its account credentials live in the session rather than on the key |
The reply reaches Bitrix24 with the rights of the key owner, not of the employee who started the business process. The platform performs the event and the log entry with the owner's stored credentials, so choose the key owner deliberately: they need rights over the processes the solution answers. The key's current scopes and the state of its account binding are shown by GET /v1/me. Where the key mode is switched — Keys and authorization.
In every example $VIBE_URL is the base address https://vibecode.bitrix24.com, $VIBE_API_KEY is the platform reply key from item 2, and YOUR_APP_EXTERNAL_API_KEY is the external API key from item 1.
How the solution works
- The business-process activity calls the application at the external API address and passes its
eventToken— the single-use key of its own step — in the request body. - The application stores the
eventTokentogether with the job and answers202within the call window. The activity sleeps after that. - While the work runs, the application writes into the process log through
POST /v1/workflows/activity-log. That does not change the state of the process. - Once finished, the application calls
POST /v1/workflows/eventwith the sameeventTokenand the values for the activity's output parameters. The process wakes up and moves on.
The application needs no Bitrix24 credentials at any step: the platform reaches the account with the stored credentials of the key owner.
The step examples show individual calls and rely on variables declared earlier in the scenario. A handler ready to run is in the "Full code" section.
Step 1. Take the call and answer straight away
The business-process automation rule sends a request to the application address:
Address: https://vibecode.bitrix24.com/v1/applications/:applicationId/api/<your-path>
Method: POST
Header: X-Api-Key: YOUR_APP_EXTERNAL_API_KEY
In the body the activity passes its eventToken and everything the application needs for the work. The activity puts the field there, not the platform — the author of the activity chooses its name, and in the examples below it is called eventToken.
On top of the body, the platform adds headers the caller cannot forge — incoming names with the X-Vibe- prefix are stripped:
| Header | What it carries |
|---|---|
HeaderX-Vibe-Request-Id |
What it carriesCall ID. The platform writes the same ID into its own log |
HeaderX-Vibe-Caller-Kind |
What it carriesThe value external-api — the call arrived through this channel rather than from the Bitrix24 interface |
HeaderX-Vibe-Caller-Portal-Id |
What it carriesThe Bitrix24 account the calling key belongs to |
HeaderX-Vibe-Caller-Key-Id |
What it carriesID of the external API key the call was made with |
There are no headers with a reply address, a reply token or a deadline on this path — those belong to a solution-contract call. Nor are there headers carrying an employee identity: an external call carries no session. A full breakdown of what reaches the application — What the application receives.
A 202 response tells the automation rule that the job is accepted. The response body is up to the application: the automation rule puts top-level values into business-process variables.
JavaScript
// The application route handler that the business-process activity calls.
app.post('/long-method', async (req, res) => {
const { eventToken, dealId } = req.body;
if (!eventToken) {
res.status(400).json({ accepted: false, reason: 'EVENT_TOKEN_REQUIRED' });
return;
}
// Put the job on a queue and hand control back AT ONCE: the answer has to
// leave within the call window, and the work will take longer.
queue.push({ eventToken, dealId });
res.status(202).json({ accepted: true });
});
Step 2. Write an interim step into the process log
While the work runs, the application reports into the execution log of the business process. The entry is visible to whoever looks at the process in the Bitrix24 account, and it does not change the state of the process — the activity stays paused.
cURL
curl -X POST "$VIBE_URL/v1/workflows/activity-log" \
-H "X-Api-Key: $VIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"eventToken":"xxx.yyy.zzz","logMessage":"Selection collected, computing the discount"}'
JavaScript
await fetch(`${VIBE_URL}/v1/workflows/activity-log`, {
method: 'POST',
headers: {
'X-Api-Key': VIBE_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
eventToken,
logMessage: 'Selection collected, computing the discount',
}),
});
{
"success": true,
"data": true
}
The full description of the fields and refusals — Write to the process log.
Step 3. Send the result into the process
The work is done — the application sends an event with the same eventToken. The returnValues object lands in the activity's output parameters, and the business process carries on.
| Field | Type | Required | Description |
|---|---|---|---|
FieldeventToken |
Typestring | Requiredyes | DescriptionThe very token that arrived in the call body at step 1 |
FieldreturnValues |
Typeobject | Requiredno | DescriptionValues of the activity's output parameters. Defaults to an empty object |
FieldlogMessage |
Typestring | Requiredno | DescriptionAn entry in the process execution log alongside the event |
cURL
curl -X POST "$VIBE_URL/v1/workflows/event" \
-H "X-Api-Key: $VIBE_API_KEY" \
-H "Content-Type: application/json" \
-d '{"eventToken":"xxx.yyy.zzz","returnValues":{"discount":12.5,"verdict":"approve"},"logMessage":"Discount computed"}'
JavaScript
const res = await fetch(`${VIBE_URL}/v1/workflows/event`, {
method: 'POST',
headers: {
'X-Api-Key': VIBE_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
eventToken,
returnValues: { discount: 12.5, verdict: 'approve' },
logMessage: 'Discount computed',
}),
});
const body = await res.json();
if (!body.success) {
console.error(`the event was not accepted: ${body.error.code}`);
}
{
"success": true,
"data": true
}
The data field set to true means Bitrix24 accepted the event and the process resumed. The full description of the fields and refusals — Send an event to a process.
Limits
The call window at step 1. A call through the application external API lives for 30 seconds — that is the overall limit, past which the caller gets 503 APP_API_TIMEOUT. The application itself has to start answering earlier: the platform stops waiting for its response at the twenty-fifth second and returns 503 APP_API_UNAVAILABLE to the caller. Computing over a large selection, calling an external system, generating a document or waiting for a person to answer does not fit inside those windows — so the application answers 202 and finishes the work after the answer.
A 202 at step 1 does not guarantee the work has started. The automation rule gets an acknowledgement of receipt, and from there the only signal about progress is the entries in the process log. Write an entry at every meaningful boundary: diagnosing a stuck step from the process log is cheaper than from the application logs. The platform does not limit the number of entries per step, and every entry is a separate API call that counts against the common limits.
A failure comes back as an event too. The paused activity waits for POST /v1/workflows/event and knows nothing about the application having crashed. While no event arrives, the step stays paused. So a failed computation answers with an event carrying a failure flag in returnValues rather than with silence.
One eventToken closes one step. Every pause of the process issues a new token, and right after POST /v1/workflows/event the previous one is invalid. The platform does not limit the token's lifetime and passes it to Bitrix24 as is, and an invalid or already-used token is rejected by Bitrix24 with 403 BITRIX_ACCESS_DENIED. So a blind retry after a dropped connection is unsafe: the same code arrives both when the first request got through and when the token was corrupted, and the response gives you nothing to tell the two apart.
The reply key is not kept next to the calling key. The external API key sits in the automation rule settings in the Bitrix24 account, the reply key sits in the application's environment variables. Revoking one leaves the other alone, and reissuing the external API key does not break replies already in flight.
The scope is checked before the key mode. A key without the bizproc scope and in read-only mode gets 403 SCOPE_DENIED rather than a mode refusal. Open the scope first, then look at the mode.
Reply refusals. The table covers both reply addresses — POST /v1/workflows/event and POST /v1/workflows/activity-log. The refusals of the application call itself at step 1 are in the table on Application external API.
| HTTP | Code | Description |
|---|---|---|
| HTTP400 | CodeMISSING_PARAMS |
DescriptioneventToken not provided. On the log write, a request without logMessage gets the same answer |
| HTTP401 | CodeMISSING_API_KEY |
DescriptionThe header with the key was not provided |
| HTTP401 | CodeINVALID_API_KEY |
DescriptionThe key is not recognized — no such key exists on the platform |
| HTTP401 | CodeTOKEN_MISSING |
DescriptionThe key has no Bitrix24 account binding. The response text names the reason and what to do about it |
| HTTP403 | CodeSCOPE_DENIED |
DescriptionThe key is missing the bizproc scope. Checked first — before the key mode |
| HTTP403 | CodeWRITE_BLOCKED_READONLY_KEY |
DescriptionThe key is in read-only mode. The details.switchUrl field names the page where the mode is changed |
| HTTP403 | CodeAPP_API_KEY_OUT_OF_SCOPE |
DescriptionThe reply was sent with an application external API key. That key may only forward calls to the application |
| HTTP403 | CodeBITRIX_ACCESS_DENIED |
DescriptionBitrix24 rejected the request: the token is invalid, expired, or already used |
| HTTP409 | CodeBIZPROC_MODULE_NOT_ENABLED |
DescriptionBusiness processes are not available on the Bitrix24 account: the module is not part of the plan or is switched off in the account settings. Ask a Bitrix24 administrator to enable business processes — until then both addresses give the same answer however many times you retry |
| HTTP422 | CodeBITRIX_ERROR |
DescriptionError on the Bitrix24 side |
| HTTP429 | CodeRATE_LIMITED |
DescriptionRequest rate limit exceeded. Retry in 1–2 seconds |
| HTTP502 | CodeBITRIX_UNAVAILABLE |
DescriptionBitrix24 is unavailable |
403 — the key is in read-only mode:
{
"success": false,
"error": {
"code": "WRITE_BLOCKED_READONLY_KEY",
"message": "Key is in read-only mode. Switch the key to read and write in /keys to allow Bitrix24 data changes.",
"details": {
"method": "bizproc.event.send",
"keyName": "Solution deploy key",
"currentMode": "READONLY",
"switchUrl": "/keys"
}
}
}
Full list of common API errors — Errors.
Full code
The handler takes the activity's call, answers 202 and finishes the job in the background, reporting into the process log. Every variable is declared inside it, and it runs as is on Node.js 18 and newer.
import express from 'express';
const app = express();
app.use(express.json());
app.use(express.urlencoded({ extended: true })); // the automation rule sends form fields, not JSON
const VIBE_URL = process.env.VIBE_URL ?? 'https://vibecode.bitrix24.com';
const VIBE_API_KEY = process.env.VIBE_API_KEY;
if (!VIBE_API_KEY) throw new Error('The VIBE_API_KEY environment variable is not set');
async function callVibe(path, body) {
const res = await fetch(`${VIBE_URL}/v1${path}`, {
method: 'POST',
headers: {
'X-Api-Key': VIBE_API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const payload = await res.json();
if (!payload.success) {
throw new Error(`${path}: ${payload.error.code} — ${payload.error.message}`);
}
return payload.data;
}
async function runLongWork(eventToken, dealId) {
try {
await callVibe('/workflows/activity-log', {
eventToken,
logMessage: `Computing the discount for deal ${dealId}`,
});
const discount = await computeDiscount(dealId); // your long-running work
await callVibe('/workflows/event', {
eventToken,
returnValues: { discount, verdict: discount > 0 ? 'approve' : 'reject' },
logMessage: 'Discount computed',
});
} catch (err) {
// The process sleeps and waits for an event. A failure has to come back as an
// event too, otherwise the step stays paused until the deadline set in Bitrix24.
await callVibe('/workflows/event', {
eventToken,
returnValues: { discount: 0, verdict: 'error' },
logMessage: `The computation failed: ${err.message}`,
}).catch(() => {});
}
}
app.post('/long-method', (req, res) => {
const { eventToken, dealId } = req.body;
if (!eventToken || !dealId) {
res.status(400).json({ accepted: false, reason: 'EVENT_TOKEN_AND_DEAL_REQUIRED' });
return;
}
// The answer leaves AT ONCE, the work runs after it.
res.status(202).json({ accepted: true });
void runLongWork(eventToken, String(dealId));
});
async function computeDiscount(dealId) {
await new Promise((resolve) => setTimeout(resolve, 60_000));
return 12.5;
}
app.listen(process.env.PORT || 3000);