Skip to content
Bitween AI is on the way
API reference

Beta: in progress. These pages describe what the next release is getting, from releases/r10.0-develop, and may change before it ships. For the version you can run today, read the stable documentation.

Reference · Beta

API reference

REST conventions and every endpoint

Conventions#

  • The base path is /api. Swagger UI is at /swagger, with the spec at /api/swagger.json.
  • Admin endpoints need Authorization: Bearer <jwt> from sign-in, plus the permission listed.
  • Partner endpoints need a partner key instead: the key header (partnerkey unless renamed, and partnerkey always works), Authorization: Bearer <key>, or Basic auth. An API gateway set to take tokens needs Authorization: Bearer <token> from its login server.
  • Responses use camelCase property names. Requests are accepted in any casing. Enums are strings, and numbers are accepted too.
  • Adapter and partner property sets are lists of { "key": "...", "value": "..." }.

Errors#

Status Meaning
400 Validation failed. The body usually maps an error code to messages, such as { "ALREADY_RETRIED": ["..."] }.
401 Not signed in, or the token has expired
403 Signed in, but missing the permission
404 Not found

Search queries#

Endpoints marked searchy accept these parameters.

Parameter Example Meaning
filter filter=DocumentId:1:3 Field:Rule:Value, repeatable. The UI uses rule 1 for equals and 4 for contains.
sort sort=StartedOn:2 Field:Order, where 2 is descending
page, size page=0&size=25 Paging. Always send size.
lookup lookup=true Return an id-to-name map instead of rows

They return { "result": [...], "totalCount": 123 }.

Partner endpoints#

Method and path Description
POST /api/gateway/{urlName}/async Submit to an API gateway. Returns 202 with the exchange id.
POST /api/gateway/{urlName}/sync Submit and wait for the result. Optional Wait-Period header.
POST /api/xchanges/{informationTypeIdOrName} Legacy API call. Optional waitresponse header.
GET /api/xchanges/{exchangeId} Legacy result lookup for the calling partner's own exchanges

See Entry points for status codes.

Session and account#

Method and path Permission Description
POST /api/accounts/login none Sign in with a username and password, a Microsoft token, an OpenID Connect code ({ oidcCode, oidcState }), or a refresh token (the refresh cookie, or refreshToken in the body). Returns { jwt }.
POST /api/accounts/oidcstart none Starts an OpenID Connect sign-in: returns { url }, the provider's sign-in address, and sets the cookie the sign-in is finished with.
POST /api/accounts/logout none Deletes the refresh token and clears site data. A client without cookies sends { refreshToken }.
POST /api/accounts/cligrant signed in Signs the bitween CLI in as the caller: { codeChallenge }, the CLI's S256 PKCE challenge. Returns { code }, good for two minutes. The admin UI's /cli-login page calls it after the member confirms.
POST /api/accounts/clitoken none The CLI trades that code for a session: { code, codeVerifier }. Returns { jwt, refreshToken, email }.
POST /api/accounts/servicetoken none A service account signs in: { token }, one of its bwt_ tokens. Returns { jwt, expiresIn, id, name }, with no refresh token: trade the token again when the session ends. Shares the sign-in rate limit; unknown tokens count toward the address's lockout.
GET /api/accounts/profile signed in The current member, roles and permissions
POST /api/accounts/changePassword signed in Change your own password
POST /api/accounts/forgotPassword none { email }. Emails a reset link when the address is a member's; answers the same either way. 400 PASSWORD_RESET_OFF without a mail server or Bitween:PublicUrl. Shares the sign-in rate limit.
POST /api/accounts/passwordLink none { token }, from an invite or reset link. Returns { purpose, name, email, expiresOn }, and changes nothing. 400 LINK_INVALID once used or expired.
POST /api/accounts/usePasswordLink none { token, password }. Sets the password, spends the link and ends the member's sessions. Returns { email }.
GET /api/settings/config none Sign-in options and theme
GET /api/settings/myversion signed in API version
GET /api/permissions signed in The permission catalogue

Connections#

All need the global values permissions: global-values.view to read, .create, .edit and .delete to change. Secrets come back as __private__.

