Beta: the Biskèt Partner API may still change before v1 is final. Sandbox access is by request:partners@bisket.ai.
Connect a restaurant
A grant is the restaurant owner’s permission for your partner account at one location. Only the owner can create it, on Biskèt’s hosted consent page. You never send a grant: Biskèt finds it from your key and the location.
sequenceDiagram autonumber actor Owner as Restaurant owner participant P as Your app participant C as connect.bisket.ai participant API as api.bisket.ai Owner->>P: "Connect Biskèt" P->>P: code_verifier, code_challenge = S256(verifier), state P->>C: redirect /authorize?client_id&redirect_uri&scope&state&code_challenge C->>Owner: sign in, pick the location, review the scopes Owner->>C: approve C->>P: redirect_uri?code&state P->>P: check state P->>API: POST /v1/oauth/token (code, code_verifier, redirect_uri) API-->>P: grant (location_id, scopes) API-->>P: webhook grant.created
// Connect: the PKCE pair and the authorize URL a restaurant owner opens (OAuth 2.0 + PKCE).import { createHash, randomBytes } from 'node:crypto';
const b64url = (b: Buffer) => b.toString('base64url');
export function startConnect(clientId: string, redirectUri: string, sandbox = true) { const codeVerifier = b64url(randomBytes(32)); // keep it server-side for this attempt const codeChallenge = b64url(createHash('sha256').update(codeVerifier).digest()); const state = b64url(randomBytes(16)); // check it on the way back const host = sandbox ? 'https://connect.sandbox.bisket.ai' : 'https://connect.bisket.ai'; const url = new URL('/authorize', host); url.search = new URLSearchParams({ client_id: clientId, redirect_uri: redirectUri, response_type: 'code', state, scope: 'locations:read menu:read availability:read orders:read orders:write events:read settlement:read settlement:write sales_summary:read', code_challenge: codeChallenge, code_challenge_method: 'S256' }).toString(); return { url: url.toString(), codeVerifier, state };}
export async function main() { const s = startConnect('example-delivery', 'https://partner.example/bisket/callback'); const challenge = new URL(s.url).searchParams.get('code_challenge'); return { url: s.url, challengeMatches: challenge === b64url(createHash('sha256').update(s.codeVerifier).digest()) };}- The
redirect_urimust match one Biskèt registered for you byte for byte (https, no wildcard). - The code is valid 10 minutes and once. Exchange it from your server with the verifier you generated for this attempt.
- The owner can disconnect at any time: you receive
grant.revoked. New orders are then refused (403 grant_revoked); orders already placed and settlement continue.
| Error | Meaning |
|---|---|
location_not_supported on the consent page |
The location cannot take partner orders yet (opening hours or shifts missing). The owner sees how to fix it. |
| already connected | The location already has an active grant for you: the page says so, nothing changes. |
invalid_authorization_code |
Expired, used or unknown code. Start again. |
pkce_verification_failed |
The verifier does not match the challenge. |
redirect_uri_mismatch |
Not a registered URI. |
To set up a restaurant that does not use Biskèt yet, file an install request (POST /v1/install_requests);
you are notified with install_request.updated, which carries the Connect link once the restaurant is ready.