Skip to content
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: authorization code with PKCE
connect-pkce.ts
// 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_uri must 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.