Method and path Permission Description
GET /api/connections view Every connection, with its status and the reference settings write
GET /api/connections/{id} view One connection
POST /api/connections create { name, grant, authorizeUrl, tokenUrl, clientId, clientSecret, scopes, authorizeParameters, tokenAuth, username, password }; grant is ClientCredentials, AuthorizationCode or Password
POST /api/connections/{id} edit The same; a secret sent as __private__, or left out, is kept
DELETE /api/connections/{id} delete Refused while settings refer to it
POST /api/connections/{id}/authorize edit Returns { url }, the provider's approval page, and sets the cookie the approval is finished with
POST /api/connections/complete edit { code, state } from the provider's redirect to /oauth-callback. Returns { id, name }
POST /api/connections/{id}/test edit Renews the token now and returns the connection

Exchanges#

Method and path Permission Description
GET /api/xchanges exchanges.view or dashboard.view, or signed in with lookup=true Searchy. See the filters below. A lookup returns at most 100 exchange ids, newest first, each with its information type's name.
POST /api/xchangequeries exchanges.view Turn a question into an Exchanges page filter with AI: { question, timeZone } returns { query, answer, notes, model, sent }. See Asking your exchanges.
GET /api/xchanges/statuslist exchanges.view Status values for filters
GET /api/xchanges/retrytree?id= exchanges.view The retry chain any attempt belongs to
POST /api/xchanges signed in, no permission checked Create an exchange by hand
POST /api/xchanges/{id}/retry exchanges.operate { reason, reset }
POST /api/xchangeexplanations exchanges.view Explain a failed exchange with AI: { xchangeId }, replying with summary, cause, fix, model, and sent (exactly what went to the provider, masked). Recorded as an explain-failure operator action. See Explaining a failure.
POST /api/xchanges/bulkretrypreview exchanges.operate Plan a bulk retry without running it
POST /api/xchanges/bulkretry exchanges.operate Run a bulk retry
POST /api/xchanges/export exchanges.view The selected exchanges' files as one zip
POST /api/xchanges/export/check exchanges.view How many of the selected exchanges are older than storage keeps their files, asked before exporting
GET /api/bitweendocs?documentKey= exchanges.view Read one of an exchange's files. Other keys are refused.
GET /api/files/{seal}/{key} none, the seal An exchange file for a reader without a login, served as text/plain
GET /api/delayedretries exchanges.view or dashboard.view Searchy. Waiting automatic retries.
POST /api/delayedretries/{id}/runnow exchanges.operate Run a waiting retry now

Exchange search filters, besides ordinary fields such as SubscriptionId, PartnerId and DocumentId. Those take rule 1 (is), 2 (is not) and 9 (is any of, as SubscriptionId:9:numeric|4|7). "Is none of" is one rule-2 filter per value. "Is not" keeps the exchanges with no value at all, for example no subscription.

Filter Meaning
StatusFilter:1:{n} 0 processing, 1 success, 2 bad response, 3 failed. Other values are refused.
LatestOnly:1:true Only the newest attempt of each retry chain
ErrorContains:4:{text} The error contains the text, ignoring case
PromotedProperty:4:{key}:{text} That key's value contains the text, ignoring case. Without :{text} (rule 1), the exchange promoted the key. Send one per property; every one has to hold.
PromotedPropertiesRaw:4:{text} A promoted value contains the text, ignoring case
PromotedPropertyKey:1:{key} The older form for one property. With PromotedPropertiesRaw, the text is looked for in that key's value only.
IsRetry:1:true, HasScheduledRetry:1:true, Aggregated:1:true A retry of another exchange; an automatic retry is booked; rolled up into an aggregation

Rule 2 turns each of these round except StatusFilter and LatestOnly: ErrorContains:2:timeout is "the error does not contain timeout", and it keeps the exchanges with no error. Each PromotedProperty turns round on its own. In the older form, send the key and the text with the same rule, since they turn round together.

Bulk retry takes { ids, filter, excludeIds, reason, reset }. filter is the same query-string fragment the search takes, such as filter=StatusFilter:1:3&filter=LatestOnly:1:true, and it replaces ids when present. The plan returned by both endpoints lists how many were selected and will be retried, attempts substituted with the end of their chain, and skipped exchanges with reasons, plus targets, the attempts that will run. The retry carries out at most limit (500) per request; the preview plans up to maxSelection (5,000), so a larger selection is retried by sending targets back as ids, limit at a time.

