Besmi·Developers

Besmi API · v1

Build on Besmi.

A small, clean API for building custom booking experiences on top of your Besmi business: a marketing site, an internal dashboard, a partner integration, anything. Same auth model, same data, just yours to compose.

Quick start

  1. i.
    Generate an API key. Sign in to Besmi, open Settings → Dev, click New key. Copy the key the moment it appears. Besmi only stores a hash, so it can't be shown again.
  2. ii.
    Make a request. Pass the key as a Bearer token. Every endpoint is scoped to your business. You never specify businessId.
  3. iii.
    Build whatever you want. Custom booking page, mobile app, agency dashboard, scheduled sync: all the same calls.

Your first request

curl https://besmi.com/api/v1/business \
  -H "Authorization: Bearer bsk_live_your_key_here"

Authentication

Every /api/v1/* endpoint accepts either an API key (recommended for server-to-server) or a Firebase ID token (used by the Besmi first-party UI). Both pass through the same auth pipeline. The businessId is derived from the token, never supplied as a parameter.

Header
Authorization: Bearer bsk_live_…
Key format
bsk_live_ prefix · 24 random bytes (url-safe base64) · ~40 characters total
Scopes
read for GET endpoints · write for POST / PATCH
Storage
Besmi stores only a SHA-256 hash + a 16-character prefix for lookup. Plaintext surfaces exactly once on creation. If you lose a key, generate a new one.
Revocation
Soft-revoke from Settings → Dev. Requests with a revoked key return 401 on the next call.
Treat keys as production credentials. Each key inherits your business's full read & write scope. Don't embed in client-side JavaScript or commit to git. Use environment variables and rotate on team changes.

Endpoints

Base URL: https://besmi.com

GET/api/v1/business

Read business profile

Returns name, slug, contact, hours, timezone, theme, landing-page modules, and notification preferences. Stripe / Twilio / partner identifiers are scrubbed.

curl https://besmi.com/api/v1/business \
  -H "Authorization: Bearer bsk_live_…"

Response · 200

{
  "business": {
    "id":       "biz_xxx",
    "name":     "Your Esthi Bestie",
    "slug":     "mlc",
    "timezone": "America/Los_Angeles",
    "contact":  { "email": "...", "phone": "..." },
    "schedule":    { /* working hours */ },
    "theme":       { /* colors + fonts */ },
    "landingPage": { /* modules */ }
  }
}
GET/api/v1/services

List services & categories

Returns all services and their categories, in display order. Use this to populate a custom service picker.

curl https://besmi.com/api/v1/services \
  -H "Authorization: Bearer bsk_live_…"

Response · 200

{
  "categories": [
    { "id": "cat_xxx", "name": "Lashes", "order": 0, "color": "#C026D3", "services": [] }
  ],
  "services": [
    {
      "id":              "svc_xxx",
      "categoryId":      "cat_xxx",
      "name":            "Classic Full Set",
      "price":           18000,
      "durationMinutes": 120,
      "bufferMinutes":   15,
      "bookableOnline":  true,
      "addons":          []
    }
  ]
}
GET/api/v1/availability

Get available slots

Returns bookable time slots for a given date and service duration. Accounts for working hours, blocked windows, buffers, and existing appointments.

Query parameters

date
string
YYYY-MM-DD in the business's timezone
durationMinutes
number
Service duration. Default 60. Range 0–480.
curl "https://besmi.com/api/v1/availability?date=2026-05-15&durationMinutes=120" \
  -H "Authorization: Bearer bsk_live_…"

Response · 200

{
  "slots": [
    { "start": "2026-05-15T17:00:00.000Z", "end": "2026-05-15T19:00:00.000Z" },
    { "start": "2026-05-15T19:30:00.000Z", "end": "2026-05-15T21:30:00.000Z" }
  ]
}
GET/api/v1/appointments

List appointments

Lists appointments for the authenticated business, optionally filtered by date range and status. Cursored pagination via from / nextCursor.

Query parameters

from
string
ISO 8601, inclusive lower bound on startAt
to
string
ISO 8601, exclusive upper bound on startAt
status
string
Comma-separated subset of pending,confirmed,cancelled,noshow
limit
number
Default 50, max 200
curl "https://besmi.com/api/v1/appointments?from=2026-05-01&to=2026-06-01&status=confirmed,pending" \
  -H "Authorization: Bearer bsk_live_…"
POST/api/v1/appointments

Create an appointment

Books a new appointment on behalf of the authenticated business. Requires the write scope. Payment isn't handled here. Use Besmi's built-in payment flow or your own Stripe Connect integration.

Request body

{
  "serviceId":  "svc_xxx",
  "startAt":    "2026-05-15T17:00:00.000Z",
  "endAt":      "2026-05-15T19:00:00.000Z",
  "client": {
    "firstName": "Jordan",
    "lastName":  "Lee",
    "email":     "jordan@example.com",
    "phone":     "+15555550100"
  },
  "addonIds": ["addon_xxx"],
  "status":   "confirmed",
  "note":     "Allergic to acrylic"
}
curl -X POST https://besmi.com/api/v1/appointments \
  -H "Authorization: Bearer bsk_live_…" \
  -H "Content-Type: application/json" \
  -d '{
    "serviceId": "svc_xxx",
    "startAt":   "2026-05-15T17:00:00.000Z",
    "endAt":     "2026-05-15T19:00:00.000Z",
    "client": { "firstName": "Jordan", "email": "jordan@example.com", "phone": "+15555550100" }
  }'
GET/api/v1/appointments/{id}

Read one appointment

Fetches a single appointment by id.

