Goal
Read a CSV mapping of dealId,newStageId and apply the stage change to every row. Could be hundreds of deals or tens of thousands — BatchByChunkV2.make() chunks the calls into 50-per-batch blocks server-side, no manual paging.
Stack & Prerequisites
- Node.js 20+, ESM
@bitrix24/b24jssdk@^1.0.0- A webhook with the
crmscope, set asB24_HOOK. - A CSV file
migrations.csv:dealId,newStageId 1234,WON 1235,WON 1240,LOSE
pnpm add @bitrix24/b24jssdk
export B24_HOOK="https://your-portal.bitrix24.com/rest/1/xxxxxxxxxxxxxxxx/"
Full Example
scripts/migrate-deals.mjs:
import { readFile } from 'node:fs/promises'
import {
B24Hook,
EnumCrmEntityTypeId,
ParamsFactory
} from '@bitrix24/b24jssdk'
const hookUrl = process.env.B24_HOOK
if (!hookUrl) {
console.error('B24_HOOK is not set')
process.exit(2)
}
const csv = await readFile(process.argv[2] ?? 'migrations.csv', 'utf8')
const rows = csv
.split(/\r?\n/)
.slice(1) // header
.filter(Boolean)
.map((line) => {
const [dealId, newStageId] = line.split(',')
return { dealId: Number(dealId), newStageId }
})
console.log(`planned updates: ${rows.length}`)
const b24 = B24Hook.fromWebhookUrl(hookUrl, {
restrictionParams: ParamsFactory.getBatchProcessing()
})
const calls = rows.map(({ dealId, newStageId }) => [
'crm.item.update',
{
entityTypeId: EnumCrmEntityTypeId.deal,
id: dealId,
fields: { stageId: newStageId }
}
])
const response = await b24.actions.v2.batchByChunk.make({
calls,
options: {
isHaltOnError: false,
requestId: `deal-stage-migration-${Date.now()}`
}
})
if (!response.isSuccess) {
console.error('partial failure:')
for (const message of response.getErrorMessages()) {
console.error(` - ${message}`)
}
process.exit(1)
}
console.log(`updated: ${response.getData()?.length ?? 0} of ${rows.length}`)
Run It
node scripts/migrate-deals.mjs migrations.csv
# planned updates: 3450
# updated: 3450 of 3450
If a few rows fail (deleted deal, missing scope, invalid stage), isHaltOnError: false lets the rest of the batch finish and the script exits with code 1 after listing the failures.
How It Works
BatchByChunkV2.make({ calls })accepts an arbitrary number of commands and slices them internally into batches of 50, the per-request hard limit. Output order matches input order.Result.isSuccessflips tofalseif any chunk had an error.getErrorMessages()returns one message per failed call. Successful calls are still ingetData()— partial progress is preserved.ParamsFactory.getBatchProcessing()makes the client more cautious, not more permissive: it halves the request rate (drainRate2 → 1,burstLimit50 → 30) and lowers the thresholds at which it warns and starts adding delay (both 80% → 50%), so a long migration backs off earlier and keeps the portal queue responsive for live UI traffic. The operating budget itself is unchanged.- Naming the request via
options.requestIdmakes it easy to filter portal-side logs. It does not deduplicate an accidental re-run — it is a tracking tag, and the portal happily writes twice. OnrestApi:v3anidempotencyKeyis what prevents that.
Limitations
- The two-tuple
callsform is required:BatchByChunkV2deliberately rejects named-object commands because chunk boundaries would split the names apart. - Each command counts against the daily method-call budget. For tens of thousands of updates, schedule the script to run during off-peak hours and consider splitting by responsible user.
- Bitrix24's
crm.item.updatevalidatesstageIdagainst the deal's category. A typo innewStageIdreturns a per-call error — visible ingetErrorMessages()— but the whole script does not abort thanks toisHaltOnError: false. - Atomic rollback is not supported. If you need transactional behavior, dump current values into a separate CSV first and run a second migration to restore.
See also
BatchByChunkV2.make()— full method reference.BatchV2.make()— when you have ≤ 50 calls and want named results.- Restrictions System — choosing a
restrictionParamspreset.
Webhook CLI
A 30-line Node script that authenticates against a Bitrix24 portal via inbound webhook and prints the calling user — useful as the first thing you run after creating a webhook.
ERP sync
Two-way contact sync between Bitrix24 and an external ERP. Match by INN (primary) or email (fallback). Cron every hour.