[{"data":1,"prerenderedAt":2152},["ShallowReactive",2],{"navigation":3,"-docs-working-with-the-rest-api-security":491,"-docs-working-with-the-rest-api-security-description":2141},[4],{"title":5,"path":6,"stem":7,"children":8,"page":47},"Docs","\u002Fdocs","docs",[9,78,383],{"title":10,"path":11,"stem":12,"children":13,"restApiVersion":15,"category":15,"description":15,"badge":15},"Get Started","\u002Fdocs\u002Fgetting-started","docs\u002F1.getting-started\u002F1.index",[14,17,48,63],{"title":10,"path":11,"stem":12,"restApiVersion":15,"category":15,"description":16,"badge":15},null,"Quick start guide for working with the SDK for Bitrix24 REST API",{"title":18,"path":19,"stem":20,"children":21,"page":47},"Installation","\u002Fdocs\u002Fgetting-started\u002Finstallation","docs\u002F1.getting-started\u002F2.installation",[22,27,32,37,42],{"title":23,"path":24,"stem":25,"restApiVersion":15,"category":15,"description":26,"badge":15},"Vue","\u002Fdocs\u002Fgetting-started\u002Finstallation\u002Fvue","docs\u002F1.getting-started\u002F2.installation\u002F1.vue","Guide for installing Bitrix24 JS SDK in Vue applications.",{"title":28,"path":29,"stem":30,"restApiVersion":15,"category":15,"description":31,"badge":15},"Nuxt","\u002Fdocs\u002Fgetting-started\u002Finstallation\u002Fnuxt","docs\u002F1.getting-started\u002F2.installation\u002F2.nuxt","Guide for installing Bitrix24 JS SDK in Nuxt applications.",{"title":33,"path":34,"stem":35,"restApiVersion":15,"category":15,"description":36,"badge":15},"React","\u002Fdocs\u002Fgetting-started\u002Finstallation\u002Freact","docs\u002F1.getting-started\u002F2.installation\u002F3.react","Guide for installing Bitrix24 JS SDK in React applications.",{"title":38,"path":39,"stem":40,"restApiVersion":15,"category":15,"description":41,"badge":15},"Node.js","\u002Fdocs\u002Fgetting-started\u002Finstallation\u002Fnodejs","docs\u002F1.getting-started\u002F2.installation\u002F4.nodejs","Guide for installing Bitrix24 JS SDK in Node.js applications.",{"title":43,"path":44,"stem":45,"restApiVersion":15,"category":15,"description":46,"badge":15},"UMD","\u002Fdocs\u002Fgetting-started\u002Finstallation\u002Fumd","docs\u002F1.getting-started\u002F2.installation\u002F5.umd","Guide for using UMD version Bitrix24 JS SDK in you applications.",false,{"title":49,"restApiVersion":15,"category":15,"description":15,"badge":15,"shadow":47,"path":50,"stem":51,"children":52,"page":47},"Migration","\u002Fdocs\u002Fgetting-started\u002Fmigration","docs\u002F1.getting-started\u002F3.migration",[53,58],{"title":54,"path":55,"stem":56,"restApiVersion":15,"category":15,"description":57,"badge":15},"v0 to v1","\u002Fdocs\u002Fgetting-started\u002Fmigration\u002Fv1","docs\u002F1.getting-started\u002F3.migration\u002F1.v1","A comprehensive guide to migrate your application from Bitrix24 JS SDK v0 to Bitrix24 JS SDK v1.",{"title":59,"path":60,"stem":61,"restApiVersion":15,"category":15,"description":62,"badge":15},"v2 to v3","\u002Fdocs\u002Fgetting-started\u002Fmigration\u002Fv3","docs\u002F1.getting-started\u002F3.migration\u002F2.v3","How to migrate off the deprecated REST surface that is removed in Bitrix24 JS SDK 3.0.0.",{"title":64,"restApiVersion":15,"category":15,"description":15,"badge":15,"path":65,"stem":66,"children":67,"page":47},"AI Tools","\u002Fdocs\u002Fgetting-started\u002Fai","docs\u002F1.getting-started\u002F7.ai",[68,73],{"title":69,"path":70,"stem":71,"restApiVersion":15,"category":15,"description":72,"badge":15},"Skills","\u002Fdocs\u002Fgetting-started\u002Fai\u002Fskills","docs\u002F1.getting-started\u002F7.ai\u002F1.skills","Task-focused skill files for AI coding agents working with @bitrix24\u002Fb24jssdk.",{"title":74,"path":75,"stem":76,"restApiVersion":15,"category":15,"description":77,"badge":15},"LLMs.txt","\u002Fdocs\u002Fgetting-started\u002Fai\u002Fllms-txt","docs\u002F1.getting-started\u002F7.ai\u002F2.llms-txt","How to get AI tools like Cursor, Windsurf, GitHub Copilot, ChatGPT, and Claude to understand Bitrix24 JS SDK methods, tools, and best practices.",{"title":79,"path":80,"stem":81,"children":82,"restApiVersion":15,"category":15,"description":15,"badge":15},"Working with the REST API","\u002Fdocs\u002Fworking-with-the-rest-api","docs\u002F2.working-with-the-rest-api\u002F0.index",[83,86,93,98,103,107,112,116,121,126,130,135,140,145,150,155,160,165,170,175,179,184,189,194,200,205,210,215,220,225,230,235,240,245,251,256,261,267,272,277,282,287,293,299,304,310,315,320,325,330,335,341,348,353,358,363,368,373,378],{"title":84,"path":80,"stem":81,"restApiVersion":15,"category":15,"description":85,"badge":15},"Introduction","Overview of SDK capabilities for working with Bitrix24 REST API",{"title":87,"path":88,"stem":89,"restApiVersion":90,"category":91,"description":92,"badge":15},"Call","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fcall-rest-api-ver2","docs\u002F2.working-with-the-rest-api\u002F1.call-rest-api-ver2","rest-api-ver2","actions","A method for making Bitrix24 REST API version 2 calls.",{"title":87,"path":94,"stem":95,"restApiVersion":96,"category":91,"description":97,"badge":15},"\u002Fdocs\u002Fworking-with-the-rest-api\u002Fcall-rest-api-ver3","docs\u002F2.working-with-the-rest-api\u002F1.call-rest-api-ver3","rest-api-ver3","Method for making Bitrix24 REST API version 3 calls.",{"title":99,"path":100,"stem":101,"restApiVersion":90,"category":91,"description":102,"badge":15},"CallList","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fcall-list-rest-api-ver2","docs\u002F2.working-with-the-rest-api\u002F2.call-list-rest-api-ver2","Method for quickly retrieving all data from list methods of Bitrix24 REST API version 2.",{"title":99,"path":104,"stem":105,"restApiVersion":96,"category":91,"description":106,"badge":15},"\u002Fdocs\u002Fworking-with-the-rest-api\u002Fcall-list-rest-api-ver3","docs\u002F2.working-with-the-rest-api\u002F2.call-list-rest-api-ver3","Method for quickly retrieving all data from list methods of Bitrix24 REST API version 3.",{"title":108,"path":109,"stem":110,"restApiVersion":90,"category":91,"description":111,"badge":15},"FetchList","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ffetch-list-rest-api-ver2","docs\u002F2.working-with-the-rest-api\u002F2.fetch-list-rest-api-ver2","Returns an AsyncGenerator that allows processing data from list methods of Bitrix24 REST API version 2 as it is received without loading the entire array into memory at once. This is especially useful when working with very large volumes of data.",{"title":108,"path":113,"stem":114,"restApiVersion":96,"category":91,"description":115,"badge":15},"\u002Fdocs\u002Fworking-with-the-rest-api\u002Ffetch-list-rest-api-ver3","docs\u002F2.working-with-the-rest-api\u002F2.fetch-list-rest-api-ver3","Returns an AsyncGenerator that allows processing data from list methods of Bitrix24 REST API version 3 as it is received without loading the entire array into memory at once. This is especially useful when working with very large volumes of data.",{"title":117,"path":118,"stem":119,"restApiVersion":15,"category":117,"description":120,"badge":15},"B24Hook","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fhook","docs\u002F2.working-with-the-rest-api\u002F20.hook","Server-side entry point for talking to the Bitrix24 REST API through an inbound webhook (permanent token).",{"title":122,"path":123,"stem":124,"restApiVersion":90,"category":91,"description":125,"badge":15},"Batch","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fbatch-rest-api-ver2","docs\u002F2.working-with-the-rest-api\u002F3.batch-rest-api-ver2","Method for executing batch requests to Bitrix24 REST API version 2. Allows executing up to 50 commands in a single API call.",{"title":122,"path":127,"stem":128,"restApiVersion":96,"category":91,"description":129,"badge":15},"\u002Fdocs\u002Fworking-with-the-rest-api\u002Fbatch-rest-api-ver3","docs\u002F2.working-with-the-rest-api\u002F3.batch-rest-api-ver3","Method for executing batch requests to Bitrix24 REST API version 3. Allows executing up to 50 commands in a single API call.",{"title":84,"path":131,"stem":132,"restApiVersion":15,"category":133,"description":134,"badge":15},"\u002Fdocs\u002Fworking-with-the-rest-api\u002Fframe","docs\u002F2.working-with-the-rest-api\u002F30.frame","B24Frame","Designed for managing Bitrix24 applications running inside a placement iframe. Inherits from AbstractB24 and exposes managers for authentication, parent-window messaging, sliders, dialogs, placements, and options.",{"title":136,"path":137,"stem":138,"restApiVersion":15,"category":133,"description":139,"badge":15},"Initialization","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fframe-initialize-b24-frame","docs\u002F2.working-with-the-rest-api\u002F30.frame-initialize-b24-frame","Function is designed to initialize a B24Frame",{"title":141,"path":142,"stem":143,"restApiVersion":15,"category":133,"description":144,"badge":15},"Auth","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fframe-auth","docs\u002F2.working-with-the-rest-api\u002F31.frame-auth","Designed for managing authentication in Bitrix24 applications. It handles authentication data received from the parent window and provides methods for updating and retrieving this data.",{"title":146,"path":147,"stem":148,"restApiVersion":15,"category":133,"description":149,"badge":15},"Dialog","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fframe-dialog","docs\u002F2.working-with-the-rest-api\u002F31.frame-dialog","Wraps the BX24 system dialogs (user picker, access permission picker, CRM entity picker) so they can be opened from inside a Bitrix24 application iframe.",{"title":151,"path":152,"stem":153,"restApiVersion":15,"category":133,"description":154,"badge":15},"Options","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fframe-options","docs\u002F2.working-with-the-rest-api\u002F31.frame-options","Used for managing application and user settings in the Bitrix24 application. It allows initializing data, getting, and setting options through messages to the parent window.",{"title":156,"path":157,"stem":158,"restApiVersion":15,"category":133,"description":159,"badge":15},"Parent","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fframe-parent","docs\u002F2.working-with-the-rest-api\u002F31.frame-parent","Provides methods for managing the parent application window in Bitrix24, including resizing the window, managing scroll, initiating calls, and opening the messenger.",{"title":161,"path":162,"stem":163,"restApiVersion":15,"category":133,"description":164,"badge":15},"Placement","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fframe-placement","docs\u002F2.working-with-the-rest-api\u002F31.frame-placement","Used for managing the placement of widgets in the Bitrix24 application.",{"title":166,"path":167,"stem":168,"restApiVersion":15,"category":133,"description":169,"badge":15},"Slider","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fframe-slider","docs\u002F2.working-with-the-rest-api\u002F31.frame-slider","Provides methods for working with sliders in the Bitrix24 application. It allows opening and closing sliders, as well as managing their content.",{"title":171,"path":172,"stem":173,"restApiVersion":90,"category":91,"description":174,"badge":15},"BatchByChunk","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fbatch-by-chunk-rest-api-ver2","docs\u002F2.working-with-the-rest-api\u002F4.batch-by-chunk-rest-api-ver2","Method for executing batch requests with automatic chunking for any number of commands. Automatically splits large command sets into batches of 50 and executes them sequentially. Use only arrays of tuples or arrays of objects.",{"title":171,"path":176,"stem":177,"restApiVersion":96,"category":91,"description":178,"badge":15},"\u002Fdocs\u002Fworking-with-the-rest-api\u002Fbatch-by-chunk-rest-api-ver3","docs\u002F2.working-with-the-rest-api\u002F4.batch-by-chunk-rest-api-ver3","Method for executing batch requests to Bitrix24 REST API version 3 with automatic chunking for any number of commands. Automatically splits large command sets into batches of 50 and executes them sequentially. Use only arrays of tuples or arrays of objects.",{"title":180,"path":181,"stem":182,"restApiVersion":15,"category":180,"description":183,"badge":15},"B24OAuth","\u002Fdocs\u002Fworking-with-the-rest-api\u002Foauth","docs\u002F2.working-with-the-rest-api\u002F40.oauth","Server-side entry point for talking to the Bitrix24 REST API with an OAuth 2.0 access token (and automatic refresh).",{"title":185,"path":186,"stem":187,"restApiVersion":15,"category":15,"description":188,"badge":15},"Choosing the method","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fchoosing-the-right-method","docs\u002F2.working-with-the-rest-api\u002F5.choosing-the-right-method","Decision guide for picking between Call, CallList, FetchList, Batch and BatchByChunk in REST API v2 and v3.",{"title":190,"path":191,"stem":192,"restApiVersion":15,"category":15,"description":193,"badge":15},"Filtering","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ffiltering","docs\u002F2.working-with-the-rest-api\u002F5.filtering","Reference for building filter parameters in Bitrix24 REST API v2 (prefix operators) and v3 (array-of-triples), including date formatting and the order-stripping rule.",{"title":195,"path":196,"stem":197,"restApiVersion":15,"category":198,"description":199,"badge":15},"B24HelperManager","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fhelper","docs\u002F2.working-with-the-rest-api\u002F50.helper","Helper","Aggregate manager that loads profile, app, payment, license, currency, and options data with one batched REST call, plus wires the Pull client.",{"title":201,"path":202,"stem":203,"restApiVersion":15,"category":198,"description":204,"badge":15},"useB24Helper","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fhelper-use-b24-helper","docs\u002F2.working-with-the-rest-api\u002F51.helper-use-b24-helper","Closure-based composable that owns a single B24HelperManager instance and exposes lifecycle helpers (init \u002F destroy \u002F Pull client glue).",{"title":206,"path":207,"stem":208,"restApiVersion":15,"category":198,"description":209,"badge":15},"ProfileManager","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fhelper-profile-manager","docs\u002F2.working-with-the-rest-api\u002F52.helper-profile-manager","Parsed `profile` REST response: id, name, last name, gender, photo, time zone, admin flag.",{"title":211,"path":212,"stem":213,"restApiVersion":15,"category":198,"description":214,"badge":15},"AppManager","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fhelper-app-manager","docs\u002F2.working-with-the-rest-api\u002F53.helper-app-manager","Parsed `app.info` REST response: app id, code, version, status, install flag.",{"title":216,"path":217,"stem":218,"restApiVersion":15,"category":198,"description":219,"badge":15},"PaymentManager","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fhelper-payment-manager","docs\u002F2.working-with-the-rest-api\u002F54.helper-payment-manager","Payment status of a Bitrix24 application — extracted from `app.info`.",{"title":221,"path":222,"stem":223,"restApiVersion":15,"category":198,"description":224,"badge":15},"LicenseManager","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fhelper-license-manager","docs\u002F2.working-with-the-rest-api\u002F55.helper-license-manager","Bitrix24 license info from `app.info` — and an automatic restriction-manager retune for the matching tariff plan.",{"title":226,"path":227,"stem":228,"restApiVersion":15,"category":198,"description":229,"badge":15},"CurrencyManager","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fhelper-currency-manager","docs\u002F2.working-with-the-rest-api\u002F56.helper-currency-manager","Currency catalogue cached in memory — base currency, per-currency formatters, and number-to-string formatter.",{"title":231,"path":232,"stem":233,"restApiVersion":15,"category":198,"description":234,"badge":15},"OptionsManager","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fhelper-options-manager","docs\u002F2.working-with-the-rest-api\u002F57.helper-options-manager","Typed access to `app.option.get` \u002F `user.option.get`, plus a save() that batches `*.option.set` with an optional Pull notification.",{"title":236,"path":237,"stem":238,"restApiVersion":15,"category":15,"description":239,"badge":15},"Errors","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ferrors","docs\u002F2.working-with-the-rest-api\u002F6.errors","Reference for SdkError and AjaxError codes raised by the SDK, plus the Bitrix24 REST error codes that surface through them.",{"title":241,"path":242,"stem":243,"restApiVersion":15,"category":241,"description":244,"badge":15},"Pull","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fpull","docs\u002F2.working-with-the-rest-api\u002F60.pull","Real-time message stream from Bitrix24 to your application — WebSocket primary, long-polling fallback. Frame-only.",{"title":246,"path":247,"stem":248,"restApiVersion":15,"category":249,"description":250,"badge":15},"Logger","\u002Fdocs\u002Fworking-with-the-rest-api\u002Flogger","docs\u002F2.working-with-the-rest-api\u002F66.logger","logger","Logger inspired by PHP Monolog, provides a structured logging system with support for channels, handlers, processors, and formatters. Implementation follows PSR-3 principles and the chain of responsibility pattern.",{"title":252,"path":253,"stem":254,"restApiVersion":15,"category":249,"description":255,"badge":15},"Telegram","\u002Fdocs\u002Fworking-with-the-rest-api\u002Flogger-telegram","docs\u002F2.working-with-the-rest-api\u002F66.logger-telegram","Sending logs to Telegram",{"title":257,"path":258,"stem":259,"restApiVersion":15,"category":15,"description":260,"badge":15},"Discovering v3 methods","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fdiscovering-v3-methods","docs\u002F2.working-with-the-rest-api\u002F7.discovering-v3-methods","Use rest.documentation.openapi to fetch the portal's own machine-readable list of every available REST API v3 method — the source of truth the SDK relies on instead of a hardcoded allowlist. Especially useful for AI agents and codegen.",{"title":262,"path":263,"stem":264,"restApiVersion":15,"category":265,"description":266,"badge":15},"AjaxResult","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fcore-ajax-result","docs\u002F2.working-with-the-rest-api\u002F70.core-ajax-result","Core","Specialised Result returned by every REST helper. Provides `isMore() \u002F getNext() \u002F getTotal() \u002F getStatus()` for paged responses, and immutable data.",{"title":268,"path":269,"stem":270,"restApiVersion":15,"category":265,"description":271,"badge":15},"Http","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fcore-http","docs\u002F2.working-with-the-rest-api\u002F70.core-http","Low-level transport interface returned by `b24.getHttpClient(version)`. Direct access to the axios-based clients, restriction params, statistics, and reset.",{"title":273,"path":274,"stem":275,"restApiVersion":15,"category":265,"description":276,"badge":15},"B24LangList","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fcore-lang-list","docs\u002F2.working-with-the-rest-api\u002F70.core-lang-list","Enum of Bitrix24 cloud interface languages plus the BCP-47 locale map.",{"title":278,"path":279,"stem":280,"restApiVersion":15,"category":265,"description":281,"badge":15},"RequestIdGenerator","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fcore-request-id-generator","docs\u002F2.working-with-the-rest-api\u002F70.core-request-id-generator","Default `IRequestIdGenerator` implementation. Generates UUID v7 request ids and exposes the header \u002F query parameter names used by the SDK.",{"title":283,"path":284,"stem":285,"restApiVersion":15,"category":265,"description":286,"badge":15},"Result","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fcore-result","docs\u002F2.working-with-the-rest-api\u002F70.core-result","Generic operation result with success flag, data, and an error map. Analogue of \\\\Bitrix\\\\Main\\\\Result from the Bitrix Framework.",{"title":288,"path":289,"stem":290,"restApiVersion":15,"category":291,"description":292,"badge":15},"Limiters","\u002Fdocs\u002Fworking-with-the-rest-api\u002Flimiters","docs\u002F2.working-with-the-rest-api\u002F77.limiters","limiters","The restrictions system provides a comprehensive mechanism for managing request frequency, operation execution time, and adaptive delays.",{"title":294,"path":295,"stem":296,"restApiVersion":15,"category":297,"description":298,"badge":15},"Logging & Redaction","\u002Fdocs\u002Fworking-with-the-rest-api\u002Flogging","docs\u002F2.working-with-the-rest-api\u002F78.logging","logging","What the SDK redacts automatically before any request information enters the logger or an AjaxError, what it does not redact, and how to wire a custom logger via setLogger(...) without re-introducing credential leaks.",{"title":300,"path":301,"stem":302,"restApiVersion":15,"category":15,"description":303,"badge":15},"Security","\u002Fdocs\u002Fworking-with-the-rest-api\u002Fsecurity","docs\u002F2.working-with-the-rest-api\u002F79.security","Two hardening patterns for any server that receives Bitrix24 outbound events or OAuth install\u002Funinstall callbacks: reply 200 before you verify, and compare application_token in constant time.",{"title":305,"path":306,"stem":307,"restApiVersion":15,"category":308,"description":309,"badge":15},"Browser","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftools-browser","docs\u002F2.working-with-the-rest-api\u002F80.tools-browser","tools","Cheap user-agent \u002F platform \u002F capability detector. Useful for runtime branches in front-end code.",{"title":311,"path":312,"stem":313,"restApiVersion":15,"category":308,"description":314,"badge":15},"Environment","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftools-environment","docs\u002F2.working-with-the-rest-api\u002F80.tools-environment","Runtime environment detection — `getEnvironment()` and the `Environment` enum for branching between browser and Node.js code paths.",{"title":316,"path":317,"stem":318,"restApiVersion":15,"category":308,"description":319,"badge":15},"Object helpers","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftools-object-helpers","docs\u002F2.working-with-the-rest-api\u002F80.tools-object-helpers","Tiny, dependency-free object and enum helpers — `pick`, `omit`, `getEnumValue`, and the `isArrayOfArray` type guard.",{"title":321,"path":322,"stem":323,"restApiVersion":15,"category":308,"description":324,"badge":15},"Text","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftools-text","docs\u002F2.working-with-the-rest-api\u002F80.tools-text","Text and date utilities — UUID v4 \u002F v7, encode \u002F decode HTML entities, type conversions, case helpers, Luxon-backed `toDateTime` \u002F `toB24Format`, number formatting, and query-string builder.",{"title":326,"path":327,"stem":328,"restApiVersion":15,"category":308,"description":329,"badge":15},"Type","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftools-type","docs\u002F2.working-with-the-rest-api\u002F80.tools-type","Runtime type guards — strings, numbers, plain objects, arrays, DOM nodes, Blobs, FormData, JSON-RPC message shapes.",{"title":331,"path":332,"stem":333,"restApiVersion":15,"category":308,"description":334,"badge":15},"useFormatter","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftools-use-formatters","docs\u002F2.working-with-the-rest-api\u002F80.tools-use-formatters","Composable that returns shared `FormatterNumbers` and `FormatterIban` instances pre-configured with country specifications.",{"title":336,"path":337,"stem":338,"restApiVersion":15,"category":339,"description":340,"badge":15},"Common","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftypes-common","docs\u002F2.working-with-the-rest-api\u002F90.types-common","Types","Foundational building blocks used across the SDK — scalar string aliases, field-metadata shapes (Fields, MultiField, UserFieldType), and the DataType enum.",{"title":342,"path":343,"stem":344,"children":345,"restApiVersion":15,"category":339,"description":347,"badge":15},"Types overview","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftypes-index","docs\u002F2.working-with-the-rest-api\u002F90.types-index",[346],{"title":342,"path":343,"stem":344,"restApiVersion":15,"category":339,"description":347,"badge":15},"Navigable map of every types\u002F* module in the SDK — which have a dedicated page, which are surfaced through another page, and which are follow-ups.",{"title":349,"path":350,"stem":351,"restApiVersion":15,"category":339,"description":352,"badge":15},"IResult","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftypes-iresult","docs\u002F2.working-with-the-rest-api\u002F90.types-iresult","Generic operation-result interface. Implemented by `Result\u003CT>` and (transitively) by `AjaxResult\u003CT>`.",{"title":354,"path":355,"stem":356,"restApiVersion":15,"category":339,"description":357,"badge":15},"Payloads","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftypes-payloads","docs\u002F2.working-with-the-rest-api\u002F90.types-payloads","The REST response envelope — PayloadTime, GetPayload, ListPayload, BatchPayload, SuccessPayload, and the Payload union, with a note on the v2 vs v3 envelope status.",{"title":359,"path":360,"stem":361,"restApiVersion":15,"category":339,"description":362,"badge":15},"TypeB24","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftypes-type-b24","docs\u002F2.working-with-the-rest-api\u002F90.types-type-b24","Public contract implemented by every entry-point class (B24Frame, B24Hook, B24OAuth). Single source of truth for the SDK surface.",{"title":364,"path":365,"stem":366,"restApiVersion":15,"category":265,"description":367,"badge":15},"Telemetry","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftelemetry","docs\u002F2.working-with-the-rest-api\u002F95.telemetry","Request id, SDK version, and SDK type query parameters added to every Bitrix24 REST call.",{"title":369,"path":370,"stem":371,"restApiVersion":15,"category":265,"description":372,"badge":15},"Error Codes","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ferror-codes","docs\u002F2.working-with-the-rest-api\u002F96.error-codes","Catalogue of error codes the SDK recognises — split into \"hard\" (thrown) and \"soft\" (returned as AjaxError inside AjaxResult).",{"title":374,"path":375,"stem":376,"restApiVersion":15,"category":308,"description":377,"badge":15},"HealthCheck","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftools-health-check","docs\u002F2.working-with-the-rest-api\u002Ftools-health-check","Method for checking the availability of Bitrix24 REST API. Performs a simple request to the REST API to verify service health.",{"title":379,"path":380,"stem":381,"restApiVersion":15,"category":308,"description":382,"badge":15},"Ping","\u002Fdocs\u002Fworking-with-the-rest-api\u002Ftools-ping","docs\u002F2.working-with-the-rest-api\u002Ftools-ping","Method for measuring Bitrix24 REST API response speed. Performs a test request and returns response time in milliseconds.",{"title":384,"path":385,"stem":386,"children":387,"restApiVersion":15,"category":15,"description":15,"badge":15},"Examples","\u002Fdocs\u002Fexamples","docs\u002F99.examples\u002F0.index",[388,390,396,401,406,411,416,421,426,431,436,441,446,451,456,461,466,471,476,481,486],{"title":84,"path":385,"stem":386,"restApiVersion":15,"category":15,"description":389,"badge":15},"Recipes that pair the SDK with realistic scenarios — CRM analytics, mass messaging, AI assistants, OAuth installs, and more. Each recipe is a single, copy-paste-friendly file.",{"title":391,"path":392,"stem":393,"restApiVersion":15,"category":394,"description":395,"badge":15},"CRM analytics","\u002Fdocs\u002Fexamples\u002Fcrm-analytics","docs\u002F99.examples\u002F1.crm-analytics","examples","Stream all deals via actions.v2.fetchList.make, group by stage, print a funnel report (counts, conversion %, avg ticket, win rate).",{"title":397,"path":398,"stem":399,"restApiVersion":15,"category":394,"description":400,"badge":15},"Deals → CSV","\u002Fdocs\u002Fexamples\u002Fdashboard-deals-csv","docs\u002F99.examples\u002F1.dashboard-deals-csv","Stream every CRM deal that matches a date filter into a CSV file using a webhook and FetchListV2.",{"title":402,"path":403,"stem":404,"restApiVersion":15,"category":15,"description":405,"badge":15},"Entity List","\u002Fdocs\u002Fexamples\u002Fentity-list","docs\u002F99.examples\u002F10.entity-list","Render a paged Bitrix24 entity list with B24UI components and the SDK callList action.",{"title":407,"path":408,"stem":409,"restApiVersion":15,"category":394,"description":410,"badge":15},"Error handling","\u002Fdocs\u002Fexamples\u002Ferror-handling","docs\u002F99.examples\u002F10.error-handling","Demonstrates the four error layers (SdkError \u002F AjaxError \u002F network \u002F soft) and the hardErrorCodes \u002F softErrorCodes \u002F retryOnNetworkError knobs on the restriction manager.",{"title":412,"path":413,"stem":414,"restApiVersion":15,"category":394,"description":415,"badge":15},"Event registration","\u002Fdocs\u002Fexamples\u002Fevent-registration","docs\u002F99.examples\u002F11.event-registration","CLI tool — list, bind, and unbind Bitrix24 outbound webhook events via event.get \u002F event.bind \u002F event.unbind. Pairs with the webhook handler recipe.",{"title":417,"path":418,"stem":419,"restApiVersion":15,"category":394,"description":420,"badge":15},"OAuth install","\u002Fdocs\u002Fexamples\u002Foauth-install","docs\u002F99.examples\u002F12.oauth-install","Express server that handles ONAPPINSTALL \u002F ONAPPUPDATE \u002F ONAPPUNINSTALL events for a Bitrix24 marketplace app, persists tokens per portal, and builds a B24OAuth client on demand with a refresh callback wired to storage.",{"title":422,"path":423,"stem":424,"restApiVersion":15,"category":394,"description":425,"badge":15},"Frame app skeleton","\u002Fdocs\u002Fexamples\u002Fframe-app-skeleton","docs\u002F99.examples\u002F2.frame-app-skeleton","Minimum viable Bitrix24 iframe app: init handshake, set title, persist app state, open a slider, close cleanly.",{"title":427,"path":428,"stem":429,"restApiVersion":15,"category":394,"description":430,"badge":15},"Mass messaging","\u002Fdocs\u002Fexamples\u002Fmass-messaging","docs\u002F99.examples\u002F2.mass-messaging","Filter contacts in CRM, personalise a template, send IM notifications to assigned managers.",{"title":432,"path":433,"stem":434,"restApiVersion":15,"category":15,"description":435,"badge":15},"Installation Wizard","\u002Fdocs\u002Fexamples\u002Fapp-installation-wizard","docs\u002F99.examples\u002F20.app-installation-wizard","Multi-step install flow that creates user fields, registers placements, finalises the installation, and celebrates with confetti.",{"title":437,"path":438,"stem":439,"restApiVersion":15,"category":394,"description":440,"badge":15},"Task automation","\u002Fdocs\u002Fexamples\u002Ftask-automation","docs\u002F99.examples\u002F3.task-automation","Poll deal stages every 60 s. When a watched transition fires, create a task with description, deadline and priority.",{"title":442,"path":443,"stem":444,"restApiVersion":15,"category":394,"description":445,"badge":15},"Webhook CLI","\u002Fdocs\u002Fexamples\u002Fwebhook-cli-node","docs\u002F99.examples\u002F3.webhook-cli-node","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.",{"title":447,"path":448,"stem":449,"restApiVersion":15,"category":15,"description":450,"badge":15},"Node + Hook Export","\u002Fdocs\u002Fexamples\u002Fnode-hook-company-export","docs\u002F99.examples\u002F30.node-hook-company-export","Export Bitrix24 companies into a CSV file from a pure Node.js script using B24Hook.",{"title":452,"path":453,"stem":454,"restApiVersion":15,"category":394,"description":455,"badge":15},"Bulk update","\u002Fdocs\u002Fexamples\u002Fbulk-update-deals","docs\u002F99.examples\u002F4.bulk-update-deals","Migrate thousands of CRM deals to a new stage using BatchByChunkV2 — automatic chunking, partial-error handling, and a single progress line.",{"title":457,"path":458,"stem":459,"restApiVersion":15,"category":394,"description":460,"badge":15},"ERP sync","\u002Fdocs\u002Fexamples\u002Ferp-sync","docs\u002F99.examples\u002F4.erp-sync","Two-way contact sync between Bitrix24 and an external ERP. Match by INN (primary) or email (fallback). Cron every hour.",{"title":462,"path":463,"stem":464,"restApiVersion":15,"category":394,"description":465,"badge":15},"Disk files","\u002Fdocs\u002Fexamples\u002Fdisk-files","docs\u002F99.examples\u002F5.disk-files","List storages and folder children, create subfolders, inspect files. File upload (multipart) is intentionally out of scope.",{"title":467,"path":468,"stem":469,"restApiVersion":15,"category":394,"description":470,"badge":15},"Pull subscribe","\u002Fdocs\u002Fexamples\u002Fpull-subscribe-frame","docs\u002F99.examples\u002F5.pull-subscribe-frame","Open a live channel from a Bitrix24 frame app and react to push events using useB24Helper + the Pull client.",{"title":472,"path":473,"stem":474,"restApiVersion":15,"category":394,"description":475,"badge":15},"Telegram bot","\u002Fdocs\u002Fexamples\u002Ftelegram-bot","docs\u002F99.examples\u002F6.telegram-bot","Poll new CRM deals every two minutes; notify a Telegram chat with an HTML-formatted card.",{"title":477,"path":478,"stem":479,"restApiVersion":15,"category":394,"description":480,"badge":15},"Webhook handler","\u002Fdocs\u002Fexamples\u002Fwebhook-handler","docs\u002F99.examples\u002F7.webhook-handler","Express server that receives Bitrix24 outbound events, fetches details via REST, dispatches by event name. Always returns 200.",{"title":482,"path":483,"stem":484,"restApiVersion":15,"category":394,"description":485,"badge":15},"AI assistant","\u002Fdocs\u002Fexamples\u002Fai-assistant","docs\u002F99.examples\u002F8.ai-assistant","Load a deal and its activities, ask GPT-4o for the next-best action, create a task bound to the deal with priority and deadline.",{"title":487,"path":488,"stem":489,"restApiVersion":15,"category":394,"description":490,"badge":15},"Web search + LLM","\u002Fdocs\u002Fexamples\u002Fweb-search-llm","docs\u002F99.examples\u002F9.web-search-llm","Two-step RAG: ask any web-search provider, run the result through your LLM with [N] citations, post the answer as a CRM timeline comment on a deal.",{"id":492,"title":493,"audited":494,"badge":15,"body":495,"category":15,"cookbookOrder":15,"description":303,"extension":2128,"featured":47,"links":2129,"meta":2137,"navigation":2138,"path":301,"restApiVersion":15,"scopes":15,"seo":2139,"stack":15,"stem":302,"__hash__":2140},"docs\u002Fdocs\u002F2.working-with-the-rest-api\u002F79.security.md","Security for event-receiver apps","2026-07-02",{"type":496,"value":497,"toc":2119},"minimark",[498,503,512,562,569,584,588,619,636,939,1175,1189,1193,1202,1225,1564,1574,1579,1600,1981,1985,1993,2049,2054,2080,2084,2115],[499,500,502],"h2",{"id":501},"overview","Overview",[504,505,506,507,511],"p",{},"This page is for servers that expose a ",[508,509,510],"strong",{},"public HTTP endpoint Bitrix24 POSTs to",":",[513,514,515,541],"ul",{},[516,517,518,521,522,526,527,530,531,534,535,540],"li",{},[508,519,520],{},"Outbound event receivers"," — an app that registers events with ",[523,524,525],"code",{},"event.bind"," (",[523,528,529],{},"ONCRMDEALADD",", ",[523,532,533],{},"ONCRMDEALUPDATE",", …) and processes them, as in the ",[536,537,539],"a",{"href":538},"\u002Fdocs\u002Fexamples\u002Fwebhook-handler\u002F","webhook handler recipe",".",[516,542,543,546,547,550,551,550,554,557,558,540],{},[508,544,545],{},"OAuth apps"," — a marketplace app that handles ",[523,548,549],{},"ONAPPINSTALL"," \u002F ",[523,552,553],{},"ONAPPUPDATE",[523,555,556],{},"ONAPPUNINSTALL"," callbacks and stores per-portal credentials, as in the ",[536,559,561],{"href":560},"\u002Fdocs\u002Fexamples\u002Foauth-install\u002F","OAuth install recipe",[504,563,564,565,568],{},"Both share the same threat model: the URL is reachable by anyone, and Bitrix24 authenticates each delivery with an ",[523,566,567],{},"application_token"," in the payload. Two patterns keep these endpoints correct and safe. They are independent of the SDK transport surface — they live in your HTTP handler — so they are documented here rather than on an action page.",[570,571,572],"note",{},[504,573,574,575,579,580,583],{},"Credential redaction inside the SDK is a separate concern — see ",[536,576,578],{"href":577},"\u002Fdocs\u002Fworking-with-the-rest-api\u002Flogging\u002F","Logging & Credential Redaction",". This page is about the ",[508,581,582],{},"request-handling"," layer around the SDK.",[499,585,587],{"id":586},"pattern-1-reply-200-first-verify-after","Pattern 1 — Reply 200 first, verify after",[504,589,590,591,594,595,599,600,603,604,607,608,614,615,618],{},"Bitrix24 doesn't call your handler in real time — it queues events on a separate server and ",[508,592,593],{},"watches how fast your handler answers",". A handler that runs verification or processing ",[596,597,598],"em",{},"before"," replying answers slowly, and Bitrix24 starts delivering to it with ",[508,601,602],{},"lower priority"," (longer pauses between calls). And there is ",[508,605,606],{},"no automatic retry",": if your handler errors or times out, Bitrix24 records the failure and never re-sends that event — \"no second chance\" (see the ",[536,609,613],{"href":610,"rel":611},"https:\u002F\u002Fapidocs.bitrix24.com\u002Fapi-reference\u002Fevents\u002Findex.html",[612],"nofollow","events overview","). So a slow-or-throwing handler both degrades your delivery rate ",[508,616,617],{},"and"," silently drops events.",[504,620,621,622,625,626,629,630,635],{},"The fix is ordering: send ",[523,623,624],{},"2xx"," ",[508,627,628],{},"first",", then verify and process. The acknowledgement goes out fast (keeping you off the slow-handler penalty), and verification\u002Fprocessing no longer block it. If losing an event is unacceptable, don't rely on live delivery — use ",[536,631,634],{"href":632,"rel":633},"https:\u002F\u002Fapidocs.bitrix24.com\u002Fapi-reference\u002Fevents\u002Foffline-events.html",[612],"offline events"," as the durable channel.",[637,638,643],"pre",{"className":639,"code":640,"language":641,"meta":642,"style":642},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","import { type Request, type Response } from 'express'\n\n\u002F\u002F Stand-ins for your real verification \u002F processing logic.\nfunction verifyOrigin(req: Request): boolean { return Boolean(req.body?.auth) }\nasync function handleEvent(payload: unknown): Promise\u003Cvoid> { \u002F* … *\u002F }\n\n\u002F\u002F WRONG — verify + process run BEFORE the 200 reply.\nasync function onWebhookWrong(req: Request, res: Response) {\n  if (!verifyOrigin(req)) return  \u002F\u002F may be slow, may throw\n  await handleEvent(req.body)     \u002F\u002F if this throws, the next line never runs…\n  res.sendStatus(200)             \u002F\u002F …so the ack never goes out — the event is\n                                  \u002F\u002F lost (no retry) and the slow path gets you\n                                  \u002F\u002F deprioritized\n}\n","ts","",[523,644,645,689,696,703,762,806,811,817,850,877,899,922,928,934],{"__ignoreMap":642},[646,647,650,654,658,661,665,668,670,673,676,679,682,686],"span",{"class":648,"line":649},"line",1,[646,651,653],{"class":652},"s7zQu","import",[646,655,657],{"class":656},"sMK4o"," {",[646,659,660],{"class":652}," type",[646,662,664],{"class":663},"sTEyZ"," Request",[646,666,667],{"class":656},",",[646,669,660],{"class":652},[646,671,672],{"class":663}," Response",[646,674,675],{"class":656}," }",[646,677,678],{"class":652}," from",[646,680,681],{"class":656}," '",[646,683,685],{"class":684},"sfazB","express",[646,687,688],{"class":656},"'\n",[646,690,692],{"class":648,"line":691},2,[646,693,695],{"emptyLinePlaceholder":694},true,"\n",[646,697,699],{"class":648,"line":698},3,[646,700,702],{"class":701},"sHwdD","\u002F\u002F Stand-ins for your real verification \u002F processing logic.\n",[646,704,706,710,714,717,721,723,726,729,732,734,737,740,743,745,747,750,753,756,759],{"class":648,"line":705},4,[646,707,709],{"class":708},"spNyl","function",[646,711,713],{"class":712},"s2Zo4"," verifyOrigin",[646,715,716],{"class":656},"(",[646,718,720],{"class":719},"sHdIc","req",[646,722,511],{"class":656},[646,724,664],{"class":725},"sBMFI",[646,727,728],{"class":656},"):",[646,730,731],{"class":725}," boolean",[646,733,657],{"class":656},[646,735,736],{"class":652}," return",[646,738,739],{"class":712}," Boolean",[646,741,716],{"class":742},"swJcz",[646,744,720],{"class":663},[646,746,540],{"class":656},[646,748,749],{"class":663},"body",[646,751,752],{"class":656},"?.",[646,754,755],{"class":663},"auth",[646,757,758],{"class":742},") ",[646,760,761],{"class":656},"}\n",[646,763,765,768,771,774,776,779,781,784,786,789,792,795,798,800,803],{"class":648,"line":764},5,[646,766,767],{"class":708},"async",[646,769,770],{"class":708}," function",[646,772,773],{"class":712}," handleEvent",[646,775,716],{"class":656},[646,777,778],{"class":719},"payload",[646,780,511],{"class":656},[646,782,783],{"class":725}," unknown",[646,785,728],{"class":656},[646,787,788],{"class":725}," Promise",[646,790,791],{"class":656},"\u003C",[646,793,794],{"class":725},"void",[646,796,797],{"class":656},">",[646,799,657],{"class":656},[646,801,802],{"class":701}," \u002F* … *\u002F",[646,804,805],{"class":656}," }\n",[646,807,809],{"class":648,"line":808},6,[646,810,695],{"emptyLinePlaceholder":694},[646,812,814],{"class":648,"line":813},7,[646,815,816],{"class":701},"\u002F\u002F WRONG — verify + process run BEFORE the 200 reply.\n",[646,818,820,822,824,827,829,831,833,835,837,840,842,844,847],{"class":648,"line":819},8,[646,821,767],{"class":708},[646,823,770],{"class":708},[646,825,826],{"class":712}," onWebhookWrong",[646,828,716],{"class":656},[646,830,720],{"class":719},[646,832,511],{"class":656},[646,834,664],{"class":725},[646,836,667],{"class":656},[646,838,839],{"class":719}," res",[646,841,511],{"class":656},[646,843,672],{"class":725},[646,845,846],{"class":656},")",[646,848,849],{"class":656}," {\n",[646,851,853,856,858,861,864,866,868,871,874],{"class":648,"line":852},9,[646,854,855],{"class":652},"  if",[646,857,526],{"class":742},[646,859,860],{"class":656},"!",[646,862,863],{"class":712},"verifyOrigin",[646,865,716],{"class":742},[646,867,720],{"class":663},[646,869,870],{"class":742},")) ",[646,872,873],{"class":652},"return",[646,875,876],{"class":701},"  \u002F\u002F may be slow, may throw\n",[646,878,880,883,885,887,889,891,893,896],{"class":648,"line":879},10,[646,881,882],{"class":652},"  await",[646,884,773],{"class":712},[646,886,716],{"class":742},[646,888,720],{"class":663},[646,890,540],{"class":656},[646,892,749],{"class":663},[646,894,895],{"class":742},")     ",[646,897,898],{"class":701},"\u002F\u002F if this throws, the next line never runs…\n",[646,900,902,905,907,910,912,916,919],{"class":648,"line":901},11,[646,903,904],{"class":663},"  res",[646,906,540],{"class":656},[646,908,909],{"class":712},"sendStatus",[646,911,716],{"class":742},[646,913,915],{"class":914},"sbssI","200",[646,917,918],{"class":742},")             ",[646,920,921],{"class":701},"\u002F\u002F …so the ack never goes out — the event is\n",[646,923,925],{"class":648,"line":924},12,[646,926,927],{"class":701},"                                  \u002F\u002F lost (no retry) and the slow path gets you\n",[646,929,931],{"class":648,"line":930},13,[646,932,933],{"class":701},"                                  \u002F\u002F deprioritized\n",[646,935,937],{"class":648,"line":936},14,[646,938,761],{"class":656},[637,940,942],{"className":639,"code":941,"language":641,"meta":642,"style":642},"import { type Request, type Response } from 'express'\n\nfunction verifyOrigin(req: Request): boolean { return Boolean(req.body?.auth) }\nasync function handleEvent(payload: unknown): Promise\u003Cvoid> { \u002F* … *\u002F }\n\n\u002F\u002F CORRECT — acknowledge first, verify + process after.\nasync function onWebhook(req: Request, res: Response) {\n  const payload = req.body\n\n  res.sendStatus(200)             \u002F\u002F 1. reply immediately — fast ack avoids the\n                                  \u002F\u002F    slow-handler penalty\n\n  if (!verifyOrigin(req)) return  \u002F\u002F 2. drop spoofed \u002F stale events (already acked)\n  await handleEvent(payload)      \u002F\u002F 3. safe: Bitrix24 already has its 200\n}\n",[523,943,944,970,974,1014,1046,1050,1055,1084,1103,1107,1124,1129,1133,1154,1170],{"__ignoreMap":642},[646,945,946,948,950,952,954,956,958,960,962,964,966,968],{"class":648,"line":649},[646,947,653],{"class":652},[646,949,657],{"class":656},[646,951,660],{"class":652},[646,953,664],{"class":663},[646,955,667],{"class":656},[646,957,660],{"class":652},[646,959,672],{"class":663},[646,961,675],{"class":656},[646,963,678],{"class":652},[646,965,681],{"class":656},[646,967,685],{"class":684},[646,969,688],{"class":656},[646,971,972],{"class":648,"line":691},[646,973,695],{"emptyLinePlaceholder":694},[646,975,976,978,980,982,984,986,988,990,992,994,996,998,1000,1002,1004,1006,1008,1010,1012],{"class":648,"line":698},[646,977,709],{"class":708},[646,979,713],{"class":712},[646,981,716],{"class":656},[646,983,720],{"class":719},[646,985,511],{"class":656},[646,987,664],{"class":725},[646,989,728],{"class":656},[646,991,731],{"class":725},[646,993,657],{"class":656},[646,995,736],{"class":652},[646,997,739],{"class":712},[646,999,716],{"class":742},[646,1001,720],{"class":663},[646,1003,540],{"class":656},[646,1005,749],{"class":663},[646,1007,752],{"class":656},[646,1009,755],{"class":663},[646,1011,758],{"class":742},[646,1013,761],{"class":656},[646,1015,1016,1018,1020,1022,1024,1026,1028,1030,1032,1034,1036,1038,1040,1042,1044],{"class":648,"line":705},[646,1017,767],{"class":708},[646,1019,770],{"class":708},[646,1021,773],{"class":712},[646,1023,716],{"class":656},[646,1025,778],{"class":719},[646,1027,511],{"class":656},[646,1029,783],{"class":725},[646,1031,728],{"class":656},[646,1033,788],{"class":725},[646,1035,791],{"class":656},[646,1037,794],{"class":725},[646,1039,797],{"class":656},[646,1041,657],{"class":656},[646,1043,802],{"class":701},[646,1045,805],{"class":656},[646,1047,1048],{"class":648,"line":764},[646,1049,695],{"emptyLinePlaceholder":694},[646,1051,1052],{"class":648,"line":808},[646,1053,1054],{"class":701},"\u002F\u002F CORRECT — acknowledge first, verify + process after.\n",[646,1056,1057,1059,1061,1064,1066,1068,1070,1072,1074,1076,1078,1080,1082],{"class":648,"line":813},[646,1058,767],{"class":708},[646,1060,770],{"class":708},[646,1062,1063],{"class":712}," onWebhook",[646,1065,716],{"class":656},[646,1067,720],{"class":719},[646,1069,511],{"class":656},[646,1071,664],{"class":725},[646,1073,667],{"class":656},[646,1075,839],{"class":719},[646,1077,511],{"class":656},[646,1079,672],{"class":725},[646,1081,846],{"class":656},[646,1083,849],{"class":656},[646,1085,1086,1089,1092,1095,1098,1100],{"class":648,"line":819},[646,1087,1088],{"class":708},"  const",[646,1090,1091],{"class":663}," payload",[646,1093,1094],{"class":656}," =",[646,1096,1097],{"class":663}," req",[646,1099,540],{"class":656},[646,1101,1102],{"class":663},"body\n",[646,1104,1105],{"class":648,"line":852},[646,1106,695],{"emptyLinePlaceholder":694},[646,1108,1109,1111,1113,1115,1117,1119,1121],{"class":648,"line":879},[646,1110,904],{"class":663},[646,1112,540],{"class":656},[646,1114,909],{"class":712},[646,1116,716],{"class":742},[646,1118,915],{"class":914},[646,1120,918],{"class":742},[646,1122,1123],{"class":701},"\u002F\u002F 1. reply immediately — fast ack avoids the\n",[646,1125,1126],{"class":648,"line":901},[646,1127,1128],{"class":701},"                                  \u002F\u002F    slow-handler penalty\n",[646,1130,1131],{"class":648,"line":924},[646,1132,695],{"emptyLinePlaceholder":694},[646,1134,1135,1137,1139,1141,1143,1145,1147,1149,1151],{"class":648,"line":930},[646,1136,855],{"class":652},[646,1138,526],{"class":742},[646,1140,860],{"class":656},[646,1142,863],{"class":712},[646,1144,716],{"class":742},[646,1146,720],{"class":663},[646,1148,870],{"class":742},[646,1150,873],{"class":652},[646,1152,1153],{"class":701},"  \u002F\u002F 2. drop spoofed \u002F stale events (already acked)\n",[646,1155,1156,1158,1160,1162,1164,1167],{"class":648,"line":936},[646,1157,882],{"class":652},[646,1159,773],{"class":712},[646,1161,716],{"class":742},[646,1163,778],{"class":663},[646,1165,1166],{"class":742},")      ",[646,1168,1169],{"class":701},"\u002F\u002F 3. safe: Bitrix24 already has its 200\n",[646,1171,1173],{"class":648,"line":1172},15,[646,1174,761],{"class":656},[504,1176,1177,1178,1180,1181,1184,1185,1188],{},"The ",[536,1179,539],{"href":538}," replies with a small JSON acknowledgement — ",[523,1182,1183],{},"res.status(200).json({ status: 'ok' })"," — which is equivalent to ",[523,1186,1187],{},"res.sendStatus(200)","; either works as long as it goes out first.",[499,1190,1192],{"id":1191},"pattern-2-verify-application_token-in-constant-time","Pattern 2 — Verify application_token in constant time",[504,1194,1195,1196,1198,1199,1201],{},"Bitrix24 includes an ",[523,1197,567],{}," in the ",[523,1200,755],{}," block of every event. It is the shared secret that proves the request really came from your portal — without checking it, anyone who learns the URL can replay arbitrary events.",[504,1203,1204,1205,1208,1209,1212,1213,1216,1217,1220,1221,1224],{},"Compare it with a ",[508,1206,1207],{},"constant-time"," function. A plain ",[523,1210,1211],{},"==="," (or ",[523,1214,1215],{},"!=",") returns as soon as it hits the first differing byte, so an attacker can recover the token one character at a time by measuring response latency. Node's ",[523,1218,1219],{},"crypto.timingSafeEqual"," compares in constant time, but it ",[508,1222,1223],{},"throws when the two buffers differ in length"," — so pre-check the length first (which also avoids leaking length):",[637,1226,1228],{"className":639,"code":1227,"language":641,"meta":642,"style":642},"import { type Request, type Response } from 'express'\nimport { timingSafeEqual } from 'node:crypto'\n\n\u002F\u002F Constant-time compare. `timingSafeEqual` throws on a length mismatch, so we\n\u002F\u002F pre-check length first — this also avoids leaking length via the exception.\nfunction safeEqual(a: string, b: string): boolean {\n  const ab = Buffer.from(a, 'utf8')\n  const bb = Buffer.from(b, 'utf8')\n  if (ab.length !== bb.length) return false\n  return timingSafeEqual(ab, bb)\n}\n\nconst expectedApplicationToken = process.env.B24_APPLICATION_TOKEN ?? ''\n\n\u002F\u002F Called AFTER res.sendStatus(200) (Pattern 1). Returns false → drop the event.\nfunction verifyEvent(req: Request, _res: Response): boolean {\n  const incomingToken = req.body?.auth?.application_token ?? ''\n  return safeEqual(incomingToken, expectedApplicationToken)\n}\n",[523,1229,1230,1256,1276,1280,1285,1290,1321,1355,1385,1416,1433,1437,1441,1471,1475,1480,1511,1540,1559],{"__ignoreMap":642},[646,1231,1232,1234,1236,1238,1240,1242,1244,1246,1248,1250,1252,1254],{"class":648,"line":649},[646,1233,653],{"class":652},[646,1235,657],{"class":656},[646,1237,660],{"class":652},[646,1239,664],{"class":663},[646,1241,667],{"class":656},[646,1243,660],{"class":652},[646,1245,672],{"class":663},[646,1247,675],{"class":656},[646,1249,678],{"class":652},[646,1251,681],{"class":656},[646,1253,685],{"class":684},[646,1255,688],{"class":656},[646,1257,1258,1260,1262,1265,1267,1269,1271,1274],{"class":648,"line":691},[646,1259,653],{"class":652},[646,1261,657],{"class":656},[646,1263,1264],{"class":663}," timingSafeEqual",[646,1266,675],{"class":656},[646,1268,678],{"class":652},[646,1270,681],{"class":656},[646,1272,1273],{"class":684},"node:crypto",[646,1275,688],{"class":656},[646,1277,1278],{"class":648,"line":698},[646,1279,695],{"emptyLinePlaceholder":694},[646,1281,1282],{"class":648,"line":705},[646,1283,1284],{"class":701},"\u002F\u002F Constant-time compare. `timingSafeEqual` throws on a length mismatch, so we\n",[646,1286,1287],{"class":648,"line":764},[646,1288,1289],{"class":701},"\u002F\u002F pre-check length first — this also avoids leaking length via the exception.\n",[646,1291,1292,1294,1297,1299,1301,1303,1306,1308,1311,1313,1315,1317,1319],{"class":648,"line":808},[646,1293,709],{"class":708},[646,1295,1296],{"class":712}," safeEqual",[646,1298,716],{"class":656},[646,1300,536],{"class":719},[646,1302,511],{"class":656},[646,1304,1305],{"class":725}," string",[646,1307,667],{"class":656},[646,1309,1310],{"class":719}," b",[646,1312,511],{"class":656},[646,1314,1305],{"class":725},[646,1316,728],{"class":656},[646,1318,731],{"class":725},[646,1320,849],{"class":656},[646,1322,1323,1325,1328,1330,1333,1335,1338,1340,1342,1344,1346,1349,1352],{"class":648,"line":813},[646,1324,1088],{"class":708},[646,1326,1327],{"class":663}," ab",[646,1329,1094],{"class":656},[646,1331,1332],{"class":663}," Buffer",[646,1334,540],{"class":656},[646,1336,1337],{"class":712},"from",[646,1339,716],{"class":742},[646,1341,536],{"class":663},[646,1343,667],{"class":656},[646,1345,681],{"class":656},[646,1347,1348],{"class":684},"utf8",[646,1350,1351],{"class":656},"'",[646,1353,1354],{"class":742},")\n",[646,1356,1357,1359,1362,1364,1366,1368,1370,1372,1375,1377,1379,1381,1383],{"class":648,"line":819},[646,1358,1088],{"class":708},[646,1360,1361],{"class":663}," bb",[646,1363,1094],{"class":656},[646,1365,1332],{"class":663},[646,1367,540],{"class":656},[646,1369,1337],{"class":712},[646,1371,716],{"class":742},[646,1373,1374],{"class":663},"b",[646,1376,667],{"class":656},[646,1378,681],{"class":656},[646,1380,1348],{"class":684},[646,1382,1351],{"class":656},[646,1384,1354],{"class":742},[646,1386,1387,1389,1391,1394,1396,1399,1402,1404,1406,1408,1410,1412],{"class":648,"line":852},[646,1388,855],{"class":652},[646,1390,526],{"class":742},[646,1392,1393],{"class":663},"ab",[646,1395,540],{"class":656},[646,1397,1398],{"class":663},"length",[646,1400,1401],{"class":656}," !==",[646,1403,1361],{"class":663},[646,1405,540],{"class":656},[646,1407,1398],{"class":663},[646,1409,758],{"class":742},[646,1411,873],{"class":652},[646,1413,1415],{"class":1414},"sfNiH"," false\n",[646,1417,1418,1421,1423,1425,1427,1429,1431],{"class":648,"line":879},[646,1419,1420],{"class":652},"  return",[646,1422,1264],{"class":712},[646,1424,716],{"class":742},[646,1426,1393],{"class":663},[646,1428,667],{"class":656},[646,1430,1361],{"class":663},[646,1432,1354],{"class":742},[646,1434,1435],{"class":648,"line":901},[646,1436,761],{"class":656},[646,1438,1439],{"class":648,"line":924},[646,1440,695],{"emptyLinePlaceholder":694},[646,1442,1443,1446,1449,1452,1455,1457,1460,1462,1465,1468],{"class":648,"line":930},[646,1444,1445],{"class":708},"const",[646,1447,1448],{"class":663}," expectedApplicationToken ",[646,1450,1451],{"class":656},"=",[646,1453,1454],{"class":663}," process",[646,1456,540],{"class":656},[646,1458,1459],{"class":663},"env",[646,1461,540],{"class":656},[646,1463,1464],{"class":663},"B24_APPLICATION_TOKEN ",[646,1466,1467],{"class":656},"??",[646,1469,1470],{"class":656}," ''\n",[646,1472,1473],{"class":648,"line":936},[646,1474,695],{"emptyLinePlaceholder":694},[646,1476,1477],{"class":648,"line":1172},[646,1478,1479],{"class":701},"\u002F\u002F Called AFTER res.sendStatus(200) (Pattern 1). Returns false → drop the event.\n",[646,1481,1483,1485,1488,1490,1492,1494,1496,1498,1501,1503,1505,1507,1509],{"class":648,"line":1482},16,[646,1484,709],{"class":708},[646,1486,1487],{"class":712}," verifyEvent",[646,1489,716],{"class":656},[646,1491,720],{"class":719},[646,1493,511],{"class":656},[646,1495,664],{"class":725},[646,1497,667],{"class":656},[646,1499,1500],{"class":719}," _res",[646,1502,511],{"class":656},[646,1504,672],{"class":725},[646,1506,728],{"class":656},[646,1508,731],{"class":725},[646,1510,849],{"class":656},[646,1512,1514,1516,1519,1521,1523,1525,1527,1529,1531,1533,1535,1538],{"class":648,"line":1513},17,[646,1515,1088],{"class":708},[646,1517,1518],{"class":663}," incomingToken",[646,1520,1094],{"class":656},[646,1522,1097],{"class":663},[646,1524,540],{"class":656},[646,1526,749],{"class":663},[646,1528,752],{"class":656},[646,1530,755],{"class":663},[646,1532,752],{"class":656},[646,1534,567],{"class":663},[646,1536,1537],{"class":656}," ??",[646,1539,1470],{"class":656},[646,1541,1543,1545,1547,1549,1552,1554,1557],{"class":648,"line":1542},18,[646,1544,1420],{"class":652},[646,1546,1296],{"class":712},[646,1548,716],{"class":742},[646,1550,1551],{"class":663},"incomingToken",[646,1553,667],{"class":656},[646,1555,1556],{"class":663}," expectedApplicationToken",[646,1558,1354],{"class":742},[646,1560,1562],{"class":648,"line":1561},19,[646,1563,761],{"class":656},[504,1565,1566,1567,1570,1571,1573],{},"Store the expected token out of band (an env var such as ",[523,1568,1569],{},"B24_APPLICATION_TOKEN",", sourced from the Bitrix24 dev console → Local Application → ",[523,1572,567],{},") and fail closed when it is unset.",[1575,1576,1578],"h3",{"id":1577},"oauth-uninstall-verify-before-deleting","OAuth uninstall: verify before deleting",[504,1580,1581,1582,1585,1586,1588,1589,1592,1593,1596,1597,1599],{},"The same rule is critical on the ",[508,1583,1584],{},"uninstall"," callback. ",[523,1587,556],{}," deletes a portal's stored credentials — so if you delete without verifying, anyone who can reach ",[523,1590,1591],{},"\u002Funinstall"," can wipe a portal's tokens just by guessing its ",[523,1594,1595],{},"member_id",". Always compare the incoming ",[523,1598,567],{}," against the one you recorded at install time, in constant time, before removing anything:",[637,1601,1603],{"className":639,"code":1602,"language":641,"meta":642,"style":642},"import { type Request, type Response } from 'express'\n\n\u002F\u002F `safeEqual` is the constant-time helper from Pattern 2 above.\ndeclare function safeEqual(a: string, b: string): boolean\ndeclare function getCredentials(memberId: string): Promise\u003C{ applicationToken: string } | null>\ndeclare function deleteCredentials(memberId: string): Promise\u003Cvoid>\n\nasync function handleUninstall(req: Request, res: Response) {\n  res.sendStatus(200)  \u002F\u002F reply first — Pattern 1 applies to uninstall too\n\n  const memberId = req.body?.auth?.member_id ?? ''\n  const receivedToken = req.body?.auth?.application_token ?? ''\n  if (!memberId || !receivedToken) return\n\n  const stored = await getCredentials(memberId)\n  if (!stored) return  \u002F\u002F idempotent — nothing to delete\n\n  \u002F\u002F ALWAYS verify application_token before deleting. Without this check, anyone\n  \u002F\u002F who reaches \u002Funinstall could wipe a portal's tokens by guessing member_id.\n  if (!safeEqual(stored.applicationToken, receivedToken)) return\n\n  await deleteCredentials(memberId)\n}\n",[523,1604,1605,1631,1635,1640,1670,1713,1740,1744,1773,1791,1795,1822,1849,1873,1877,1897,1915,1919,1924,1929,1958,1963,1976],{"__ignoreMap":642},[646,1606,1607,1609,1611,1613,1615,1617,1619,1621,1623,1625,1627,1629],{"class":648,"line":649},[646,1608,653],{"class":652},[646,1610,657],{"class":656},[646,1612,660],{"class":652},[646,1614,664],{"class":663},[646,1616,667],{"class":656},[646,1618,660],{"class":652},[646,1620,672],{"class":663},[646,1622,675],{"class":656},[646,1624,678],{"class":652},[646,1626,681],{"class":656},[646,1628,685],{"class":684},[646,1630,688],{"class":656},[646,1632,1633],{"class":648,"line":691},[646,1634,695],{"emptyLinePlaceholder":694},[646,1636,1637],{"class":648,"line":698},[646,1638,1639],{"class":701},"\u002F\u002F `safeEqual` is the constant-time helper from Pattern 2 above.\n",[646,1641,1642,1645,1647,1649,1651,1653,1655,1657,1659,1661,1663,1665,1667],{"class":648,"line":705},[646,1643,1644],{"class":708},"declare",[646,1646,770],{"class":708},[646,1648,1296],{"class":712},[646,1650,716],{"class":656},[646,1652,536],{"class":719},[646,1654,511],{"class":656},[646,1656,1305],{"class":725},[646,1658,667],{"class":656},[646,1660,1310],{"class":719},[646,1662,511],{"class":656},[646,1664,1305],{"class":725},[646,1666,728],{"class":656},[646,1668,1669],{"class":725}," boolean\n",[646,1671,1672,1674,1676,1679,1681,1684,1686,1688,1690,1692,1695,1698,1700,1702,1704,1707,1710],{"class":648,"line":764},[646,1673,1644],{"class":708},[646,1675,770],{"class":708},[646,1677,1678],{"class":712}," getCredentials",[646,1680,716],{"class":656},[646,1682,1683],{"class":719},"memberId",[646,1685,511],{"class":656},[646,1687,1305],{"class":725},[646,1689,728],{"class":656},[646,1691,788],{"class":725},[646,1693,1694],{"class":656},"\u003C{",[646,1696,1697],{"class":742}," applicationToken",[646,1699,511],{"class":656},[646,1701,1305],{"class":725},[646,1703,675],{"class":656},[646,1705,1706],{"class":656}," |",[646,1708,1709],{"class":725}," null",[646,1711,1712],{"class":656},">\n",[646,1714,1715,1717,1719,1722,1724,1726,1728,1730,1732,1734,1736,1738],{"class":648,"line":808},[646,1716,1644],{"class":708},[646,1718,770],{"class":708},[646,1720,1721],{"class":712}," deleteCredentials",[646,1723,716],{"class":656},[646,1725,1683],{"class":719},[646,1727,511],{"class":656},[646,1729,1305],{"class":725},[646,1731,728],{"class":656},[646,1733,788],{"class":725},[646,1735,791],{"class":656},[646,1737,794],{"class":725},[646,1739,1712],{"class":656},[646,1741,1742],{"class":648,"line":813},[646,1743,695],{"emptyLinePlaceholder":694},[646,1745,1746,1748,1750,1753,1755,1757,1759,1761,1763,1765,1767,1769,1771],{"class":648,"line":819},[646,1747,767],{"class":708},[646,1749,770],{"class":708},[646,1751,1752],{"class":712}," handleUninstall",[646,1754,716],{"class":656},[646,1756,720],{"class":719},[646,1758,511],{"class":656},[646,1760,664],{"class":725},[646,1762,667],{"class":656},[646,1764,839],{"class":719},[646,1766,511],{"class":656},[646,1768,672],{"class":725},[646,1770,846],{"class":656},[646,1772,849],{"class":656},[646,1774,1775,1777,1779,1781,1783,1785,1788],{"class":648,"line":852},[646,1776,904],{"class":663},[646,1778,540],{"class":656},[646,1780,909],{"class":712},[646,1782,716],{"class":742},[646,1784,915],{"class":914},[646,1786,1787],{"class":742},")  ",[646,1789,1790],{"class":701},"\u002F\u002F reply first — Pattern 1 applies to uninstall too\n",[646,1792,1793],{"class":648,"line":879},[646,1794,695],{"emptyLinePlaceholder":694},[646,1796,1797,1799,1802,1804,1806,1808,1810,1812,1814,1816,1818,1820],{"class":648,"line":901},[646,1798,1088],{"class":708},[646,1800,1801],{"class":663}," memberId",[646,1803,1094],{"class":656},[646,1805,1097],{"class":663},[646,1807,540],{"class":656},[646,1809,749],{"class":663},[646,1811,752],{"class":656},[646,1813,755],{"class":663},[646,1815,752],{"class":656},[646,1817,1595],{"class":663},[646,1819,1537],{"class":656},[646,1821,1470],{"class":656},[646,1823,1824,1826,1829,1831,1833,1835,1837,1839,1841,1843,1845,1847],{"class":648,"line":924},[646,1825,1088],{"class":708},[646,1827,1828],{"class":663}," receivedToken",[646,1830,1094],{"class":656},[646,1832,1097],{"class":663},[646,1834,540],{"class":656},[646,1836,749],{"class":663},[646,1838,752],{"class":656},[646,1840,755],{"class":663},[646,1842,752],{"class":656},[646,1844,567],{"class":663},[646,1846,1537],{"class":656},[646,1848,1470],{"class":656},[646,1850,1851,1853,1855,1857,1859,1862,1865,1868,1870],{"class":648,"line":930},[646,1852,855],{"class":652},[646,1854,526],{"class":742},[646,1856,860],{"class":656},[646,1858,1683],{"class":663},[646,1860,1861],{"class":656}," ||",[646,1863,1864],{"class":656}," !",[646,1866,1867],{"class":663},"receivedToken",[646,1869,758],{"class":742},[646,1871,1872],{"class":652},"return\n",[646,1874,1875],{"class":648,"line":936},[646,1876,695],{"emptyLinePlaceholder":694},[646,1878,1879,1881,1884,1886,1889,1891,1893,1895],{"class":648,"line":1172},[646,1880,1088],{"class":708},[646,1882,1883],{"class":663}," stored",[646,1885,1094],{"class":656},[646,1887,1888],{"class":652}," await",[646,1890,1678],{"class":712},[646,1892,716],{"class":742},[646,1894,1683],{"class":663},[646,1896,1354],{"class":742},[646,1898,1899,1901,1903,1905,1908,1910,1912],{"class":648,"line":1482},[646,1900,855],{"class":652},[646,1902,526],{"class":742},[646,1904,860],{"class":656},[646,1906,1907],{"class":663},"stored",[646,1909,758],{"class":742},[646,1911,873],{"class":652},[646,1913,1914],{"class":701},"  \u002F\u002F idempotent — nothing to delete\n",[646,1916,1917],{"class":648,"line":1513},[646,1918,695],{"emptyLinePlaceholder":694},[646,1920,1921],{"class":648,"line":1542},[646,1922,1923],{"class":701},"  \u002F\u002F ALWAYS verify application_token before deleting. Without this check, anyone\n",[646,1925,1926],{"class":648,"line":1561},[646,1927,1928],{"class":701},"  \u002F\u002F who reaches \u002Funinstall could wipe a portal's tokens by guessing member_id.\n",[646,1930,1932,1934,1936,1938,1941,1943,1945,1947,1950,1952,1954,1956],{"class":648,"line":1931},20,[646,1933,855],{"class":652},[646,1935,526],{"class":742},[646,1937,860],{"class":656},[646,1939,1940],{"class":712},"safeEqual",[646,1942,716],{"class":742},[646,1944,1907],{"class":663},[646,1946,540],{"class":656},[646,1948,1949],{"class":663},"applicationToken",[646,1951,667],{"class":656},[646,1953,1828],{"class":663},[646,1955,870],{"class":742},[646,1957,1872],{"class":652},[646,1959,1961],{"class":648,"line":1960},21,[646,1962,695],{"emptyLinePlaceholder":694},[646,1964,1966,1968,1970,1972,1974],{"class":648,"line":1965},22,[646,1967,882],{"class":652},[646,1969,1721],{"class":712},[646,1971,716],{"class":742},[646,1973,1683],{"class":663},[646,1975,1354],{"class":742},[646,1977,1979],{"class":648,"line":1978},23,[646,1980,761],{"class":656},[499,1982,1984],{"id":1983},"checklist","Checklist",[504,1986,1987,526,1990,1992],{},[508,1988,1989],{},"Every event-receiver endpoint",[523,1991,525],{}," handlers):",[513,1994,1995,2005,2013,2023,2034,2040],{},[516,1996,1997,1998,2001,2002,2004],{},"Serve the handler over ",[508,1999,2000],{},"HTTPS"," at a publicly reachable URL — Bitrix24 calls it from its own servers, and the ",[523,2003,567],{}," travels in the request body, so plain HTTP would expose it in transit.",[516,2006,2007,2008,625,2010,2012],{},"Reply ",[523,2009,624],{},[508,2011,598],{}," verifying or processing (Pattern 1).",[516,2014,2015,2016,2018,2019,2022],{},"Require an ",[523,2017,567],{}," env var and ",[508,2020,2021],{},"fail closed"," if it is unset.",[516,2024,2025,2026,2028,2029,2031,2032,540],{},"Compare the incoming ",[523,2027,567],{}," with ",[523,2030,1940],{}," (Pattern 2), never ",[523,2033,1211],{},[516,2035,2036,2037,2039],{},"Drop — do not error — on a mismatch (return the ",[523,2038,624],{}," you already sent; don't reveal which check failed).",[516,2041,2042,2043,2045,2046,2048],{},"Never log the ",[523,2044,567],{}," (or the raw ",[523,2047,755],{}," block). Log a decision, not the secret.",[504,2050,2051,2053],{},[508,2052,545],{}," (install \u002F uninstall callbacks), additionally:",[513,2055,2056,2062,2070,2073],{},[516,2057,2058,2059,2061],{},"Persist ",[523,2060,567],{}," alongside the tokens at install time.",[516,2063,2064,2065,625,2067,2069],{},"On uninstall, verify ",[523,2066,567],{},[508,2068,598],{}," deleting credentials.",[516,2071,2072],{},"Treat a missing record as idempotent success (no error — it is already gone).",[516,2074,2075,2076,2079],{},"Store tokens with restrictive permissions (file mode ",[523,2077,2078],{},"0o600"," or a datastore with per-tenant isolation).",[499,2081,2083],{"id":2082},"see-also","See also",[513,2085,2086,2092,2097,2110],{},[516,2087,2088,2091],{},[536,2089,2090],{"href":538},"Webhook handler recipe"," — full outbound-event server these patterns come from.",[516,2093,2094,2096],{},[536,2095,561],{"href":560}," — install\u002Funinstall handshake with per-portal storage.",[516,2098,2099,2103,2104,550,2106,2109],{},[536,2100,2102],{"href":2101},"\u002Fdocs\u002Fexamples\u002Fevent-registration\u002F","Outbound event registration recipe"," — ",[523,2105,525],{},[523,2107,2108],{},"event.unbind"," from the SDK.",[516,2111,2112,2114],{},[536,2113,578],{"href":577}," — what the SDK strips before request data reaches a logger.",[2116,2117,2118],"style",{},"html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}",{"title":642,"searchDepth":691,"depth":691,"links":2120},[2121,2122,2123,2126,2127],{"id":501,"depth":691,"text":502},{"id":586,"depth":691,"text":587},{"id":1191,"depth":691,"text":1192,"children":2124},[2125],{"id":1577,"depth":698,"text":1578},{"id":1983,"depth":691,"text":1984},{"id":2082,"depth":691,"text":2083},"md",[2130,2134],{"label":2131,"iconName":2132,"to":2133},"Recipe 7 — webhook handler (source)","GitHubIcon","https:\u002F\u002Fgithub.com\u002Fbitrix24\u002Fb24jssdk\u002Fblob\u002Fmain\u002Fskills\u002Fb24jssdk-recipes\u002Fexamples\u002F07-webhook-handler.ts",{"label":2135,"iconName":2132,"to":2136},"Recipe 12 — OAuth install (source)","https:\u002F\u002Fgithub.com\u002Fbitrix24\u002Fb24jssdk\u002Fblob\u002Fmain\u002Fskills\u002Fb24jssdk-recipes\u002Fexamples\u002F12-oauth-install.ts",{},{"title":300},{"title":493,"description":303},"gwfxzCQxt8qLTyKSWVYvx-9f6QsXzwcBG0Cq65ZM8ig",{"data":2142,"body":2143},{},{"type":2144,"children":2145},"root",[2146],{"type":2147,"tag":504,"props":2148,"children":2149},"element",{},[2150],{"type":2151,"value":303},"text",1783080293164]