Manage Event Tickets

Download skillThe skill is a reference md and part of wix-manage skill. You can use the following command to add the full wix-manage skill to your project:
Copy

Goal

Manage the ticket types ("ticket definitions") of a ticketed event. A ticket definition is one type of ticket — "General Admission", "VIP" — with its price, quantity and sale period. Individual tickets that guests bought are orders and tickets, in Manage Event Guests.

Not here:

  • Creating a new event together with its first tickets: Create an Event.
  • Closing or pausing registration for the whole event: an event setting, PATCH https://www.wixapis.com/events/v3/events/<EVENT_ID> with { "event": { "registration": { "registrationPaused": true } } } (Manage Wix Events). Do not close an event's registration by ending every ticket's sale period.
  • Tickets per order, tax and reservation hold: also event settings, under event.registration.tickets (ticketLimitPerOrder, taxSettings, reservationDurationInMinutes) with the same PATCH https://www.wixapis.com/events/v3/events/<EVENT_ID>.

Prerequisite — the Wix Events app

Every call here returns 428 WIX_EVENTS_APP_NOT_INSTALLED on a site without the Wix Events app. Install it with appDefId 140603ad-af8d-84a5-2c80-a0f60cb47351: POST https://www.wixapis.com/apps-installer-service/v1/app-instance/install with {"tenant":{"tenantType":"SITE","id":"<SITE_ID>"},"appInstance":{"appDefId":"140603ad-af8d-84a5-2c80-a0f60cb47351"}} — appDefId nests under appInstance. See Install Wix Apps.

Before you start

Tickets exist only on events created with registration.initialType: "TICKETING". On an RSVP event every call here fails 428 INVALID_EVENT_TYPE, and the event can't be converted. Find the event and check its type first — this exact body:

Copy
  • paging is a sibling of filter inside query; nested inside filter it fails 400.
  • Send it as is: no extra filter fields and no sort — a nested registration filter or a sort inside filter fails 400. Read the type and dates from the result instead.
  • Don't send includeDrafts — true needs a draft-events permission and fails 403.
  • The id is events[].id, the type events[].registration.initialType. If several events match, pick by date or ask (Manage Wix Events).

If it is RSVP, create nothing in this turn: tell the user an RSVP event can't be converted, that a ticketed copy would be a new event and its RSVPs would not carry over, and ask whether to create it — and with which tickets and prices. See Manage Wix Events.

Reference pages for these calls — use them rather than searching: Query Ticket Definitions, Create Ticket Definition, Update Ticket Definition; to find the event, Query Events.

The examples use https://www.wixapis.com/events/v3/ticket-definitions. The reference's https://www.wixapis.com/events-ticket-definitions/v3/ticket-definitions is the same API — either base works.

List an event's tickets

Copy

Always filter by eventId — without it the query returns every event's tickets. The response is ticketDefinitions[], with paging under metadata (not pagingMetadata). Each definition carries:

  • id and revision — both needed to update it;
  • name, pricingMethod, feeType, hidden, initialLimit, salePeriod;
  • saleStatus — SALE_SCHEDULED (not on sale yet), SALE_STARTED or SALE_ENDED;
  • with SALES_DETAILS: salesDetails.soldCount, unsoldCount (null when unlimited), reservedCount, soldOut.

Other filters: name, hidden, saleStatus, pricingMethod.free, feeType ($eq, $in, …). To find a ticket by the name the user gave, filter eventId and name together.

Add a ticket type

POST https://www.wixapis.com/events/v3/ticket-definitions, one call per ticket type, up to 100 per event. Create them one after another, not in parallel; on 429, wait and retry that call before moving to the next, and count the successes before reporting. Then list the event's tickets once (List an event's tickets) and report what is stored.

Copy

salePeriod dates are instants: "stop selling on November 15" for a New York event is the start of that day in America/New_York — 2026-11-15T05:00:00Z — not T00:00:00Z, which is 7pm the evening before. Convert from the event's dateAndTimeSettings.timeZoneId, and say the local date and time you set. Before sending, check the instant: converted back to the event's time zone it must read 00

on the requested date. If it doesn't, fix it before the create — not with a follow-up update.

