swarmtix_discounts_create
Creates a private discount code on an event, taking either an amount or a percentage off
swarmtix_discounts_create
Creates a discount code on an event and returns it. The code is private: it reduces the price only for a buyer who types it at checkout, and it is not advertised on the event page.
When you would reach for it
- A partner or sponsor code for a specific audience.
- An early-bird code with an end date and a redemption limit.
- A speaker or staff code taking a fixed amount off one ticket tier.
Scope
discounts:write
This has a scope of its own rather than sharing events:write, because a discount is the one thing in this catalog that changes what a buyer pays. An organizer who wants an assistant to set an event up should not have to hand it the ability to price that event down as well.
Creating a code takes no money and gives none away on its own — it is redeemed at checkout, by a buyer, against tickets that are on sale. But it is the closest this catalog gets to the money, so it is the place the arguments are checked hardest.
Calling this twice with the same code creates two offers that both answer to it. It is not idempotent, and there is no delete tool — a duplicate is deactivated in the dashboard. Check swarmtix_discounts_list before retrying a call that timed out.
Parameters
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
event | string | Yes | — | The event's id (a GUID) or its URL slug. |
code | string | Yes | — | The code a buyer types at checkout. Letters, digits, dots, dashes and underscores, up to 100 characters. |
endsAt | string | Yes | — | When the offer stops being accepted, in ISO 8601 form, read as UTC when no offset is given. |
amountOff | number | No | — | How much to take off each ticket, in the event's currency. Give this or percentOff, not both. |
percentOff | number | No | — | What percentage to take off each ticket, from 1 to 100. Give this or amountOff, not both. |
startsAt | string | No | now | When the offer starts being accepted, in ISO 8601 form. |
ticketType | string | No | every type | The id of the one ticket type this applies to, from swarmtix_ticket_types_list. |
usesLimit | integer | No | no limit | How many times in total the code may be redeemed. 1 to 1,000,000. |
Exactly one of amountOff and percentOff is required. They are stored in one place with a flag saying which kind the offer is, so a request carrying both would silently keep one and throw the other away — it is refused instead.
endsAt is required and must be after the start. There is no "runs until sales end" option here: the setting that means that reads the furthest-future date across the whole system rather than this event's, so this tool always writes an explicit end date.
The offer is named after the code.
What the code may contain
Letters, digits, dots, dashes and underscores. That is narrower than what the product itself accepts, on purpose: a code that passes this check cannot then be rejected further down with a message this layer is not allowed to repeat, leaving you a generic failure for something you could have fixed.
Example call
{
"name": "swarmtix_discounts_create",
"arguments": {
"event": "spring-summit-2027",
"code": "PARTNER20",
"percentOff": 20,
"endsAt": "2027-09-30T23:59:00Z",
"usesLimit": 50
}
}What comes back
The offer as stored:
{
"id": "a15c9e02-3b7d-4c61-8f95-6d24e0a7b3c8",
"eventId": "3b6f81ce-04a2-4d97-b5e8-7c10924af3d6",
"name": "PARTNER20",
"code": "PARTNER20",
"amountOff": 0,
"percentageOff": 20,
"appliesToAllTicketTypes": true,
"unlimited": false,
"usesLimit": 50,
"startDate": "2027-01-14T11:06:22",
"endDate": "2027-09-30T23:59:00",
"active": true
}| Field | What it is |
|---|---|
id | The new offer's id |
eventId | The event it was created on |
name | The offer's name — the code |
code | The code a buyer types |
amountOff | The fixed reduction. 0 when the offer is a percentage. |
percentageOff | The percentage reduction. 0 when the offer is a fixed amount. |
appliesToAllTicketTypes | Whether it covers every ticket type on the event |
ticketTypeId | The single ticket type it covers, when it is scoped to one |
unlimited | Whether it has no redemption limit |
usesLimit | The limit, when there is one |
startDate, endDate | The window it is accepted between |
active | Whether the offer is live |
What happens when you call it
- The event is resolved through the edit gate.
codeis validated against the character rule above, andendsAtandstartsAtare parsed as UTC when they carry no offset.- Exactly one of
amountOffandpercentOffis required, and the value is rounded to two decimals. ticketType, if given, is checked against this event. Nothing downstream does that: the offer is written with whatever id it is handed, so an id from another organisation's event would be stored against yours and two events would share a discount row neither organiser could account for.- The offer is created as a coded discount — redeemed by typing the code, not advertised on the event page. The public kind is a marketing decision rather than a configuration one, and is not this connection's to take.
- The stored row is read back, checked for belonging to this event, and returned.
Troubleshooting
"amountOff is not valid: neither amountOff nor percentOff was given. Expected one of them, saying how much the code takes off."
One of the two is required.
"amountOff is not valid: both amountOff and percentOff were given. Expected only one of them, because an offer takes off an amount or a percentage but not both."
Send one. Sending both would have quietly kept one and dropped the other.
"amountOff is not valid: it was 0. Expected an amount greater than 0, or percentOff instead."
Zero is not "no discount" here — it is the flag that says the offer is a percentage, and the percentage would be zero too. Use a real amount, or percentOff.
"percentOff is not valid: it was 150. Expected an amount from 0 to 100."
Percentages run from 1 to 100.
"percentOff is not valid: it was 0.5. Expected a percentage from 1 to 100."
Fractions below 1% are not accepted.
"endsAt is not valid: "soon" is not a date and time. Expected a date and time in ISO 8601 form."
Use an ISO 8601 instant — 2027-09-30T23:59:00Z.
"endsAt is not valid: it is not after startsAt. Expected a date and time after the offer starts."
The window has no duration. When startsAt is omitted the offer starts immediately, so endsAt must be in the future.
"code is not valid: it contains " ". Expected a code of letters, digits, dots, dashes or underscores."
The code has a space or another disallowed character. Letters, digits, ., - and _ only.
"ticketType is not valid: there is no ticket type with that id on this event. Expected the id of a ticket type on this event, or nothing to cover all of them."
The ticket type id does not name an active type on that event. List them with swarmtix_ticket_types_list, or omit the argument to cover every type.
"usesLimit is not valid: it was 0. Expected a whole number from 1 to 1000000."
Omit usesLimit for an unlimited code rather than sending zero.
"Event not found, or this connection does not have access to it."
The event does not exist, it belongs to another organisation, or this organizer may read it but not edit it.
"This connection is not authorized for discounts:write. Reconnect and grant it to use this tool."
Reconnect and approve discounts:write. It is a separate permission from events:write on purpose.
The code does not work at checkout
Check it with swarmtix_discounts_check_code — it reports which of the four reasons applies in a sentence.
Two offers now share the same code
Expected after a retry. Deactivate one in the dashboard; the MCP catalog has no delete tool.
Use cases
- Partner codes with a limit and an expiry
- Speaker comps — a fixed amount off one ticket tier
- Early-bird pricing without creating a second ticket type
- Campaign codes created and then verified in two calls