Export takes { ids, filter, excludeIds }, chosen the same way, and streams back a zip (application/zip) with a folder per exchange and a missing.txt listing files that storage no longer has. At most 500 exchanges go in one zip; a larger selection is refused with TOO_MANY. export/check takes the same body and returns { count, withoutFiles, keptDays }: how many exchanges the zip would take, how many of them are older than the bucket's rule keeps files, and that rule's days when one covers them all.

The retry tree returns { rootId, nodes, truncated }. Each node has its id, retryFor, promoted properties, times, status, exception, whether it was manual, and any scheduled retry or blocked reason. It stops at 100 levels or 500 attempts.

Subscriptions#

Method and path Permission
GET /api/subscriptions subscriptions.view. Searchy. Adapter properties come back empty, since they hold credentials; read them from GET /api/subscriptions/{id}. Each row's references names what they referenced instead: { globals: [{ setId, keys }], partnerProperties }, as written in the tokens. On a mirror, filter=Mirror:1:<fromProd, untested, same, check, different or noTraffic> keeps the rows prod has too, or those whose test says so; the API and bus gateway lists take it too (see GET /api/mirror/listed).
GET /api/subscriptions/{id} subscriptions.view
POST /api/subscriptions subscriptions.create
POST /api/subscriptions/{id} subscriptions.edit. Replaces the whole configuration.
DELETE /api/subscriptions/{id} subscriptions.delete. Refused while a gateway, route, response link or aggregation points at it.
POST /api/subscriptions/{id}/pause subscriptions.operate. Toggles pause.
POST /api/subscriptions/{id}/receivenow subscriptions.operate
POST /api/subscriptions/{id}/clearrunning subscriptions.operate. Clears a running flag a dead run left behind, so the next scheduled run starts. Refused with STILL_RUNNING while the job is running on the node that answers. Audited. Returns { id, cleared }.
POST /api/subscriptions/{id}/aggregatenow subscriptions.operate
POST /api/subscriptions/{id}/savemapper subscriptions.edit. { mapperId, mapperProperties }
POST /api/subscriptions/{id}/retryusage subscriptions.view
POST /api/subscriptions/{id}/resetretryusage subscriptions.operate
GET /api/subscriptions/runs?subscriptionId=&limit= subscriptions.view
GET /api/subscriptions/history?subscriptionId= subscriptions.view. Its versions, newest first: [{ number, savedOn, savedBy, changed }], changed naming the settings that differ from the version before.
GET /api/subscriptions/revision?subscriptionId=&number= subscriptions.view. { number, savedOn, configuration }, the configuration in the shape GET /api/subscriptions/{id} answers, secrets as __private__. Restoring it is posting it to POST /api/subscriptions/{id}.
GET /api/subscriptions/receiveattempts?subscriptionId=&outcome=&offset=&limit= subscriptions.view
GET /api/subscriptions/bookmark?subscriptionId= subscriptions.view. { value, updatedOn }: where a receiver that keeps a bookmark last got to; value is null when there is none.
POST /api/subscriptions/{id}/forgetbookmark subscriptions.operate. Forgets it, so the next run starts from the beginning. In the audit trail.
GET /api/subscriptions/sourcepaths?subscriptionId= exchanges.view. The paths in the last document it received, with their values: what a response or bus gateway subscription it feeds can read.
GET /api/subscriptions/lastruns subscriptions.view
GET /api/subscriptions/schedulehealth subscriptions.view
GET /api/subscriptioncategories subscriptions.view
POST /api/subscriptioncategories subscriptions.create
POST /api/subscriptioncategories/{id} subscriptions.edit
POST /api/subscriptioncategories/{id}/delete subscriptions.delete

A scheduled job that pulls files from S3:

{
  "name": "Orders from S3",
  "type": "Receiving",
  "documentId": 3,
  "partnerId": 5,
  "receiverId": "NativeS3Receiver",
  "receiverProperties": [
    { "key": "ServiceUrl", "value": "https://s3.example.com" },
    { "key": "BucketName", "value": "incoming" },
    { "key": "AccessKeyId", "value": "{{globals.s3.accessKeyId}}" },
    { "key": "SecretAccessKey", "value": "{{globals.s3.secretAccessKey}}" }
  ],
  "handlerId": "NativeHttpHandler",
  "handlerProperties": [ { "key": "Url", "value": "{{partner.erpUrl}}/orders" } ],
  "schedules": [ { "recurrence": "Hourly", "days": 0, "hours": 0, "minutes": 15 } ],
  "retryPolicyId": 1,
  "inactive": false
}