A sale date the user gives without a year is the next such date from today — on 20 October 2026, "stop selling on December 1st" is 1 December 2026. A sale period can never end after the event starts: check salePeriod.endDate against the event's dateAndTimeSettings.startDate before sending, and ask if that date falls after the event starts.

Currency: use the one the user names, as given ("20 euros" is EUR) — a ticket in that currency is accepted even when the event has no tickets yet. When the user names none, use the event's existing currency (pricingMethod.fixedPrice.currency of its other tickets, or registration.tickets.currency on the event). To move an event that already has tickets to another currency, see Change the currency. The response is { "ticketDefinition": { "id", "revision": "1", "saleStatus", ... } }.

TicketpricingMethodfeeType
Fixed price{ "fixedPrice": { "value": "25.00", "currency": "USD" } }FEE_ADDED_AT_CHECKOUT
Free{ "fixedPrice": { "value": "0", "currency": "USD" } }NO_FEE
Donation / pay what you want (minimum may be "0"){ "guestPrice": { "value": "5.00", "currency": "USD" } }FEE_ADDED_AT_CHECKOUT
Several prices in one ticket{ "pricingOptions": { "optionDetails": [ { "optionId": "<NEW_GUID>", "name": "Adult", "price": { "value": "30.00", "currency": "USD" } }, { "optionId": "<NEW_GUID>", "name": "Child", "price": { "value": "15.00", "currency": "USD" } } ] } }FEE_ADDED_AT_CHECKOUT
  • Service fee. Free ticket: NO_FEE. Paid ticket: FEE_ADDED_AT_CHECKOUT (the guest pays the fee on top), or FEE_INCLUDED when the user wants to absorb the fee. On 400 INVALID_FEE_TYPE, read the error's description: feeRequired=false means the site collects no ticket fees — retry that ticket once with NO_FEE; feeRequired=true means NO_FEE was sent on a paid ticket — retry once with FEE_ADDED_AT_CHECKOUT.
  • Several prices need an optionId on every option — a GUID you generate. Without it the create fails with a bare 500 Internal Server Error. Keep the ids from the response: an update must send them back.
  • value is a string — "value": 10 fails 400 Unexpected value for field value.
  • Quantity is initialLimit; omit it for unlimited. If the event has a seating map, the seats set the quantity and initialLimit is ignored.
  • name is 1–30 characters, description up to 500, policyText up to 1000; a price option name up to 200 and up to 100 options.
  • Read-only, never send: pricingMethod.free, pricingMethod.pricingType, saleStatus, limited, actualLimit, limitPerCheckout, salesDetails. They are ignored without an error, so a "free" flag or a per-checkout limit you send silently does nothing. The number of tickets one buyer can take per order is the event's registration.tickets.ticketLimitPerOrder.
  • 428 INVALID_SALE_PERIOD — salePeriod.startDate is not before endDate (on create and update).

Change a ticket

PATCH https://www.wixapis.com/events/v3/ticket-definitions/{ticketDefinition.id}. The current revision is required (400 revision must not be empty without it). Read the ticket right before updating and send its id, current revision, and the fields you are changing.

Price changes need "mask": "pricingMethod". The ticket update takes its field mask under the key mask (not fieldmask); send it even though the reference schema omits it. Without it:

  • a fixedPrice change returns 200 and bumps the revision, but the price is not changed;
  • a pricingOptions change fails 400 INVALID_PATCH … field path is illegal.

A narrower path such as pricingMethod.fixedPrice.value doesn't work either. Name, description, policy, initialLimit, hidden and salePeriod apply without a mask. Example — change a price:

Copy
  • Change the price: send the whole pricingMethod for the ticket's pricing kind, with "mask": "pricingMethod". To change one price option of a several-prices ticket, send the full pricingOptions.optionDetails array — every option, each with its existing optionId, and the new price on the one that changes — with the same mask.
  • Rename / describe: name, description, policyText.
  • Quantity: initialLimit — not below the number already sold.
  • Then check the response. The returned ticketDefinition must show the new value as well as a revision one higher — a higher revision alone proves nothing. If the value did not change, do not report success: read the ticket again and tell the user what is stored.

