Skip to content
Bitween AI is here
Client SDK

More

Client SDK

Exchanges from your own program, in .NET or JavaScript: search, read files, retry, bulk retry and export

The client SDK lets your own program work with a Bitween's exchanges, as the Exchanges and Scheduled retries pages do: search them, read their files, follow their retry chains, retry one or many, and export their files. It comes for .NET (SimplyWorks.Bitween.Client on NuGet) and for JavaScript and TypeScript (@simplyworks/bitween-client on npm), with the same calls in each.

It calls the same API the admin UI calls, and answers with the same rows. Each release of Bitween from releases/r10.0 publishes both packages at Bitween's version; use the version of the Bitween you call, or an earlier one.

A service account#

The SDK signs in as a service account (Team > Service accounts in Bitween). Create one, give it a role, and issue it a token. The token, starting bwt_, is shown once.

To The role needs
Search exchanges, read their files and retry chains, see scheduled retries, check and export exchanges.view
Retry, bulk retry, and run a scheduled retry now exchanges.operate (without exchanges.view too, a retry can't answer with the new attempt's id)

The SDK trades the token for a session on its first call, and again whenever the session runs out or is refused, so the token is all you hand it.

Keep the token on your server. A browser app calls your own back end, and your back end calls Bitween: a token in browser code can be read by anyone using the page, and does all its role allows.

.NET#

dotnet add package SimplyWorks.Bitween.Client

It needs .NET 10.

using SW.Bitween.Client;

using var bitween = new BitweenClient("https://bitween.example.com", token);

var failed = await bitween.Exchanges.SearchAsync(new ExchangeQuery
{
    Status = ExchangeStatus.Failed,
    From = DateTimeOffset.UtcNow.AddDays(-1),
    PartnerIds = [7],
});

foreach (var exchange in failed.Result)
    Console.WriteLine($"{exchange.Id} {exchange.DocumentName}: {exchange.Exception}");

var first = failed.Result.FirstOrDefault();
if (first != null)
{
    var input = await bitween.Exchanges.ReadFileAsync(first.InputKey);
    var retryId = await bitween.Exchanges.RetryAsync(first.Id);
}

Keep one BitweenClient for the life of the program, as a singleton in dependency injection: it can be used from many threads at once, and they share one session. To send through an HttpClient of your own, pass it with the Bitween's address as its BaseAddress: new BitweenClient(httpClient, token).

The rows and plans are the API's own types, from SimplyWorks.Bitween.Sdk, in SW.Bitween.Model: XchangeRow, XchangeRetryTree, XchangeBulkRetryPlan, XchangeFilesExportCheck and DelayedRetryRow.

JavaScript and TypeScript#

npm install @simplyworks/bitween-client

It needs Node 22 or later, and has no dependencies.

const { BitweenClient } = require("@simplyworks/bitween-client");

const bitween = new BitweenClient({ url: "https://bitween.example.com", token: process.env.BITWEEN_TOKEN });

const failed = await bitween.exchanges.search({ status: "failed", from: new Date(Date.now() - 86400e3), partnerIds: [7] });
const [first] = failed.result;
if (first) {
  const input = await bitween.exchanges.readFile(first.inputKey);
  const retryId = await bitween.exchanges.retry(first.id);
}

Types come with it. Rows have the API's camelCase names, and times are ISO strings.

What it does#

.NET JavaScript Does
Exchanges.SearchAsync(query, page, size, count) exchanges.search(query, { page, size, count }) A page of the exchanges the query matches, newest first, and totalCount. Pages count from 0; 25 a page unless asked. The count stops at 10,001, meaning more than 10,000; count: false leaves it out, as 0, and answers sooner.
Exchanges.GetAsync(id) exchanges.get(id) One exchange, or null
Exchanges.ReadFileAsync(key) exchanges.readFile(key) A file's text, by the row's inputKey, outputKey or responseKey
Exchanges.GetRetryTreeAsync(id) exchanges.retryTree(id) Every attempt in the retry chain, oldest first
Exchanges.RetryAsync(id, reset) exchanges.retry(id, { reset }) Runs it again from its input, and answers with the new attempt's id: null when it can't look that up, because the account may retry but not view or the lookup failed. The retry is made either way. reset uses the subscription's current settings.
Exchanges.PreviewBulkRetryAsync(selection) exchanges.previewBulkRetry(selection) What a bulk retry would do, changing nothing
Exchanges.BulkRetryAsync(selection) exchanges.bulkRetry(selection) Retries a selection, and says what it did
Exchanges.CheckExportAsync(selection) exchanges.checkExport(selection) How many the export takes, and how many are older than storage keeps files
Exchanges.ExportAsync(selection) exchanges.export(selection) The files as one zip, a folder for each exchange, as a stream
ScheduledRetries.SearchAsync(query, page, size) scheduledRetries.search(query, { page, size }) The automatic retries waiting to run, soonest first
ScheduledRetries.RunNowAsync(id) scheduledRetries.runNow(id) Runs an exchange's booked automatic retry now

Queries#

A query filters the way the Exchanges page does. What is left unset matches any; everything set has to hold.

Field Matches
Status / status Processing, Success, BadResponse, Failed (processing, success, badResponse, failed)
From, To / from, to Started at or after, at or before
PartnerIds, SubscriptionIds, InformationTypeIds Any of these
Ids / ids These exchanges, with the retry of each and those each rolled up, as the page's id search does
RetryFor / retryFor The retry of this exchange
CorrelationId / correlationId This correlation id
LatestOnly / latestOnly Only the newest attempt of each retry chain
ErrorContains / errorContains The error contains the text, ignoring case
PromotedProperties / promotedProperties By key: the value contains the text, or with no text (null or blank), the key was promoted
Filters / filters Filters in Bitween's own Field:Rule:Value form, for the rest. See Exchanges.

A scheduled retry query takes SubscriptionId, InformationTypeId, ErrorContains, and From and To for when it is due.

Selections#

Bulk retry and export take a selection: exchanges by id, or every exchange a query matches, however many pages that is, with some left out. A query with an empty list of ids, partners, subscriptions or information types is refused: an empty list adds no filter, so it would widen the selection rather than narrow it.

ExchangeSelection.Of(["a1b2...", "c3d4..."])
ExchangeSelection.Matching(new ExchangeQuery { Status = ExchangeStatus.Failed, PartnerIds = [7] }, except: ["a1b2..."])
{ ids: ["a1b2...", "c3d4..."] }
{ matching: { status: "failed", partnerIds: [7] }, except: ["a1b2..."] }

A bulk retry retries each chain once, from its newest attempt, and skips what succeeded or has an automatic retry booked, saying why. It carries out at most 500 at a time: past that the plan comes back with overLimit set and nothing done. To retry more, take targets from the preview, which plans up to 500,000, and retry them by id, 500 at a time. An export takes at most 500 exchanges.

Errors#

A refusal throws BitweenClientException in .NET and BitweenClientError in JavaScript. Both carry the HTTP status, Bitween's reasons by code, and the first code alone.

Code or status Means
ALREADY_RETRIED An exchange is retried at most once. Retry its newest attempt, which the row's hasRetry and the retry chain show.
AUTO_RETRY_SCHEDULED An automatic retry is booked for it. Run that now instead.
TOO_MANY An export of more than 500
DATABASE_TIMED_OUT (504) The query took longer than the database allows. Ask for less, such as a shorter time range.
400 at sign-in The token is unknown, expired or revoked, or its account is disabled
403 The account's roles don't have the permission the call needs