A subscription bound to a data source also carries dataSourceId.

expectTraffic asks to be told when the subscription stops receiving exchanges. See Entry points. It is replaced like the rest of the configuration, so an update without it removes it.

"expectTraffic": { "everyMinutes": 15, "days": ["Sun", "Mon", "Tue", "Wed", "Thu"], "from": "08:00", "to": "22:00", "timeZone": "Asia/Riyadh" }
Field Rule
everyMinutes Required, 5 to 10080. When a window has hours, no longer than the window.
days Sun to Sat, any case; stored in week order. Null or empty means every day.
from, to HH:mm, both or neither. to later than from; a window can't run past midnight.
timeZone An IANA name such as Asia/Riyadh. Null means UTC.

A value that breaks a rule is refused with a validation error whose message says which. GET /api/subscriptions/{id} and the search also return quietSince: when the subscription was found quiet, or null. It can't be set through the API.

notifications sends through notification channels when the subscription's exchanges finish or it goes quiet. Unlike the rest, leaving it out of an update keeps the stored rows; an empty list clears them.

"notifications": [ { "channelId": 4, "onFailure": true, "onBadResult": false, "onSuccess": false, "onQuiet": true } ]

Adapters and mapping#

Method and path Permission Description
GET /api/adapters/Catalog?prefix=&startupValues= subscriptions.view Every adapter of one kind with its properties. prefix is receivers, handlers, mappers or validators. Includes adapters published only to the catalog. Each entry of versionHistory has hasSource and runtime. startupValues=false leaves the properties out (startupValues is null): reading them starts each published adapter, which takes seconds each the first time.
GET /api/adapters?prefix= subscriptions.view Adapter ids of one kind
GET /api/adapters/Versioned?prefix= subscriptions.view Adapter ids with versions
GET /api/adapters/{id}/GetStartupValues subscriptions.view One adapter's properties
GET /api/adapters/{id}/properties subscriptions.view
GET /api/adapters/{id}/Metadata subscriptions.view A custom adapter's package metadata
GET /api/adapters/source?adapterId=&version= adapter-source.view The source files a published version carries, each with its SHA-256
GET /api/adapters/sourcefile?adapterId=&version=&path= adapter-source.view One source file, checked against its manifest hash. Recorded in the audit trail.
GET /api/adapters/versions?adapterId= subscriptions.view Every published version of one adapter, with which is current and which are withdrawn
POST /api/adapters/packages?version=&current=&releaseNotes= adapter-source.operate Publish a package built by bitween adapter build. The body is the zip itself (application/zip), up to 200 MB. version is major, minor, patch or an exact version, the package's own when empty; current=true makes it current; releaseNotes replaces the package's own. Returns { adapterId, version, current, sha256 }. A refusal is 400 with { field: [message] }.
POST /api/adapters/promote adapter-source.operate Make a published version current: { adapterId, version }
GET /api/marketplace/search?q=&tier= subscriptions.view What the registry set under Settings lists: { configured, disabled, registry, result: [{ card, installed }], totalCount }. configured is false, and nothing is fetched, when the marketplace is off or has no address. installed says what this Bitween has of each: { installed, fromRegistry, current, versions }.
GET /api/marketplace/package?id= subscriptions.view One registry package with its versions, the newest version's manifest, and what this Bitween has of it.
POST /api/marketplace/install adapter-source.operate Install a version from the registry: { id, version, current }. Every signature is checked first; see Adapters. Returns { adapterId, version, current, sha256, publisher }. Audited as an AdapterRelease with the action installed.
GET /api/marketplace/updates subscriptions.view What the last registry check found, read from the database: { configured, checkedOn, error, adapters: [{ adapterId, current, latest, updateAvailable, newer: [{ version, releaseNotes }], revoked: [{ version, reason, revokedOn }], listed, checkedOn }] }. See Updates and revocations.
POST /api/marketplace/check adapter-source.operate Ask the registry now rather than waiting for the daily check. Answers as updates; a registry that can't be reached is in error, not a failure.
GET /api/marketplace/kit?id=&version= subscriptions.view A kit's connectors and the parameters it asks for
POST /api/marketplace/kitplan adapter-source.operate { id, version, name, parameters }: what installing it would do, as { name, ready, applied, lines, problems, warnings, changes }. Changes nothing.
POST /api/marketplace/kitinstall adapter-source.operate The same, installed: its connectors, then its configuration, as the caller
POST /api/kits/plan?name=&examples=&set=NAME=value adapter-source.operate Plan a kit sent as its zip, the request body, as bitween kit build writes it, changing nothing. Answers as kitplan. With examples=true, a required parameter not given takes the kit's example, or a placeholder. The registry calls it on a reference Bitween to check each kit published.
POST /api/adapters/withdraw adapter-source.operate Take a published version out of use: { adapterId, version }. It stays listed but can't be pinned or made current.
GET /api/adapterdrafts?adapterId= adapter-source.edit The editor's drafts, newest first, optionally for one adapter
GET /api/adapterdrafts/{id} adapter-source.edit A draft with its files
POST /api/adapterdrafts adapter-source.edit Start a draft: { name, language, kind, adapterId }, where language is python, node or typescript and adapterId comes from the name when left out; or { fromAdapterId, fromVersion }. Returns the draft's id.
POST /api/adapterdrafts/{id} adapter-source.edit Save its files: { files: { path: content }, baseHash }. With baseHash, the draft's filesHash when it was opened, a save made after someone else's is refused with DRAFT_CHANGED; without it the save goes through.
DELETE /api/adapterdrafts/{id} adapter-source.edit Delete a draft
POST /api/adapterdrafts/{id}/build adapter-source.edit Build and check it: { settings, buildOnly }
POST /api/adapterdrafts/{id}/try adapter-source.edit Call a command: { settings, command, input }
POST /api/adapterdrafts/{id}/publish adapter-source.operate Publish a version, not made current: { version, releaseNotes, settings }. version is major, minor, patch (the default) or an exact version.
POST /api/mappingpreviews signed in, no permission checked Preview rules-based mapping
POST /api/mappingsuggestions subscriptions.create or subscriptions.edit Generate rules-based mapping rules with AI: { mappingRules, sourceDocument, targetDocument, partnerId, refine, instructions }. See Generate with AI.
POST /api/mappers subscriptions.edit Preview a legacy Scriban template

