Install flow (Flow A)
This is the flow for a marketplace integration. It starts inside Bito: a merchant opens Integration → Integration, finds your integration in the marketplace and presses its install button (Install free or External payment). Bito then delivers an authorization code straight to your server. Nothing is redirected through the merchant's browser, so you do not need a callback page — only a callback endpoint.
Merchant (in Bito) Bito Your server
| | |
| 1. presses Install | |
|------------------------->| |
| | 2. POST redirect_uri |
| | { code, client_id, |
| | account_id } |
| |--------------------------->|
| | |
| | 3. POST /auth/oauth/token |
| |<---------------------------|
| | { access_token, |
| | refresh_token } |
| |--------------------------->|
| | |
| 4. uses your integration| |1. The merchant installs your integration
Nothing to implement. The Install button appears for every merchant once your integration is PUBLIC; while it is PRIVATE only your own account sees it, which is exactly what you want while developing.
2. Bito posts the authorization code to your redirect_uri
Bito sends a POST — not a redirect — to the redirect_uri you registered when you created the integration.
Request Bito sends you
POST https://your-app.example.com/bito/install
Content-Type: application/json
X-Bito-Signature: t=1736937000,v1=6b1f0a...c94e
{
"code": "abc123def456",
"client_id": "69c50a2d50f7b939923a469b",
"account_id": "507f1f77bcf86cd799439011"
}| Field | Description |
|---|---|
code | The authorization code. Single use, expires in 20 minutes. |
client_id | Your OAuth client id — check it matches yours before acting. |
account_id | The merchant account that installed you. Store it: this is how you tell your tenants apart. |
X-Bito-Signature | Request signature — see below. |
Verify the request really came from Bito
This POST is signed with your client_secret. Only Bito and you hold that secret, so a matching signature proves the request is genuine and a third party cannot forge an install.
- Header format:
X-Bito-Signature: t=<unix seconds>,v1=<hex> v1 = HMAC_SHA256(client_secret, "{t}.{raw_body}")— computed over the exact raw request body, byte for byte. Do not re-serialize the JSON.- Recompute
v1and compare it with the header in constant time; reject the request if they differ. - Reject requests whose
tis more than ~5 minutes from the current time — this blunts replay attacks. - Also confirm
client_idandaccount_idmatch the tenant you expect.
Example (Node.js)
const crypto = require('crypto');
function verifyBitoSignature(rawBody, headers, clientSecret, now = Date.now()) {
const m = /t=(\d+),v1=([a-f0-9]+)/.exec(headers['x-bito-signature'] || '');
if (!m) return false;
const [, t, v1] = m;
if (Math.abs(now / 1000 - Number(t)) > 5 * 60) return false; // stale / replay
const expected = crypto
.createHmac('sha256', clientSecret)
.update(t + '.' + rawBody)
.digest('hex');
const a = Buffer.from(expected, 'hex');
const b = Buffer.from(v1, 'hex');
return a.length === b.length && crypto.timingSafeEqual(a, b);
}WARNING
Answer with 2xx quickly and do the token exchange in the background if you need to. Bito does not retry this call.
3. Exchange the code for tokens
Within 20 minutes, call the token endpoint from your server — never from a browser, because the call carries your client_secret.
See Working with tokens → Exchanging the authorization code.
4. Store the tokens against the account
Keep access_token, refresh_token and account_id together. One merchant equals one token pair. When the access token expires, refresh it.
TIP
Also implement the uninstall hook so you can drop the merchant's data when they remove your integration.