Developers
Realtime and the SDK
The SDK keeps one live connection to Bekom. Calls go over it, and Bekom pushes what changes back over it, so there is nothing to poll.
Install and connect
npm install @bekom/sdk
import bekom from '@bekom/sdk';
// 'production' takes bkm_live_ keys; 'testing' takes bkm_test_ keys.
const client = bekom.connect('production', { apiKey: process.env.BEKOM_API_KEY });
const me = await client.describe(); // who this key is, its scopes and routesFor buyers: your notices and the bids you receive
const notices = await client.notices.list();
const bids = await client.notices.bids(noticeId);
if (bids.sealed) {
console.log(`${bids.count} sealed bids; they open at ${bids.closingAt}`);
} else {
const bid = await client.bids.get(bids.rows[0].id); // sections, files, versions, signature
await client.bids.review(fileId, 'resubmit', 'Please send the S3 data sheet.');
}For suppliers: the board, and preparing a bid
const { items } = await client.tenders.list({ closing: 'open', province: 'Gauteng' });
const fits = await client.tenders.fit({ bucket: 'qualify' }); // Qualify plan
// Prepare a bid on a notice published on Bekom. A person signs it in the dashboard.
await client.submissions.addFile(noticeId, {
file: { data: fs.readFileSync('tax.pdf'), name: 'tax.pdf', type: 'application/pdf' },
section: 'tax',
label: 'SARS tax compliance status',
description: 'PIN valid to March 2027',
});Listen first, then send
Bekom never answers through a callback. Every request x is answered by an xSuccess or an xError event. Register your listeners for both first, then send. A listener stays until you stop it, so it also hears results and errors caused by any other part of your program.
const stop = client.listen('listMyNotices', {
success: (notices) => render(notices),
error: (e) => console.error(e.code, e.message),
});
client.send('listMyNotices'); // the answer reaches the listener above
stop(); // when you no longer want themThe helpers (await client.notices.list() and the rest) do the same thing for one request: listen, send, and settle on whichever answer comes. Calls to the same event are queued, so two that overlap each get their own answer, never each other's.
The connection itself
client.on('disconnected', (reason) => showOffline(reason)); // calls in flight fail now, code 'disconnected'
client.on('reconnected', () => client.send('listMyNotices')); // fetch again what you show
client.on('connectionError', (e) => console.error(e.code)); // e.g. a revoked keyLive pushes
client.on('notification', (n) => console.log(n.title, n.href));
client.on('tenderIngested', () => refreshBoard());notification: Something your organisation should act on: a bid received, a file reviewed, a comment.tenderIngested: The board changed: new or updated tenders are available.
Errors
try {
await client.tenders.fit();
} catch (e) {
if (e.code === 'plan_required') console.log('Needs the', e.needs, 'plan');
if (e.code === 'scope_required') console.log('Create a key with the right scope');
if (e.code === 'disconnected') console.log('The connection dropped; send it again');
}Without the SDK
The live connection is Socket.IO on the /v1 namespace, with the key in auth.apiKey. Every call is an event named as in the REST reference; Bekom answers with <event>Success or <event>Error. The SDK is the supported way; this is here so nothing is hidden.