Topolo Docs Topolo Platform
Open workspace

Create booking — Calendar

Create a booking after discovering its event type and confirming the invitee/time.

Create a booking after discovering its event type and confirming the invitee/time.

Action: app_topolo_calendar.bookings.create · POST /api/admin/bookings

Declared permission: app_topolo_calendar.bookings:write. For authorized Calendar users; permissions and resource access still apply.

Before you start

  • Use the current credential to confirm the production organization and inspect this exact action before preparing a call.
  • Declared permission: app_topolo_calendar.bookings:write. Permission discovery is not a grant.
  • Discover the authorized Calendar workspace and pass it as resourceType=workspace/resourceId separately from this action input. Calendar records and IDs are scoped to the selected host/workspace.
  • Validate and plan the exact payload, obtain effect-specific authorization, then execute unchanged only in a separately authorized operational task. Do not run these synthetic inputs.

Inputs and field meanings

FieldPresenceDeclared constraintMeaning
startAtrequiredstringOffset-aware ISO8601 instant. The source stores normalized UTC; retain the intended host/invitee timezone interpretation.
endAtoptionalstringOffset-aware end instant after start. Create derives end from duration/type; update/reschedule can use this explicit end.
durationMinutesoptionalinteger; minimum=5; maximum=480Integer 5–480. Required for a custom creation without an event type; otherwise the type/current duration supplies a default.
inviteeNamerequiredstringIntended invitee display name. Creation requires nonempty text; confirm the person before any communication.
inviteeEmailoptionalnullable/unionIntended recipient. Creation can fall back to the host email when omitted/blank; updates preserve the old address for blank/null values. Do not rely on fallback for real invitations.
inviteeTimezoneoptionalnullable/unionInvitee timezone label. Creation defaults to the host timezone; update null/omission preserves the previous value.
inviteeNotesoptionalnullable/unionInvitee notes. Creation defaults to null; update null/omission preserves prior notes and an explicit empty string can clear them.
eventTypeIdoptionalstringType ID discovered with event_types.list/get. Creation uses either this or eventTypeSlug, never both.
eventTypeSlugoptionalstringSlug of a type belonging to this host; use instead of eventTypeId when intentionally chosen.
recurrenceoptionalnullable/unionOptional recurrence rule; omit/null for a single booking. Multiple occurrences may be created and must be reconciled individually on uncertain failure.
recurrence.frequencyrequiredstringSource recurrence frequencies are daily, weekly, monthly or yearly; the declared string schema alone is broader.
recurrence.intervalrequiredinteger; minimum=1; maximum=9007199254740991Positive integer repeat interval in units of frequency.
recurrence.countoptionalnullable/unionOptional positive occurrence count; choose deliberately to bound generated bookings.
recurrence.untiloptionalnullable/unionOptional offset-aware recurrence cutoff instant. The schema alone does not establish the runtime recurrence expansion limits.

Worked request and response

Source-reviewed illustration only. Nothing below was executed; all identifiers, counts, states, credentials and URLs are synthetic. Replace identifiers through discovery before any separately authorized call.

Resource context: workspace; it is not an extra action input field.

Illustrative action input:

{
  "eventTypeId": "evt_example",
  "startAt": "2026-11-23T10:00:00+04:00",
  "inviteeName": "Example Invitee",
  "inviteeEmail": "invitee@example.com",
  "inviteeTimezone": "Asia/Dubai"
}

Illustrative HTTP 201 source response:

{
  "booking": {
    "id": "bkg_example",
    "hostId": "hst_example",
    "eventTypeId": "evt_example",
    "calendarUid": "bkg_example@calendar.topolo.app",
    "sequence": 0,
    "startAt": "2026-11-23T06:00:00.000Z",
    "endAt": "2026-11-23T06:30:00.000Z",
    "status": "confirmed",
    "inviteeName": "Example Invitee",
    "inviteeEmail": "invitee@example.com",
    "inviteeTimezone": "Asia/Dubai",
    "inviteeNotes": null,
    "meetingProvider": null,
    "meetingProviderRef": null,
    "createdAt": "2026-11-01T00:00:00.000Z",
    "updatedAt": "2026-11-01T00:00:00.000Z",
    "cancelledAt": null,
    "cancellationReason": null,
    "recurrenceSeriesId": null,
    "recurrenceInstanceStart": null
  },
  "bookings": [
    {
      "id": "bkg_example",
      "hostId": "hst_example",
      "eventTypeId": "evt_example",
      "calendarUid": "bkg_example@calendar.topolo.app",
      "sequence": 0,
      "startAt": "2026-11-23T06:00:00.000Z",
      "endAt": "2026-11-23T06:30:00.000Z",
      "status": "confirmed",
      "inviteeName": "Example Invitee",
      "inviteeEmail": "invitee@example.com",
      "inviteeTimezone": "Asia/Dubai",
      "inviteeNotes": null,
      "meetingProvider": null,
      "meetingProviderRef": null,
      "createdAt": "2026-11-01T00:00:00.000Z",
      "updatedAt": "2026-11-01T00:00:00.000Z",
      "cancelledAt": null,
      "cancellationReason": null,
      "recurrenceSeriesId": null,
      "recurrenceInstanceStart": null
    }
  ],
  "recurrenceSeries": null
}

