HTTP API

Documentation

Base URL: https://bkin-api-5ihekgqk.on-forge.com/v1. JSON in, JSON out. Errors are application/problem+json. This is a server-to-server API. Invitee browsers talk to your product; your backend talks to bkinAPI.

Get a key

There is no self-serve signup. Genzis reviews each workspace, then issues a one-time bootstrap key. After that you mint least-privilege keys yourself.

  1. Read the requirements below and submit the access form.
  2. We create a workspace and send the bootstrap key over a private channel. The plaintext secret is shown once.
  3. Store it in your backend secret store. Create a scoped key with POST /api-keys. Revoke the bootstrap key after that, or lock it away for break-glass only.

Key shape: bkin_test_… in non-production, bkin_live_… in production. Send it as:

Authorization: Bearer bkin_…

Never put a workspace key in a browser, mobile app, or invitee URL. Public booking pages and the embed widget do not take one.

Requirements

We issue keys only when all of this is true:

  • You are embedding scheduling in a real product, not scraping availability or sending unsolicited meeting spam.
  • Calls come from your servers. The bootstrap and integration keys stay in a secret store.
  • You have an HTTPS product or company URL we can review.
  • A human contact email that can receive operational mail (webhook failures, calendar disconnects).
  • A workspace slug of lowercase letters, digits, and hyphens, such as acme.
  • A default IANA timezone for the workspace.
  • You will send Idempotency-Key on every retryable write, and verify webhook HMAC before you trust an event.
  • Webhook URLs are HTTPS on port 443, public DNS, no credentials in the URL. Loopback and private networks are rejected.
  • If you keep invitee PII in your own vault, you use privacy_mode: client_managed and do not send names or emails to bkinAPI.

Sandbox keys are for development. Production keys are issued after the integration can search, book, cancel, and accept a signed webhook.

Auth

Two credential types:

  • Workspace API key — your backend. Scoped. Used on /v1 except public booking and health.
  • Booking-access token — one invitee, one booking. Bearer on /v1/booking-access. Never in query strings or Referer. Path-token URLs exist for compatibility and set Referrer-Policy: no-referrer.

You can only delegate scopes you already have. A workspace:write key cannot mint * and cannot revoke a bootstrap key.

Scopes

ScopeWhat it allows
workspace:readRead workspace name, branding, and locale.
workspace:writeUpdate workspace settings and branding. Required to create, rotate, or revoke keys you are allowed to delegate.
hosts:read / hosts:writeList and manage bookable hosts.
availability:read / availability:writeWeekly schedules, date overrides, and slot search.
event_types:read / event_types:writeMeeting types, duration, location, and host assignment.
bookings:read / bookings:writeCreate bookings and run cancel, reschedule, complete, no-show, and reassign.
proposals:read / proposals:writeMulti-slot proposals and atomic confirmation.
contacts:readRead managed invitees when you use managed_identity.
routing:read / routing:writeQualification forms that route invitees to event types.
calendars:read / calendars:writeGoogle and Microsoft connections, busy import, and subscriptions.
webhooks:read / webhooks:writeSigned webhook endpoints, deliveries, and retries.
*Unrestricted bootstrap key. Issued once when a workspace is created. Do not ship it in your app; mint least-privilege keys from it.

Idempotency

Every retryable create or lifecycle POST requires Idempotency-Key. Derive it from your own operation, not a random UUID per attempt.

Idempotency-Key: acme:meeting:123:confirm:v1

Retry timeouts with the same key and the same body. A changed body needs a new version (…:v2) or you get idempotency_conflict. Public booking retries are scoped to the invitee’s bkin_public_actor cookie so two browsers cannot replay each other.

Errors

Failed API calls return application/problem+json. Echo X-Request-Id (or accept the one we generate) when you escalate.

{
  "type": "https://docs.bkin.genzis.net/errors/booking_conflict",
  "title": "Conflict",
  "status": 409,
  "code": "booking_conflict",
  "detail": "One or more required hosts are no longer available.",
  "request_id": "req_…"
}

Limits and conventions

  • Authenticated workspace routes: 300 requests / minute / key.
  • Public booking, embed, and routing pages: 60 / minute / IP.
  • Booking-access reads: 60 / minute. Lifecycle posts: 30 / minute.
  • Access form: 5 submissions / hour / IP.
  • JSON only. Timestamps in ISO 8601; stored in UTC with a preserved IANA timezone field.
  • Cursor pagination uses page[after] and page[size] where lists are paginated.
  • external_ref and metadata are yours. Keep them opaque if they point at private records.

Quickstart

After you have a key:

  1. POST /availability-schedules — weekly hours.
  2. POST /hosts — attach the schedule.
  3. POST /event-types — duration, location, hosts, public slug.
  4. POST /availability/search — or send invitees to the hosted page.
  5. POST /bookings or a slot proposal to confirm.
  6. POST /webhook-endpoints — subscribe before you take real bookings.
export BKIN_API_URL=https://bkin-api-5ihekgqk.on-forge.com/v1
export BKIN_API_KEY=bkin_…

curl -sS "$BKIN_API_URL/workspace" \
  -H "Authorization: Bearer $BKIN_API_KEY"

curl -sS "$BKIN_API_URL/availability-schedules" \
  -H "Authorization: Bearer $BKIN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: acme:schedule:intro:v1" \
  -d '{"name":"Weekdays","timezone":"America/New_York","is_default":true}'

curl -sS "$BKIN_API_URL/availability/search" \
  -H "Authorization: Bearer $BKIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"event_type_id":"…","from":"2026-09-01T00:00:00Z","to":"2026-09-08T00:00:00Z"}'

