AIエージェント向け: このページのMarkdown — /docs-content-en/applications/long-method.md ドキュメント索引 — /llms.txt

現在、ドキュメントは英語のみです。

A long-running method in a business process

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.

Base URL: https://vibecode.bitrix24.com/v1 | Authorization: X-Api-Key | Reply scope: bizproc

When you need it | What you will need | How the solution works | Step 1 | Step 2 | Step 3 | The reply key | Errors | Limits | Full code

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.

When you need it

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.

Work that does not fit inside those windows — a computation over a large selection, a request to an external system, document generation, waiting for a person to answer — needs a different shape. The application takes the job, answers the automation rule straight away and delivers the result once it is ready. 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 the Bitrix24 account itself. This recipe is about a business process, where the reply is up to the solution: Deliver the result of a long-running method.

What you will need

  1. 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.
  2. A platform key for the reply — vibe_api_… with the bizproc scope, in read-and-write mode, bound to the same Bitrix24 account. You create it yourself and hand it to the application through an environment variable — the platform does not issue it. The requirements are covered in The reply key.
  3. A pausable business-process activity in the Bitrix24 account. It calls the application, puts its own eventToken into the request body and falls asleep until the event. Registering such an activity — /v1/bizproc-activities.
  4. Node.js 18 or newer for the examples.

In every example $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

  1. 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.
  2. The application stores the eventToken together with the job and answers 202 within the call window. The activity sleeps after that.
  3. 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.
  4. Once finished, the application calls POST /v1/workflows/event with the same eventToken and 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.

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
X-Vibe-Request-Id Call ID. The platform writes the same ID into its own log
X-Vibe-Caller-Kind The value external-api — the call arrived through this channel rather than from the Bitrix24 interface
X-Vibe-Caller-Portal-Id The Bitrix24 account the calling key belongs to
X-Vibe-Caller-Key-Id ID 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. The full account 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

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

Terminal
curl -X POST https://vibecode.bitrix24.com/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

javascript
await fetch('https://vibecode.bitrix24.com/v1/workflows/activity-log', {
  method: 'POST',
  headers: {
    'X-Api-Key': process.env.VIBE_API_KEY,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    eventToken,
    logMessage: 'Selection collected, computing the discount',
  }),
});
JSON
{
  "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
eventToken string yes The very token that arrived in the call body at step 1
returnValues object no Values of the activity's output parameters. Defaults to an empty object
logMessage string no An entry in the process execution log alongside the event

cURL

Terminal
curl -X POST https://vibecode.bitrix24.com/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

javascript
const res = await fetch('https://vibecode.bitrix24.com/v1/workflows/event', {
  method: 'POST',
  headers: {
    'X-Api-Key': process.env.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}`);
}
JSON
{
  "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.

The reply key

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 — the pass-through into 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
The bizproc scope 403 SCOPE_DENIED
Read-and-write mode 403 WRITE_BLOCKED_READONLY_KEY. The response carries the address of the page where the mode is switched
A bound Bitrix24 account 401 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.

Errors

The table covers both reply addresses — POST /v1/workflows/event and POST /v1/workflows/activity-log. The refusals of the pass-through call at step 1 are in the table on Application external API.

HTTP Code Description
400 MISSING_PARAMS eventToken not provided. On the log write, a request without logMessage gets the same answer
401 MISSING_API_KEY The header with the key was not provided
401 INVALID_API_KEY The key is not recognized — no such key exists on the platform
401 TOKEN_MISSING The key has no portal binding. The response text names the reason and what to do about it
403 SCOPE_DENIED The key is missing the bizproc scope. Checked first — before the key mode
403 WRITE_BLOCKED_READONLY_KEY The key is in read-only mode. The details.switchUrl field names the page where the mode is changed
403 APP_API_KEY_OUT_OF_SCOPE The reply was sent with an application external API key. Only the pass-through into the application is open to that key
403 BITRIX_ACCESS_DENIED Bitrix24 rejected the request: the token is invalid, expired, or already used
422 BITRIX_ERROR Error on the Bitrix24 side
429 RATE_LIMITED Request rate limit exceeded. Retry in 1–2 seconds
502 BITRIX_UNAVAILABLE Bitrix24 is unavailable

Full list of common API errors — Errors.

Error response example

403 — the key is in read-only mode:

JSON
{
  "success": false,
  "error": {
    "code": "WRITE_BLOCKED_READONLY_KEY",
    "message": "Key is in read-only mode. Switch to read+write in /keys to enable writes.",
    "details": {
      "method": "bizproc.event.send",
      "keyName": "Solution deploy key",
      "currentMode": "READONLY",
      "switchUrl": "/keys"
    }
  }
}

Limits

Limit Value
The application's answer window at step 1 25 seconds for the application to answer, inside an overall call limit of 30 seconds
eventToken lifetime The platform does not limit it and passes the token to Bitrix24 as is. An invalid token is rejected by Bitrix24 — 403 BITRIX_ACCESS_DENIED
Reusing an eventToken The token is single-use: every pause of the process issues a new one, and after POST /v1/workflows/event the previous one is invalid
Log entries per step The platform does not limit them. Every entry is a separate API call and counts against the common limits

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.

javascript
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 = 'https://vibecode.bitrix24.com/v1';
const VIBE_API_KEY = process.env.VIBE_API_KEY;

async function callVibe(path, body) {
  const res = await fetch(`${VIBE_URL}${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);

Known specifics

  • 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 refusal marker in returnValues rather than with silence.
  • 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 on every meaningful boundary: unpicking a stuck step from the process log is cheaper than from the application logs.
  • 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.
  • One eventToken closes one step. The token becomes invalid right after POST /v1/workflows/event, and an already-used one is rejected by Bitrix24 with 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 spoiled, and the response gives you nothing to tell the two apart.
  • 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.

See also