Hide or show a ticket

PATCH as above with "hidden": true to hide a ticket from the event page so it can't be bought, or "hidden": false to show it again. A hidden ticket can still be given to guests from the dashboard; there is no private link or code that unlocks a hidden ticket for chosen buyers — say so if the user asks for one.

Sale period — schedule, end or reopen sales

salePeriod controls when one ticket can be bought:

The user wantssalePeriod
Tickets go on sale later{ "startDate": "<ISO>" }
Stop selling this ticket on a date (early bird){ "endDate": "<ISO>" }
Show the ticket while it's not on saleadd "displayNotOnSale": true
Stop selling this ticket now{ "endDate": "<now, ISO>" }
Reopen an ended ticket{ "endDate": "<a future ISO date>" } — saleStatus goes from SALE_ENDED back to SALE_STARTED

Dates are ISO-8601 strings, rounded down to the minute. After the change, read saleStatus in the response: SALE_STARTED means it is buyable now. If a reopened ticket still reads SALE_ENDED, also check the event: a past event, a cancelled event, or registration.registrationPaused: true stops all sales regardless of the ticket's sale period — reopen registration with PATCH https://www.wixapis.com/events/v3/events/<EVENT_ID> and { "event": { "registration": { "registrationPaused": false } } }.

Ending sales for every ticket is not how to close an event's registration — pause registration on the event instead.

Reorder tickets

POST https://www.wixapis.com/events/v3/ticket-definitions/reorder, moving one ticket before or after another; this sets the order on the event page and in the dashboard:

Copy

Send exactly one of beforeDefinitionId or afterDefinitionId (neither fails 400 INVALID_REORDER_INSTRUCTION). For a full new order, move the tickets one at a time from second to last, each with afterDefinitionId set to the ticket that should precede it. The call returns {}. The list call sorts by creation date by default, not by this order, so don't use it to check the result — report the order you applied and whether every reorder call returned 2xx.

Delete tickets

  • One: DELETE https://www.wixapis.com/events/v3/ticket-definitions/{ticketDefinitionId}.
  • All tickets of an event, or a filtered set: POST https://www.wixapis.com/events/v3/bulk/ticket-definitions/delete with a single-level filter — { "filter": { "eventId": "<EVENT_ID>" } }. Unlike the Events bulk calls, there is no inner filter. Always include eventId, or the call reaches other events' tickets. It returns {}, and a read right afterwards can still list the tickets — wait a few seconds and query again before reporting what was deleted.

Deleting a ticket that has sales is allowed — nothing stops it. Before deleting one (salesDetails.soldCount above 0), tell the user how many were sold and confirm; hiding it is usually what they want. Fetching a deleted ticket afterwards returns 404.

Change the currency

POST https://www.wixapis.com/events/v3/ticket-definitions/currency with { "eventId": "<EVENT_ID>", "currency": "EUR" } changes the currency of all the event's tickets. Amounts are not converted — 10 USD becomes 10 EUR — so update prices afterwards if the user wants converted prices. Before calling, read the event with "fields": ["DASHBOARD"]: when summaries.tickets.currencyLocked is true the call fails 428 CURRENCY_LOCKED — tell the user the currency can no longer be changed. Decide from that flag, not from whether the event has orders.

Count tickets

POST https://www.wixapis.com/events/v3/ticket-definitions/count with { "filter": { "eventId": "<EVENT_ID>" }, "facet": ["saleStatus"] } returns counts per sale status, as facets.saleStatus.counts (for example { "SALE_STARTED": 2 }). For sold and remaining tickets, use the list call with SALES_DETAILS and sum soldCount and unsoldCount.

Not supported

Say so plainly rather than approximating:

  • a private link or code that unlocks a hidden ticket;
  • a per-person or per-email cap across separate orders (only the per-order limit exists);
  • a waitlist for a sold-out ticketed event (waitlists are RSVP-only);
  • moving a sold ticket to another event or occurrence;
  • one ticket admitting several people — create a ticket type per group size, or collect names on the event's registration form (Events Form API).

Last updated: 22 October 2026

Did this help?