curl -sS "$BKIN_API_URL/bookings" \
  -H "Authorization: Bearer $BKIN_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: acme:meeting:123:confirm:v1" \
  -d '{
    "event_type_id": "…",
    "starts_at": "2026-09-03T13:00:00Z",
    "timezone": "America/New_York",
    "external_ref": "acme:meeting:123",
    "invitee": {
      "privacy_mode": "client_managed",
      "external_ref": "acme:contact:C-9",
      "timezone": "America/New_York",
      "locale": "en"
    }
  }'

Resources

Paths below are relative to https://bkin-api-5ihekgqk.on-forge.com/v1.

AreaRoutesNotes
System GET / , /health , /ready No API key. Readiness checks database connectivity.
Workspace GET/PATCH /workspace Name, timezone, locale, branding, embed origins.
API keys GET/POST /api-keys , POST /api-keys/{id}/rotate|revoke Plaintext secret is returned only on create and rotate.
Hosts GET/POST /hosts , GET/PATCH /hosts/{id} Attach an availability schedule. Hosts are the people who take meetings.
Availability GET/POST /availability-schedules , PATCH /availability-schedules/{id} , overrides (GET/POST/DELETE) , POST /availability/search Search returns bookable instants plus degraded_host_ids when a conflict calendar is stale.
Event types GET/POST /event-types , GET/PATCH/DELETE /event-types/{id} , POST …/clone Public slug is used on hosted pages: /book/{workspace}/{slug}.
Bookings GET/POST /bookings , GET/PATCH /bookings/{id} , cancel, reschedule, complete, no-show, reassign-host, automations, access-tokens Writes require Idempotency-Key. Conflicts return booking_conflict.
Proposals GET/POST /slot-proposals , GET …/{id} , POST …/confirm|expire|decline Offer several slots; confirmation books exactly one. Decline closes the proposal and emits slot_proposal.declined.
Contacts GET /contacts , GET /contacts/{id} Only when invitees use managed_identity.
Public pages GET /booking-pages/{workspace}[/{event}] , GET …/slots , POST …/bookings No workspace key. Invitee cookies scope public idempotency.
Booking access GET /booking-access , POST /booking-access/cancel|reschedule , path-token variants Bearer management token. Never put the token in a query string.
Routing GET/POST /routing-forms , GET/POST /routing-pages/{workspace}/{form} Public submit has no workspace key.
Calendars GET /calendar-connections , OAuth, calendars, busy import, health, subscriptions Stores busy ranges, not event titles. Stale connections are not treated as free.
Webhooks GET/POST /webhook-endpoints , GET /webhook-deliveries , POST …/retry HTTPS only. HMAC over timestamp + raw body.
Workflows GET/POST /workflow-rules , PATCH /workflow-rules/{id} Follow-up automations after booking lifecycle events.
Ops GET /analytics/summary , GET /audit-events , GET /operations/health Workspace-scoped operational reads.

Privacy

managed_identity stores name, email, and phone on the invitee so bkinAPI can send lifecycle mail. client_managed stores only a display label and your external_ref. Webhooks then omit private identity. Your product sends the invitation using the access URL we return.

Calendar sync stores busy ranges and hashed provider event IDs, not titles or attendees. Public slot search never discloses why a time is busy.

Webhooks

Create an endpoint, keep the signing_secret shown once, and subscribe to types you can handle. Verify the signature over the exact unparsed body before JSON decode. Reject timestamps older than five minutes. Insert Bkin-Event-Id into a unique inbox in the same transaction as your state change. Return 2xx only after durable accept; replays return 2xx without applying the transition again. Redirects are not followed.

HMAC-SHA256(signing_secret, "{Bkin-Timestamp}.{rawBody}")
Bkin-Signature: v1=<hex>
Bkin-Event-Id: …
Bkin-Webhook-Id: …
Bkin-Timestamp: 1710000000

Event types: slot_proposal.created, slot_proposal.declined, slot_proposal.expired, booking.created, booking.confirmed, booking.rescheduled, booking.cancelled, booking.completed, booking.no_show, booking.host_reassigned, booking.sync_failed, calendar.connection.created, calendar.connection.degraded, calendar.connection.recovered, workflow.completed, workflow.failed

Hosted pages and embed

Standalone: https://bkin-api-5ihekgqk.on-forge.com/book/{workspace}/{event}. Embed: /embed/{workspace}/{event} (allowlist origins on the workspace). Or:

<div data-bkin-workspace="acme" data-bkin-event="intro-call"></div>
<script src="https://bkin-api-5ihekgqk.on-forge.com/bkin-widget.js" async></script>

Listen for bkin:booked on the host page. The event does not include the management token or invitee contact data. JSON equivalents live under /v1/booking-pages/….

Calendars

Google Calendar and Microsoft 365 are supported. Start OAuth from POST /calendar-connections/{provider}/authorization, select calendars, then subscribe to change notifications. A degraded connection is not treated as free time. Meeting links: Google Meet through Google Calendar; Teams through Microsoft Graph.

OpenAPI and client

Machine-readable contract: https://bkin-api-5ihekgqk.on-forge.com/openapi.yaml. Generate the TypeScript client with npm run api:client:generate, or call createBkinClient from clients/typescript.

import { createBkinClient } from '@genzis/bkin-api-client';

const bkin = createBkinClient({
  baseUrl: process.env.BKIN_API_URL,
  apiKey: process.env.BKIN_API_KEY,
});