curl https://besmi.com/api/v1/appointments/apt_xxx \
  -H "Authorization: Bearer bsk_live_…"
PATCH/api/v1/appointments/{id}

Update an appointment

Patch status, time, addons, owner note, or client contact. Payment fields are intentionally NOT patchable. Use the dedicated Stripe surfaces so Stripe stays the source of truth.

Request body (any subset)

{
  "status":   "cancelled",
  "startAt":  "2026-05-16T17:00:00.000Z",
  "endAt":    "2026-05-16T19:00:00.000Z",
  "addonIds": ["addon_xxx"],
  "note":     "Client called to reschedule",
  "client":   { "phone": "+15555550101" }
}
curl -X PATCH https://besmi.com/api/v1/appointments/apt_xxx \
  -H "Authorization: Bearer bsk_live_…" \
  -H "Content-Type: application/json" \
  -d '{ "status": "cancelled" }'
GET/api/v1/customers

List customers

Returns customer records, optionally filtered by a name / email / phone substring.

Query parameters

q
string
Case-insensitive substring filter
limit
number
Default 100, max 500
curl "https://besmi.com/api/v1/customers?q=jordan&limit=20" \
  -H "Authorization: Bearer bsk_live_…"

MCP connector

Besmi ships an MCP connector. MCP (Model Context Protocol) is the open standard AI assistants use to call external tools, so an assistant such as Claude or ChatGPT can work directly against a Besmi account: read the schedule, book, reschedule and cancel appointments, look up clients, pull stats, manage discounts, and edit the public page.

This is separate from the REST API above. The REST API is for code you write; the MCP connector is for an AI assistant acting on the owner's behalf. It does not use API keys. Connection is authorized by the business owner through OAuth 2.1, and can be revoked at any time from Settings → Connect.

Endpoint

https://besmi.com/api/mcp

Auth

OAuth 2.1 with PKCE. No API key. The owner approves the connection once.

Tools

54 tools: schedule, hours and locations, availability, bookings (options, add-ons, stylists, standing appointments), clients, sales and payouts (read only), the receptionist inbox, waitlist, marketing, and an AI page designer (drafts, renders, design review, Looks)

Protocol

Streamable HTTP, protocol 2025-11-25 (and older); MCP Apps page-preview view at ui://besmi/page-preview

Scopes

read (look things up), book (schedule, inbox, waitlist), design (the page, as drafts), manage (client records, discounts, marketing). The owner sees them on the consent screen.

Discovery

401 responses carry WWW-Authenticate pointing at /.well-known/oauth-protected-resource (RFC 9728), which names the authorization server at /.well-known/oauth-authorization-server (RFC 8414).

Clients

Dynamic client registration (RFC 7591) at /api/oauth/register. Public clients only: authorization code + PKCE S256, refresh tokens, revocation at /api/oauth/revoke.

Plans

Connecting needs a Pro or Icon plan. A few tools also need a feature from a higher plan; tools/list marks those, and calling one returns an upgrade note.

Any MCP client that supports remote servers with OAuth can connect, not only Claude and ChatGPT. Point it at the endpoint and it discovers the rest. Tools are annotated honestly (readOnlyHint, destructiveHint, idempotentHint), failures come back with isError, and results include structuredContent. Design tools also return a PNG of the page so the model can see its work.

Tools (54)

Studio

get_business_overviewread
list_servicesread
list_teamread
get_hoursread

Schedule and availability

get_scheduleread
get_upcomingread
get_pending_requestsread
check_availabilityread
find_next_availabilityread
block_timebook · writes
set_day_offbook · writes
set_special_hoursbook · writes

Bookings

create_appointmentbook · writes
confirm_appointmentbook · writes
reschedule_appointmentbook · writes
cancel_appointmentbook · writes
mark_no_showbook · writes

Clients

find_clientread
list_clientsread
get_client_historyread
update_clientmanage · writes
get_birthdaysread
get_reviewsread
request_card_on_filemanage · writes

Money (read only)

get_salesread
get_payoutsread

Inbox and waitlist

get_needs_youread
get_conversationread
reply_to_clientbook · writes
mark_handledbook · writes
get_waitlistread
offer_openings_to_waitlistbook · writes
remove_from_waitlistbook · writes

Marketing

list_discountsread
create_discountmanage · writes
set_discount_activemanage · writes
preview_audienceread
list_audiencesread
create_audiencemanage · writes
list_automationsread
create_automationmanage · writes
set_automation_enabledmanage · writes
draft_broadcastread

Page designer

get_design_guideread
get_page_designread
render_pageread
review_page_designread
get_page_sectionread
update_page_designdesign · writes
edit_page_sectionsdesign · writes
apply_lookdesign · writes
publish_page_draftdesign · writes
discard_page_draftdesign · writes
revert_page_designdesign · writes

Owners add the connector from Settings, then Connect. New tools under the four existing scopes reach existing connections automatically; only a brand-new scope needs a reconnect. The connector never charges or moves money, and never sends marketing on its own.

Errors

Standard HTTP status codes. The response body is always JSON with an error field on failures.

400

Bad Request: malformed body, invalid date, missing required field

401

Unauthorized: missing, invalid, or revoked key

403

Forbidden: key lacks the required scope

404

Not Found: resource doesn't exist or belongs to another business

500

Internal: something went wrong on our side. Safe to retry.

WebhooksComing soon

Subscribe to appointment.created, appointment.updated, and customer.created events. HMAC-SHA256 signed (Stripe-style). Email developers@besmi.com if you need this on a timeline.

Help

Question, bug report, or integration request? Email developers@besmi.com. Common questions live in the FAQ; this page is the source of truth for endpoint shapes.