Create an Event with the Wix Events API
Goal
Create an event on a Wix site — one-off or recurring, RSVP or ticketed, with ticket tiers. To publish, cancel, clone, update or count events that already exist, see Manage Wix Events.
First: pick the right "Events" API
Several unrelated Wix APIs are called "Events". Routing to the wrong one is the most expensive mistake here, because the wrong API answers plausibly for several calls before failing.
| The user means | API | Base path |
|---|---|---|
| A public event guests attend — gala, concert, workshop, meetup | Wix Events V3 — this recipe | /events/v3/events |
| A bookable session on a staff or service calendar | Calendar Events V3 | /calendar/v3/events |
| An entry on the marketing plan calendar | Marketing Calendar Event V1 | /promote/marketing-plan-service/v1/events |
| An automation trigger | Triggered Events | /automations/v1/events/report |
A recurring class or course that guests book is Bookings. A recurring event series guests attend is Wix Events.
Prerequisite — the Wix Events app
Every /events/v3 call against a site without it returns 428 WIX_EVENTS_APP_NOT_INSTALLED.
Install with appDefId 140603ad-af8d-84a5-2c80-a0f60cb47351 via
POST /apps-installer-service/v1/app-instance/install, body
{"tenant":{"tenantType":"SITE","id":"<SITE_ID>"},"appInstance":{"appDefId":"<APP_DEF_ID>"}} —
appDefId nests under appInstance. See Install Wix Apps.
Create the event
curl -X POST 'https://www.wixapis.com/events/v3/events' \
-H 'Content-Type: application/json' \
-H 'Authorization: <AUTH>' \
--data-binary '{
"event": {
"title": "Summer Gala",
"location": { "name": "Grand Hall", "type": "VENUE" },
"dateAndTimeSettings": {
"startDate": "2026-09-15T19:00:00.000Z",
"endDate": "2026-09-15T22:00:00.000Z",
"timeZoneId": "America/New_York"
},
"registration": { "initialType": "RSVP" }
}
}'title, location, dateAndTimeSettings and registration.initialType are required. The event
is created published (status: "UPCOMING").
What comes back — the created event under event; the id you need for every follow-up call is
event.id:
{
"event": {
"id": "<EVENT_ID>",
"title": "Summer Gala",
"slug": "summer-gala",
"status": "UPCOMING",
"location": { "name": "Grand Hall", "type": "VENUE", "locationTbd": false },
"dateAndTimeSettings": {
"startDate": "2026-09-15T19:00:00Z", "endDate": "2026-09-15T22:00:00Z",
"timeZoneId": "America/New_York", "recurrenceStatus": "ONE_TIME"
},
"createdDate": "...", "updatedDate": "..."
}
}Update, cancel, publish and clone return the same { "event": { ... } }; delete returns
{ "eventId": "..." }. Some fields are returned only when the request's fields array asks for
them: "DETAILS" adds shortDescription, "TEXTS" adds description, "REGISTRATION" adds
registration (including rsvp.limit), "URLS" adds eventPageUrl. Without fields, read the
result from the top-level fields above and do not treat a missing registration as an error.
Do not add
"draft": trueunless the user asked for a draft. Draft events require theWIX_EVENTS.READ_DRAFT_EVENTSpermission, and without it every follow-up call fails403— adding ticket definitions, querying the event, fetching it by slug, even publishing it. The event is created, but nothing can be done with it. Create it published and skip the publish step entirely.
Date and time
startDate and endDate are ISO-8601 strings, not {seconds, nanos} objects. The spec types
them as string / format: date-time but also points at google.protobuf.Timestamp, whose
fields are seconds and nanos. That is the internal form and must not be sent. This applies to
every date-time field across the Wix APIs.
| Rule | If broken |
|---|---|
| ISO-8601 strings | 400 Expected a string. |
endDate is required |
400 endDate.isDefined must be true, event cannot have negative duration |
timeZoneId is required, in TZ database form |
400 getTimeZoneId is not supported |
The
timeZoneIderror reads as though the field is unsupported. It is not — it means the field is missing. Add it; do not remove it.
Midnight is 00:00 of the next day. 2026-08-18T24:00:00.000Z is not a valid ISO time.
Date to be announced: send dateAndTimeSettings as
{ "dateAndTimeTbd": true, "dateAndTimeTbdMessage": "Date coming soon" } and nothing else. The
message is mandatory — omitting it fails 400 getScheduleTbdMessage must not be a blank.
Location
location.type is VENUE or ONLINE. There is no TBD type — a to-be-announced location is a
VENUE with locationTbd: true.
| Body | Result |
|---|---|
{ "name": "Grand Hall", "type": "VENUE" } |
Accepted |
{ "name": "To be announced", "locationTbd": true } |
Accepted — type defaults to VENUE |
{ "locationTbd": true } |
400 Location address must not be a blank |
{ "name": "TBA", "type": "TBD" } |
400 type enum must be in [VENUE(0), ONLINE(1)] |
For a street address add address:
{ "country": "US", "subdivision": "US-NY", "city": "New York", "postalCode": "10001", "streetAddress": { "number": "429", "name": "11th Ave" } } — subdivision is the ISO-3166-2
code; the API fills in formattedAddress and geocode.
Registration type and capacity
initialType must be nested under registration; at the root of event it is treated as
omitted. Accepted values are RSVP, TICKETING, EXTERNAL and NONE.
The API's error text advertises
[RSVP,TICKETS,RSVP_AND_TICKETS]. Those do not work —TICKETSis rejected. UseTICKETING.
initialType is immutable: an RSVP event can never become TICKETING or vice versa. Choose
correctly at creation — "register by RSVP, not tickets" means RSVP.
Guest limit (RSVP events). The cap is registration.rsvp.limit:
"registration": { "initialType": "RSVP", "rsvp": { "limit": 30 } }. A limit placed directly
on registration is silently ignored — no cap, no error. Add waitlistEnabled: true for a
waitlist. Ticketed events cap per tier instead, via initialLimit below.
Description — two different fields
| Field | Type | Use for |
|---|---|---|
shortDescription |
plain string, max 500 | One line under the event title |
description |
Ricos rich content object | The formatted body on the event page |
A plain sentence belongs in shortDescription. A string sent to description fails
400 Expected an object — it takes a Ricos { "nodes": [...] } tree, as built in
Rich Content. shortDescription is returned only
with "fields": ["DETAILS"], description only with ["TEXTS"]; both are stored regardless.
Ticket tiers
For events created with "initialType": "TICKETING". One call per tier — there is no
bulk-create, and up to 100 tiers per event.
curl -X POST 'https://www.wixapis.com/events/v3/ticket-definitions' \
-H 'Content-Type: application/json' -H 'Authorization: <AUTH>' \
--data-binary '{
"ticketDefinition": {
"eventId": "<EVENT_ID>",
"name": "General Admission",
"pricingMethod": { "fixedPrice": { "value": "25.00", "currency": "USD" } },
"feeType": "FEE_ADDED_AT_CHECKOUT",
"initialLimit": 100
}
}'| To create | pricingMethod |
|---|---|
| Fixed price | { "fixedPrice": { "value": "25.00", "currency": "USD" } } |
| Free | { "fixedPrice": { "value": "0", "currency": "USD" } } |
| Donation, with a minimum | { "guestPrice": { "value": "5.00", "currency": "USD" } } |
The response is the definition under ticketDefinition:
{
"ticketDefinition": {
"id": "<TICKET_DEFINITION_ID>",
"eventId": "<EVENT_ID>",
"revision": "1",
"name": "General Admission",
"limited": true, "initialLimit": 100, "actualLimit": 100,
"pricingMethod": { "fixedPrice": { "value": "25.00", "currency": "USD" } },
"feeType": "FEE_ADDED_AT_CHECKOUT",
"saleStatus": "SALE_STARTED"
}
}Keep revision — PATCH /events/v3/ticket-definitions/{id} requires the current value, and it
increments on every update.
feeTypeis required. A free ticket (fixedPriceof"0") takesNO_FEE. A paid ticket takesFEE_ADDED_AT_CHECKOUT(the guest pays the service fee) unless the user asked to absorb it, thenFEE_INCLUDED.NO_FEEon a paid ticket is accepted only on sites that do not collect ticket fees. If a paid ticket fails with400 INVALID_FEE_TYPE, the site is one that does not collect ticket fees: retry that ticket once withNO_FEE.valueis a string."value": 10fails400 Unexpected value for field value.- No writable
freeflag —pricingMethod.freeis read-only; a free ticket is afixedPriceof"0". - Quantity is
initialLimit, notinitialQuantity; omit for unlimited.limitPerCheckoutis read-only.namemax 30 chars.
Recurring events
Wix Events has no recurrence rule — no RRULE, no "weekly" pattern. Calculate every
occurrence yourself and list them all (up to 1000). Each occurrence needs its own
timeZoneId; omitting it fails with a cryptic ZoneOffset error:
"dateAndTimeSettings": {
"startDate": "2026-10-20T18:00:00.000Z",
"endDate": "2026-10-20T19:00:00.000Z",
"timeZoneId": "America/New_York",
"recurringEvents": {
"individualEventDates": [
{ "startDate": "2026-10-20T18:00:00.000Z", "endDate": "2026-10-20T19:00:00.000Z", "timeZoneId": "America/New_York" },
{ "startDate": "2026-10-27T18:00:00.000Z", "endDate": "2026-10-27T19:00:00.000Z", "timeZoneId": "America/New_York" }
]
}
}Pick a sensible horizon for "weekly" and say how many occurrences you created. The event returns
recurrenceStatus: "RECURRING". Each occurrence is an independent event with its own ID; all
share a generated recurringEvents.categoryId, which is how you query the series.
Gotchas & troubleshooting
- An invalid enum value reports as a missing one —
<field> value is requiredrather than "invalid value". If a field you did send is reported as required, suspect the value. - Error text names internal accessors:
getTimeZoneId,getScheduleTbdMessage,endDate.isDefinedeach map to the plain field of the same name.
Related APIs
- Wix Events V3: REST
- Ticket Definitions V3: REST
- Manage Wix Events — publish, cancel, delete, clone, update, count