Adapter descriptions are cached per node. Publishing, promoting or withdrawing through the API clears them on every node; a package published straight to storage shows its old properties for up to a minute.

Publishing a package, promoting, withdrawing, and publishing from a draft are recorded in the audit trail as AdapterRelease rows. Drafts are recorded as AdapterDraft rows, with a hash of their files rather than the files.

Gateways#

Method and path Permission
GET /api/apigateways, GET /api/apigateways/{id} api-gateways.view
GET /api/apigateways/{id}/rejections api-gateways.view. Calls to the gateway turned away since the answering node started, counted by reason (not-authenticated, not-attached, gateway-off, no-subscription, rate-limited, unknown-gateway), with the most recent: when, status and caller address. Kept in memory per node.
GET /api/apigateways/attachments?apiGatewayId=&search=&offset=&limit= api-gateways.view
POST /api/apigateways api-gateways.create. { name, urlName, inactive, authentication?, failureAnswer? }. authentication is { method: "PartnerKey" | "Jwt", keyHeader, issuer, audience, partnerClaim }, and defaults to partner keys. An empty keyHeader uses the one in Settings. failureAnswer is "Accurate" (the default for a new gateway) or "Legacy"; see Entry points.
POST /api/apigateways/{id} api-gateways.edit. Same body. Leaving authentication or failureAnswer out keeps what the gateway has.
DELETE /api/apigateways/{id} api-gateways.delete
POST /api/apigateways/{id}/addpartner api-gateways.edit. { partnerId, subscriptionId } or { partnerId, newIntegration }
POST /api/apigateways/{id}/updatepartner api-gateways.edit
POST /api/apigateways/{id}/removepartner api-gateways.edit
GET /api/busgateways, GET /api/busgateways/{id} bus-gateways.view
POST /api/busgateways bus-gateways.create. { name, documentId, dataSourceId, endpoint }
POST /api/busgateways/{id} bus-gateways.edit
DELETE /api/busgateways/{id} bus-gateways.delete
POST /api/busgateways/{id}/addroute bus-gateways.edit. { subscriptionId or newIntegration, partnerId, matchExpression }
POST /api/busgateways/{id}/updateroute bus-gateways.edit
POST /api/busgateways/{id}/removeroute bus-gateways.edit

