v2.2.0

Discovering v3 methods (OpenAPI)

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.

Overview

rest.documentation.openapi is a v3 REST method that returns the portal's ownOpenAPI 3.0.0 document — a single machine-readable description of every REST API v3 method that exists on that specific portal, with the request body fields (and example values) and the response envelope for each one.

This is the source of truth for v3 method availability. The SDK deliberately keeps no hardcoded list of v3 methods (an earlier allowlist both lagged the server and blocked valid methods); instead, $b24.actions.v3.* sends any method to the server and lets it decide. When you need to know what's actually there — which methods, which fields — you ask the portal itself with this call.

The document lists the methods this portal has. Its contents follow the installed modules and the build, so two portals answer differently — always treat it as portal-specific, not a global catalogue. Measured: an on-premise build published 147 methods across 8 modules, a cloud portal 245, and a second cloud portal 220 with a different set again, including changed request fields for methods present on both.Listing is not permission. A method present in the document still fails with BITRIX_REST_V3_EXCEPTION_INSUFFICIENTSCOPEEXCEPTION (403) when the token lacks the scope — so the document cannot be used as a permission check. It is not filtered by your rights either: widening a webhook from 10 scopes to 15 did not change it at all. Scopes are published separately, by rest.scope.list.
This method answers without the usual envelope: the OpenAPI document arrives at the top level, with no result wrapper and no time block. The SDK wraps it back into result so the snippets below work, but a raw HTTP client will see the document itself as the response body.

Calling it through the SDK

It's an ordinary v3 call — no special helper:

const response = await $b24.actions.v3.call.make<{ openapi: string }>({
  method: 'rest.documentation.openapi'
})

if (!response.isSuccess) {
  console.error(new Error(`Error: ${response.getErrorMessages().join('; ')}`))
} else {
  const doc = response.getData()?.result
  console.log(`OpenAPI ${doc?.openapi}`) // e.g. "3.0.0"
}

Is a given action published? The *.aggregate case

Because paths is one entry per method, a suffix filter answers "does this portal publish this kind of action at all" in a single call. Aggregation is the case that keeps coming up, since actions.v3.aggregate.make is @experimental precisely because nothing shipped exercises it:

const response = await $b24.actions.v3.call.make<{ paths?: Record<string, unknown> }>({
  method: 'rest.documentation.openapi'
})

const aggregates = Object.keys(response.getData()?.result?.paths ?? {})
  .filter(path => path.endsWith('.aggregate'))
  .map(path => path.replace(/^\//, ''))

console.log(aggregates.length === 0 ? '(none)' : aggregates.join(', '))

On the four portals measured this prints (none): no shipped module opts into the server-side aggregation trait, so Aggregate.make() has nothing to run against and a client-side reduce over CallList is the answer. Ask your own portal rather than taking that as permanent — the machinery ships and works, and a module can opt in at any version.

What the document looks like

A standard OpenAPI 3.0.0 object. The parts you care about in practice:

FieldWhat it holds
openapispec version string, e.g. "3.0.0"
info{ title: "Bitrix24 REST V3 API", version }
tagsone entry per modulehumanresources, mail, main, note, rest, tasks, timeman, …
pathsone entry per method, keyed by /method.name (e.g. /tasks.task.list)

Each paths['/method.name'] carries post (and get) operations with a tags array (the owning module), a requestBody JSON Schema describing the accepted parameters — often with an example field listing the real field names — and a responses schema for the { result } envelope.

// Shape (abridged) — the fields you read in practice
type OpenApiDoc = {
  openapi: string // e.g. "3.0.0"
  info: { title: string, version: string } // title: "Bitrix24 REST V3 API"
  tags: Array<{ name: string, description: string }> // one entry per module
  paths: Record<string, {
    // keyed by "/method.name", e.g. "/tasks.task.list"
    post?: {
      tags?: string[] // the owning module
      requestBody?: { content: { 'application/json': { schema: unknown } } }
    }
    get?: { tags?: string[] }
  }>
}

Why this matters for AI agents

The document is the cleanest way for an LLM or a code generator to work against a portal without guessing:

  • Discovery — enumerate exactly which v3 methods exist before generating a call, instead of hallucinating a method name that returns METHODNOTFOUNDEXCEPTION.
  • Correct fields — read the real select / parameter names from each method's requestBody schema (and its example) instead of inventing them.
  • Typing / codegen — feed the schema into a generator to produce typed wrappers, or narrow $b24.actions.v3.call.make<T>() with the right T.
  • Portal-aware — because the answer is per-portal, an agent can adapt to the modules actually installed rather than to a generic assumption. Scopes are a separate question the document does not answer; pair it with rest.scope.list, which is where the portal publishes them.
The document is large (often 100 KB+) and response-only — it does not change between calls within a session. Fetch it once, cache it, and query the cached object. Don't request it on every interaction.

Error Handling

rest.documentation.openapi is a normal v3 call, so it follows the standard AjaxResult contract — always check isSuccess before reading the document:

const response = await $b24.actions.v3.call.make<{ openapi: string }>({
  method: 'rest.documentation.openapi'
})

if (!response.isSuccess) {
  // e.g. the token lacks the scope, or the portal is unreachable
  console.error(new Error(`Error: ${response.getErrorMessages().join('; ')}`))
} else {
  const doc = response.getData()?.result
  console.log(`OpenAPI ${doc?.openapi}`)
}

Examples

List every available method

const response = await $b24.actions.v3.call.make<{
  paths: Record<string, unknown>
}>({ method: 'rest.documentation.openapi' })

const methods = Object.keys(response.getData()?.result?.paths ?? {})
  .map(path => path.replace(/^\//, '')) // "/tasks.task.list" → "tasks.task.list"

console.log(`${methods.length} v3 methods available`)

Methods grouped by module

type OpenApiDoc = {
  paths: Record<string, { post?: { tags?: string[] } }>
}

const doc = (await $b24.actions.v3.call.make<OpenApiDoc>({
  method: 'rest.documentation.openapi'
})).getData()?.result

const byModule = new Map<string, string[]>()
for (const [path, ops] of Object.entries(doc?.paths ?? {})) {
  const moduleName = ops.post?.tags?.[0] ?? 'unknown'
  const list = byModule.get(moduleName) ?? []
  list.push(path.replace(/^\//, ''))
  byModule.set(moduleName, list)
}

console.log([...byModule.keys()]) // ['humanresources', 'mail', 'main', 'note', 'rest', 'tasks', 'timeman', …]

Check whether a specific method exists

const doc = (await $b24.actions.v3.call.make<{ paths: Record<string, unknown> }>({
  method: 'rest.documentation.openapi'
})).getData()?.result

const exists = Boolean(doc?.paths?.['/note.collection.list'])
console.log(`note.collection.list on v3: ${exists}`)

Alternatives and Recommendations

  • For one entity's fields: <entity>.field.list is the cheaper half of the same question. This document tells you which methods exist; that one tells you which fields this entity has — and carries filterable, editable and requiredGroups, which are not in the request schema here at all.
  • Apidocs reference: the public REST API v3 reference documents methods in human-readable form; rest.documentation.openapi is the programmatic, portal-specific counterpart.
  • For calling a discovered method: use Call, CallList / FetchList, or the native CallTail / FetchTail when the method publishes a tail action.
  • Picking v2 vs v3: see Choosing the right method.