---
title: "Create an event"
method: POST
path: "/events"
tags: ["Events"]
---

# Create an event

`POST /events`

## Request

- Base URL: `https://joinways.app/api/v1`
- URL: `https://joinways.app/api/v1/events`
- Auth: one of:
  - HTTP bearer
  - OAuth 2 (authorization code, authorization URL `https://joinways.app/oauth/authorize`, token URL `https://joinways.app/api/oauth/token`, scopes: `mcp.write`)

Creates an event with the app's rules: venue availability (`block` mode refuses a closure or hours outside opening, `409`), locked venue refused, rooms of the venue only, contact and company found or created, a booking platform name given as `company_name` becomes the event `source` instead of a client company, calendars synchronised. The response is the stored event; `warnings` lists what could not be done (`space_ids_not_booked`). **Role permission:** `events.manage`.

## Headers

- `Idempotency-Key` string

## Request body

- EventCreate: Payload to create an event.
  - `title` string, required
  - `start_date` string, date-time, required: Start (ISO 8601; a `YYYY-MM-DD` day is accepted).
  - `end_date` string, date-time: End, after the start. Defaults to `start_date`.
  - `venue_id` string, uuid: The venue to book (internally `location_id`); a venue, not a space (pass spaces in `space_ids`). Defaults to the workspace's only active venue; with several, it is required (`400 venue_required`).
  - `status` 'option' | 'confirmed' | 'lost' | 'cancelled': `option` = pending / tentative, `confirmed` = booked, `lost` = did not convert, `cancelled` = cancelled after booking.
  - `type` string: Event type, e.g. dinner.
  - `pax` integer
  - `budget` number
  - `company_id` string, uuid: An existing company of the workspace.
  - `company_name` string: Company found by name or created. A booking platform name (Aleou, Kactus…) is not a client: it becomes the event `source`.
  - `contact_person_id` string, uuid: An existing contact of the workspace, instead of the `contact_*` fields.
  - `contact_first_name` string
  - `contact_last_name` string
  - `contact_email` string, email: Contact found by email or created.
  - `contact_phone` string
  - `assigned_to_account_id` string, uuid: A member of the workspace (`400 invalid_assignee` otherwise). Defaults to the token owner.
  - `timezone` string: IANA time zone. Defaults to the venue's.
  - `space_ids` string[]: Spaces (rooms) of the venue the event books; omitted or empty = the whole venue. Ignored for a venue only rented whole (`booked_whole_only`).
  - `source` string: Origin of the request, e.g. `platform:Kactus`.
  - `flexible` boolean
  - `selected_dates` string[]: Non-contiguous days (`YYYY-MM-DD`). With 2 or more distinct days, `start_date` / `end_date` are the time envelope and these days are the event's days.
  - `day_hours` DayHoursEntry[], nullable: One entry for every day of the event, each day exactly once (`400 invalid_day_hours` otherwise); `null` = the same hours every day. An end at or before the start finishes the next morning, at 06:00 at the latest; the last day may not end between 00:00 and 06:00 without starting the evening before. `null` keeps `start_date` / `end_date`: on consecutive days, an end at or before the start then reads as one continuous stay, nights included. When an update moves `start_date` / `end_date` without `day_hours`, the first day's start and the last day's end follow them and the other days keep their hours; if a day no longer fits, the event goes back to the same hours every day (`day_hours: null` in the response).
    - `date` string, date, required
    - `start` string, required
    - `end` string, required

## Response `201`

The created event.

