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.
- Read the requirements below and submit the access form.
- We create a workspace and send the bootstrap key over a private channel. The plaintext secret is shown once.
- 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-Keyon 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_managedand 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
/v1except 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 setReferrer-Policy: no-referrer.
You can only delegate scopes you already have. A workspace:write key cannot mint * and cannot revoke a bootstrap key.
Scopes
| Scope | What it allows |
|---|---|
workspace:read | Read workspace name, branding, and locale. |
workspace:write | Update workspace settings and branding. Required to create, rotate, or revoke keys you are allowed to delegate. |
hosts:read / hosts:write | List and manage bookable hosts. |
availability:read / availability:write | Weekly schedules, date overrides, and slot search. |
event_types:read / event_types:write | Meeting types, duration, location, and host assignment. |
bookings:read / bookings:write | Create bookings and run cancel, reschedule, complete, no-show, and reassign. |
proposals:read / proposals:write | Multi-slot proposals and atomic confirmation. |
contacts:read | Read managed invitees when you use managed_identity. |
routing:read / routing:write | Qualification forms that route invitees to event types. |
calendars:read / calendars:write | Google and Microsoft connections, busy import, and subscriptions. |
webhooks:read / webhooks:write | Signed 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_…"
}
invalid_api_key— Invalid API key (401)unauthenticated— Authentication required (401)invalid_booking_access_token— Invalid booking-access token (401)insufficient_scope— Insufficient scope (403)forbidden— Access denied (403)booking_access_forbidden— Booking-access forbidden (403)resource_not_found— Resource not found (404)validation_failed— Validation failed (422)idempotency_conflict— Idempotency conflict (409)booking_conflict— Booking conflict (409)event_type_slug_conflict— Event type slug conflict (409)webhook_delivery_conflict— Webhook delivery conflict (409)conflict— Conflict (409)http_error— Request failedinternal_error— Internal server error (500)
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]andpage[size]where lists are paginated. external_refandmetadataare yours. Keep them opaque if they point at private records.
Quickstart
After you have a key:
POST /availability-schedules— weekly hours.POST /hosts— attach the schedule.POST /event-types— duration, location, hosts, public slug.POST /availability/search— or send invitees to the hosted page.POST /bookingsor a slot proposal to confirm.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.
| Area | Routes | Notes |
|---|---|---|
| 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,
});