Data sources#

Method and path Permission
GET /api/datasources data-sources.view, or signed in with lookup=true. Searchy.
GET /api/datasources/Providers data-sources.view
GET /api/datasources/{id} data-sources.view
GET /api/datasources/{id}/telemetry data-sources.view
POST /api/datasources data-sources.create
POST /api/datasources/{id} data-sources.edit
DELETE /api/datasources/{id} data-sources.delete
POST /api/datasources/{id}/test data-sources.operate
POST /api/datasources/{id}/inspect data-sources.view. { command: "Discover" | "GetStats" | "Describe", arguments }
GET /api/datasourcestatements, GET /api/datasourcestatements/{id} data-source-statements.view
POST /api/datasourcestatements data-source-statements.create
POST /api/datasourcestatements/{id} data-source-statements.edit
DELETE /api/datasourcestatements/{id} data-source-statements.delete
POST /api/datasourcestatements/{id}/usage data-source-statements.view

See Data sources and Databases.

Configuration#

Method and path Permission
GET /api/documents, GET /api/documents/{id}, GET /api/documents/{id}/properties documents.view
POST /api/documents documents.create
POST /api/documents/{id} documents.edit
DELETE /api/documents/{id} documents.delete
GET /api/partners, GET /api/partners/{id} partners.view. Keys are masked. {id} also returns keysLastUsedOn: when a gateway last let a call through with each key, by name, to within five minutes.
POST /api/partners partners.create
POST /api/partners/{id} partners.edit. Replaces name, properties, login identity and API keys.
DELETE /api/partners/{id} partners.delete
GET /api/partners/generatekey signed in, no permission checked. Returns a random key as text; nothing is stored.
GET /api/globaladaptervaluessets, GET /api/globaladaptervaluessets/{id} global-values.view
POST /api/globaladaptervaluessets global-values.create. { id, name, values }
POST /api/globaladaptervaluessets/{id} global-values.edit
POST /api/globaladaptervaluessets/{id}/delete global-values.delete. Refused with GLOBAL_VALUES_IN_USE while a subscription or notification channel references the set in a {{globals.…}} token.
GET /api/workgroups?name=&offset=&limit= workgroups.view. Includes live queue metrics.
POST /api/workgroups workgroups.create. { name, busMessageName, options: { rabbitMqOptions: { prefetch, priority } } }
POST /api/workgroups/{id} workgroups.edit
POST /api/workgroups/{id}/delete workgroups.delete. Refused while subscriptions use it.
GET /api/retrypolicies, GET /api/retrypolicies/{id} retry-policies.view
POST /api/retrypolicies retry-policies.create. { name, groups, alertChannelId }; see Retry policies. An alert names a notification channel; alertHandlerId is refused.
POST /api/retrysuggestions subscriptions.view and retry-policies.create. Suggest a retry policy with AI from a subscription's failures: { subscriptionId, days } (1-90, default 30), replying with name, summary, groups (ready for POST /api/retrypolicies), coverage, model and sent. Saves nothing. See Suggesting a policy with AI.
POST /api/retrypolicies/{id} retry-policies.edit
DELETE /api/retrypolicies/{id} retry-policies.delete
POST /api/retrypolicies/test retry-policies.view
POST /api/retrypolicies/{id}/usage retry-policies.view
POST /api/retrypolicies/{id}/attempts retry-policies.view
POST /api/retrypolicies/{id}/resetusage retry-policies.edit
POST /api/retrypolicies/{id}/savealertoverride retry-policies.edit. { subscriptionId, groupId, alertMode, alertChannelId }
GET /api/notificationchannels, GET /api/notificationchannels/{id} notifiers.view. Secret settings come back as __private__.
GET /api/notificationchannels?lookup=true signed in. Ids and names only, for pickers.
POST /api/notificationchannels notifiers.create. { name, handlerId, handlerProperties }
POST /api/notificationchannels/{id} notifiers.edit. A __private__ value keeps the stored secret only while the adapter and where it connects stay the same.
POST /api/notificationchannels/{id}/test notifiers.edit. Sends one made-up notification through the channel as saved. Returns { sent, error }.
DELETE /api/notificationchannels/{id} notifiers.delete. Refused while anything still uses it: a subscription, a retry policy, a group or an alert override.
GET /api/notifiers, GET /api/notifiers/{id} notifiers.view
POST /api/notifiers notifiers.create
POST /api/notifiers/{id} notifiers.edit
DELETE /api/notifiers/{id} notifiers.delete
GET /api/notifications notifiers.view. Searchy. Each row has its channelId (null for a legacy notifier), whether it was suppressed by the quiet window, and the subscriptionId and subscriptionName it reported. ChannelId:1:{id} keeps one channel's sends.