- object: The stored event, as returned by `POST /events`, `PATCH /events/{id}` and the conversion of a lead. Every column of the event is returned except the internal search fields, the embedding and the public tokens; the main ones are listed. Amounts are `null` without `reports.view` or `quotes.manage`.
  - `id` string, uuid
  - `account_id` string, uuid
  - `event_number` integer, nullable
  - `internal_reference` string, nullable
  - `title` string
  - `description` string, nullable
  - `type` string, nullable
  - `category` string, nullable
  - `status` 'option' | 'confirmed' | 'lost' | 'cancelled': `option` = pending / tentative, `confirmed` = booked, `lost` = did not convert, `cancelled` = cancelled after booking.
  - `status_changed_at` string, date-time, nullable
  - `stage_id` string, uuid, nullable
  - `stage_changed_at` string, date-time, nullable
  - `pax` integer, nullable
  - `start_date` string, date-time
  - `end_date` string, date-time, nullable
  - `selected_dates` string[], nullable
  - `day_hours` DayHoursEntry[], nullable: Per-day hours of a multi-day event, by date: one entry per day when the days have different hours, `null` when every day has the same hours (the time of `start_date` to the time of `end_date`) or for a one-day event.
    - `date` string, date, required
    - `start` string, required
    - `end` string, required
  - `timezone` string, nullable
  - `is_virtual` boolean, nullable
  - `flexible` boolean, nullable
  - `language` string, nullable
  - `venue_id` string, uuid, nullable: The venue (internally `location_id`).
  - `location` string, nullable: Free-text place (legacy), not the venue.
  - `address` string, nullable
  - `city` string, nullable
  - `postal_code` string, nullable
  - `country` string, nullable
  - `source` string, nullable
  - `source_lead_id` string, uuid, nullable: Primary request (lead) of the event.
  - `budget` number, nullable
  - `expected_revenue` number, nullable
  - `currency` string, nullable
  - `confirmed_amount` number, nullable
  - `confirmed_amount_excl_tax` number, nullable
  - `cancellation_fee_excl_tax` number, nullable
  - `confirmed_at` string, date-time, nullable
  - `loss_reason` string, nullable
  - `loss_reason_detail` string, nullable
  - `cancellation_reason` string, nullable
  - `cancellation_reason_detail` string, nullable
  - `tags` string[], nullable
  - `notes` string, nullable
  - `company_id` string, uuid, nullable
  - `contact_person_id` string, uuid, nullable
  - `assigned_to_account_id` string, uuid, nullable
  - `created_by` string, uuid, nullable
  - `billing_company_id` string, uuid, nullable: Third-party company the quotes are billed to (instead of the client).
  - `quote_to_platform` boolean, nullable: The quotes are billed to the booking platform of `source`.
  - `event_group_id` string, uuid, nullable
  - `external_link` string, nullable
  - `setup_type` string, nullable
  - `setup_notes` string, nullable
  - `catering_notes` string, nullable
  - `dietary_notes` string, nullable: Diets and allergies (« Contraintes alimentaires »), for the whole event.
  - `technical_notes` string, nullable
  - `service_notes` string, nullable
  - `beo_context` string, nullable
  - `beo_remarks` string, nullable
  - `schedule` object[], nullable: BEO program lines (see `BeoUpdate.schedule`).
  - `beo_public_url` string, uri, nullable: Shareable, no-login URL of the public BEO page.
  - `archived_at` string, date-time, nullable
  - `archived_by` string, uuid, nullable
  - `created_at` string, date-time, nullable
  - `updated_at` string, date-time, nullable
  - `warnings` string[]: Only present when something could not be done: `space_ids_not_booked`, `day_hours_not_saved` (the event is created with the same hours every day).

## Other responses

- `400`: Validation failed (`invalid_request`). For a body that fails the schema, `details` holds `formErrors` and `fieldErrors`.
- `401`: Missing, invalid, expired or revoked bearer token (`unauthorized`).
- `403`: `forbidden`: the token lacks the scope, its owner's role lacks the permission named by the operation, or the workspace has neither the Pro plan nor a trial (`details.reason` `plan_not_eligible`).
- `409`: The venue (or a booked space) is in `block` availability mode and the event's days or hours fall on a closure or outside its opening hours. `error.code` is `conflict`; `error.details` carries `reason: venue_unavailable`, `violation` (`VENUE_CLOSED` or `OUTSIDE_HOURS`) and the `closures` / `outside_hours` hit (see GET /venues/{venueId}/conflicts). In `warn` mode the event is saved.
- `429`: Too many requests (`rate_limited`). `details.reason`: `per-ip`, `per-account-minute`, `per-account-day`, or `email_rate_limited` (email budget). Always carries `Retry-After`: wait that many seconds.

---

[API](https://skmtc.dev/joinways/apis/joinways-public-api.md) · [All operations](https://skmtc.dev/joinways/apis/joinways-public-api/llms.txt) · [OpenAPI document](https://skmtc.dev/joinways/apis/joinways-public-api/revisions/39e84309953e?raw)
