punctual:

REST API

The REST API mounted at /api/v1 — endpoints, authentication, scopes and the error format.

A REST API mounted at /api/v1. Every request needs an API key with the right scope; every response body — success or error — is JSON, except a successful DELETE, which returns 204 No Content with no body at all.

Authentication

Send your key as a bearer token:

curl https://book.cccrafts.ai/api/v1/event-types \
  -H "Authorization: Bearer pk_..."

A key carries one or more scopes — read, write, or * for both. GET and HEAD requests need read; everything else needs write. A key without the required scope gets 403, never a silent downgrade.

Errors

Errors are RFC 7807 problem documents, application/problem+json:

{
  "type": "urn:punctual:problem:invalid-request",
  "title": "Invalid request",
  "status": 400,
  "detail": "The request body did not validate.",
  "errors": [
    { "field": "durationMinutes", "message": "Number must be a multiple of 5" }
  ]
}

The type is a URN, not a dereferenceable URL — a self-hoster's error payloads never point at punctual's own domain.

Endpoints

MethodPathScopeDescription
GET/api/v1/event-typesreadList your event types
POST/api/v1/event-typeswriteCreate an event type
GET/api/v1/event-types/:idreadGet one event type
PATCH/api/v1/event-types/:idwriteUpdate fields on an event type
DELETE/api/v1/event-types/:idwriteDelete an event type
GET/api/v1/availabilityreadGet the key holder's weekly schedule and date overrides
PUT/api/v1/availabilitywriteReplace the weekly schedule and overrides
GET/api/v1/slotsreadList bookable start times for an event type in a time range
GET/api/v1/bookingsreadList confirmed bookings in a time range
GET/api/v1/bookings/:idreadGet one booking, any status — cancelled and rescheduled included
POST/api/v1/bookingswriteCreate a booking directly, bypassing the public booking page
POST/api/v1/bookings/:id/cancelwriteCancel a confirmed booking
POST/api/v1/bookings/:id/reschedulewriteMove a confirmed booking to a new time
GET/api/v1/webhooksreadList registered webhooks
POST/api/v1/webhookswriteRegister a webhook; the response includes its signing secret once
DELETE/api/v1/webhooks/:idwriteRemove a webhook

Example: create a booking

curl https://book.cccrafts.ai/api/v1/bookings \
  -X POST \
  -H "Authorization: Bearer pk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "eventTypeId": "evt_abc123",
    "start": "2026-08-20T14:30:00Z",
    "guestName": "Ada Lovelace",
    "guestEmail": "ada@example.com"
  }'

start must be ISO-8601 with an explicit offset (or epoch milliseconds) — an offsetless string is rejected rather than silently read as UTC.

Ranges

Any endpoint that takes from/to (/slots, /bookings) caps the span at 62 days per request.

Full route definitions: src/http/api/rest.ts.