For a nonrecurring, nonapproval phone event, booking and bookings[0] are the created record and recurrenceSeries is null. The source normalizes the time to UTC. Persistence may schedule invitations/reminders but is not delivery proof.

The illustrative response passes the declared structural schema. This establishes only schema compatibility, not deployed shape, values, authorization or business outcome.

The create response starts at sequence 0 and returns the plain booking record; attendee rows are stored separately and are not hydrated in this response. Read bookings.get for hydrated details.

Effects and verification

  • Creates one booking or a recurrence series and its occurrences, subject to conflict checks.
  • A nonapproval booking can request invitation/reminder delivery and provider meeting work; obtain effect-specific authorization.
  1. Reconcile the returned ID with bookings.get/list, verify invitee/time/status, and inspect notification/provider receipts separately.

Refusals and recovery

Selected source refusals are listed below; runtime middleware can return additional structured errors.

HTTPErrorSupported response
403forbiddenReconcile the declared permission, organization and verified resource grant with the legitimate access owner; do not switch context to bypass a refusal.
404no_hostRead host.get; if it returns host:null, create the intended workspace host through host.update before retrying.
409booking_conflictChoose another time after checking bookings and busy time. Reconcile a previous uncertain create before trying again.
400duration_requiredFor a custom booking without either event type reference, provide durationMinutes.
404event_type_not_foundDiscover a type belonging to this host.

Recovery: Cancel only the verified newly created booking after reconciling its ID. Cancellation can send another message and cannot recall an earlier invitation or erase the history.

After an uncertain mutation, reconcile the named records and provider/delivery evidence before repeating it.

  • app_topolo_calendar.event_types.get: Read one known event type, including an inactive one.
  • app_topolo_calendar.bookings.list: Find bookings within an explicit time window.
  • app_topolo_calendar.bookings.get: Read a booking and its related event type and delivery receipts.
  • app_topolo_calendar.bookings.cancel: Cancel one booking and release its slot.

Declared schemas and provenance

The hidden action guide carries these exact current declared schemas. They are not rewritten to make the illustrations pass.

Input schema:

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "startAt": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    },
    "endAt": {
      "type": "string",
      "format": "date-time",
      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
    },
    "durationMinutes": {
      "type": "integer",
      "minimum": 5,
      "maximum": 480
    },
    "inviteeName": {
      "type": "string",
      "minLength": 1
    },
    "inviteeEmail": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "inviteeTimezone": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "inviteeNotes": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ]
    },
    "eventTypeId": {
      "type": "string",
      "minLength": 1
    },
    "eventTypeSlug": {
      "type": "string",
      "minLength": 1
    },
    "recurrence": {
      "anyOf": [
        {
          "type": "object",
          "properties": {
            "frequency": {
              "type": "string"
            },
            "interval": {
              "type": "integer",
              "minimum": 1,
              "maximum": 9007199254740991
            },
            "count": {
              "anyOf": [
                {
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 9007199254740991
                },
                {
                  "type": "null"
                }
              ]
            },
            "until": {
              "anyOf": [
                {
                  "type": "string",
                  "format": "date-time",
                  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z|([+-](?:[01]\\d|2[0-3]):[0-5]\\d)))$"
                },
                {
                  "type": "null"
                }
              ]
            }
          },
          "required": [
            "frequency",
            "interval"
          ],
          "additionalProperties": false
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "required": [
    "startAt",
    "inviteeName"
  ],
  "additionalProperties": false
}

Output schema:

{
  "type": "object",
  "title": "Create booking response",
  "description": "Operation result returned by bookings.create.",
  "properties": {
    "success": {
      "type": "boolean"
    },
    "id": {
      "type": "string"
    },
    "data": {
      "oneOf": [
        {
          "type": "object",
          "title": "booking record",
          "properties": {
            "id": {
              "type": "string",
              "description": "Stable identifier when the booking exposes one."
            }
          },
          "additionalProperties": true
        },
        {
          "type": "object",
          "additionalProperties": true
        }
      ]
    },
    "result": {
      "type": "object",
      "additionalProperties": true
    },
    "message": {
      "type": "string"
    }
  },
  "additionalProperties": true,
  "x-topolo-contract-source": "derived-action-metadata",
  "x-topolo-contract-precision": "structural"
}

Source: TopoloCalendar@b378ceb73800ee1d1e6c4ff6f0b4179918cf632c, apps/api/src/routes/admin-bookings.ts. Current catalog metadata and source were compared without executing the application.

  • Source-reviewed synthetic input/response; not captured or executed.
  • Source revision is not proof of current deployment, caller access, provider connection or notification delivery.
  • The illustrative response passes the declared structural schema. This establishes only schema compatibility, not deployed shape, values, authorization or business outcome.
  • Provide one of eventTypeId/eventTypeSlug, not both; or provide durationMinutes for a custom booking.
  • Although endAt appears in the declared input schema, the reviewed create handler derives end from durationMinutes/event type and does not consume endAt. Do not rely on endAt to set creation duration.
  • A recurrence can create several records and the source does not prove a cross-provider transaction; reconcile a partial/uncertain failure before replaying.