Administration#

Method and path Permission
GET /api/accounts users.view. Each member carries lastSignInOn, microsoftLinked, hasPassword, and invite (expiresOn, emailedOn, emailError) while they are yet to choose a password.
POST /api/accounts users.create. { name, email, password, roleIds }, or invite: true instead of password.
POST /api/accounts/{id}/invite users.create, and every permission the member holds. For a member with no password: returns { emailed, emailError, expiresOn, link }, with link only when it wasn't emailed.
POST /api/accounts/{id} users.edit, or yourself for the display name
POST /api/accounts/{id}/setRoles users.edit
POST /api/accounts/{id}/setDisabled users.edit
POST /api/accounts/{id}/setPassword users.edit
POST /api/accounts/{id}/unlock users.edit
POST /api/accounts/{id}/remove users.delete
GET /api/serviceaccounts, GET /api/serviceaccounts/{id} service-accounts.view. Each with its roles and tokens (shown is the first 12 characters, never the token). Service accounts aren't in GET /api/accounts.
POST /api/serviceaccounts service-accounts.create. { name, roleIds }; only roles you hold.
POST /api/serviceaccounts/{id} service-accounts.edit. { name, roleIds }
POST /api/serviceaccounts/{id}/setDisabled service-accounts.edit. Stops every token, and sessions already open.
POST /api/serviceaccounts/{id}/issueToken service-accounts.create. { name, expiresInDays } (1 to 365, 90 by default; two active at most). The answer's token is the only time it is shown.
POST /api/serviceaccounts/{id}/revokeToken service-accounts.edit. { tokenId }
DELETE /api/serviceaccounts/{id} service-accounts.delete. Revokes its tokens.
GET /api/roles, GET /api/roles/{id} roles.view
POST /api/roles roles.create
POST /api/roles/{id} roles.edit
DELETE /api/roles/{id} roles.delete
GET /api/settings settings.view
GET /api/cluster/nodes settings.view. Every node as its heartbeat last described it: host, started, last seen, online (seen in the last 90 seconds), version, whether it runs data sources, its adapter runtimes, the leases it holds and the exclusive data sources it owns.
GET /api/settings/about settings.view. This instance as the answering node sees it: version, node, database, storage, broker, the readiness checks run now, which adapter runtimes and pip/npm the node has, the adapter editor's limits, rate limits and other effective settings from configuration. Never a secret.
POST /api/settings/{key} settings.edit. { value }
DELETE /api/settings/{key} settings.edit. Resets to the product default.
GET /api/retention?refresh= settings.view. What the retention settings do; refresh=true reads the bucket's rules again.
POST /api/retention/preview settings.view. The same for proposed values, without saving them.
GET /api/audit?offset=&limit=&entityName=&entityKey=&userId=&correlationId=&from=&to= audit.view
GET /api/mirror mirror.view. Whether this Bitween is a mirror (target), prod's address and how it signs in, without calling prod.
GET /api/mirror/prodkinds mirror.view. How many of prod's items there are of each kind a list page offers to bring, read from prod's lists alone: subscriptions by type, with all, apiGateways and busGateways. error says why prod couldn't be read.
GET /api/mirror/prod?section=&type= mirror.view. Prod's items of one kind (subscriptions, apiGateways or busGateways, and of subscriptions only one type when it is given), busiest first: each with its traffic over the last days (total, failed, lastOn, daily), a detail, its state on the staging (inSync, differs, missing or waiting) and its id on both. A prod whose release doesn't count traffic answers days: null with a trafficNote. A prod that can't be reached or is older than r10.0 answers { error }.
POST /api/mirror/bring mirror.operate. { section, keys, apply }. Brings prod's picked items with what they need to run, as Prod mirror describes: pieces, each with its state (new, kept, adds, waits or install), why and reason, needsYou, and with apply, what failed. Without apply it changes nothing. Refused with MIRROR_NOT_TARGET unless the deployment sets Bitween:Mirror:Target.
GET /api/mirror/subscription?id= mirror.view. One of this staging's subscriptions beside prod's of the same name: onProd, prodId, used (the subscription and each item it needs, each with its part, level (same, faked, info, check or different), prod's and the staging's side, a note and fields, then its adapters), toBring (what bringing it would bring, as POST /api/mirror/bring says it), needsYou (each with its group: needsYou, fill for an address, secret or login still to fill in, with the item's stagingId and prodId, action alerts when it only sends alerts or filledIn for a secret saved as set-me, to tick with POST /api/mirror/filledin once filled in, or faked) and customAdapters, the adapters it runs that don't come with Bitween. A prod that can't be read answers { error }.
GET /api/mirror/test?id= mirror.view. A subscription's test: state (idle, taking, running, queued while it waits its turn, or failed with an error), busy (other work of the mirror under way), done of total, prod's arrivals (families, takenOn, and a reason when there are none), how they last ran (runs, ranOn, setupError), exchanges, how many exchanges its runs left on staging, and last, how the last run went (verdict: same, check or different, ranOn, and how many arrivals were same, check, different or notRun), which a clear keeps.
POST /api/mirror/testtake mirror.operate. { id }. Starts taking prod's latest arrivals of the subscription in the background, and answers as GET /api/mirror/test does. Refused with MIRROR_NOT_TARGET unless the deployment sets Bitween:Mirror:Target, and with MIRROR_BUSY while other work of the mirror runs.
POST /api/mirror/testrun mirror.operate. { id }. Starts the subscription's test in the background: each arrival sent in on staging the way it came in on prod and compared with prod's. Answers and is refused as POST /api/mirror/testtake is. The test stops with an error while an address, secret or login it needs is still to fill in.
POST /api/mirror/testclear mirror.operate. { id }. Removes every exchange the subscription's test made on staging, with its files, and each arrival's results; prod's arrivals stay, and so does last. Refused with MIRROR_BUSY while the test runs.
GET /api/mirror/listed?section= mirror.view. This staging's items of one kind (subscriptions, apiGateways or busGateways) that prod has too, by name, read from prod's list: each with its id, key, a subscription's type, and its test's verdict (same, check, different, noTraffic when prod had none of it to take, or null when not tested) and ranOn; a gateway's are those of the subscriptions it leads to that prod has too, with how many were tested of them; and custom when it runs an adapter that doesn't come with Bitween. Also testing, the subscription whose test is under way, and waiting, those waiting their turn. error says why prod couldn't be read.
POST /api/mirror/testmany mirror.operate. { section, ids }. Starts testing the items one subscription after another in the background (a gateway through the subscriptions it leads to), taking prod's arrivals first where none are, or none were found last time; answers the subscriptions it tests. Only those prod has too. Refused with MIRROR_NOT_TARGET unless the deployment sets Bitween:Mirror:Target, with MIRROR_NONE when none of them is on prod, and with MIRROR_BUSY while other work of the mirror runs.
POST /api/mirror/filledin mirror.operate. { section, key }. Says staging's own secrets are in where a bring saved set-me on the item, because its adapter can't be saved without them; staging can't see a saved secret. Their to-dos (action filledIn) close and its tests can run. Answers how many it closed.
POST /api/mirror/teststop mirror.operate. Drops the tests waiting their turn; the one under way finishes. Answers how many were dropped.

Monitoring#

Method and path Permission
GET /api/ops/summary, consumers, queues, retries, deadletters, alerts, unattendedqueues monitoring.view or dashboard.view
GET /api/ops/background monitoring.view or dashboard.view. The system jobs (automatic retries, exchange retention, receive attempt clean-up, broker deduplication clean-up): cron, whether scheduled, state, last and next run, running now on this node. And the outbox: messages waiting for the broker, the oldest, how many after a failed attempt with the last error, and how many sent in the last hour.
GET /api/dashboard/MainInfo dashboard.view
GET /api/dashboard/ChartsDataPoints dashboard.view
GET /api/dashboard/XChangesAndSubscriptionsInfo dashboard.view
GET /api/dashboard/retrysummary dashboard.view. Retries finished in the last 7 days, and the five failing chains with the most attempts.
GET /health none