API · Promoter programs
Programs group shared customer discounts and event scope. Each assigned promoter receives an organizer-chosen personal promo code and link. No random codes are generated. One person may belong to several programs. Existing standalone promo codes remain supported.
Use organizer account Bearer tokens and Accept: application/json on the ordinary admin routes. Reads require a read or write token; writes require a write token. Personal co-organizer tokens can manage programs only when all selected events are currently assigned to that membership. Shop-wide programs and programs containing any unassigned event are excluded; their IDs return 404. New co-organizer programs default to shop_wide: false and require event_ids. Attempts to create or change a program to a shop-wide or unauthorized scope return 422. Promoter sessions cannot manage programs. Foreign program IDs return 404; invalid terms return 422 with {"errors":{"field":["message"]}}.
Program endpoints
| Method | URL | Purpose |
|---|---|---|
| GET | /admin/promoter_programs |
List programs in a programs array |
| POST | /admin/promoter_programs |
Create a program and optional personal codes; 201 with Location |
| GET | /admin/promoter_programs/:id |
Program, personal codes and pending invitations |
| PATCH | /admin/promoter_programs/:id |
Update common terms or pause with active: false |
:id is the program’s opaque public ID, not a database number.
{
"promoter_program": {
"name": "Club promoters",
"shop_wide": false,
"event_ids": [23, 24],
"discount_type": "percentage",
"discount_amount": "10.0",
"membership_ids": [42, 43],
"codes": { "42": "LISA-CLUB", "43": "BEN-CLUB" }
}
}
shop_wide defaults to true and includes future events automatically; selected event IDs are cleared in that mode. When false, choose at least one event from this account. Updating event_ids replaces the complete event selection. membership_ids on creation assigns accepted, active promoter memberships. Supply codes as an object keyed by membership ID for every new assignment. Codes must be 3–32 letters, numbers or hyphens and unique within the shop; they are trimmed and uppercased. Missing, invalid or duplicate codes return 422 and roll back the entire batch.
discount_type is percentage (default) or fixed. discount_amount defaults to zero: tracking without a customer discount. Percentage values are 0–100; fixed amounts are nonnegative major currency units. Optional expires_at is ISO 8601; usage_limit caps redemptions per personal code; max_per_customer caps redemptions per customer and personal code. Pass null to clear optional limits or expiry.
Responses expose id, name, shop_wide, event_ids, discount_type, decimal-string discount_amount, expires_at (UTC or null), usage_limit, max_per_customer, active, leaderboard_enabled, promo_codes, and pending_invitations. Each code has id (numeric code ID), code, promoter_membership_id, active, and share_url. Pending invitations have id, email, and code (null for older invitations without a reserved code). The promo_codes array contains assigned codes only, including inactive assignments until they are removed.
People and invitations
| Method | URL | Body / effect |
|---|---|---|
| POST | /admin/promoter_programs/:id/memberships |
{"membership_ids":[42,43],"codes":{"42":"LISA-CLUB","43":"BEN-CLUB"}}; add or reactivate personal codes, 201 |
| PATCH | /admin/promoter_programs/:id/memberships/:code_id |
No body; deactivate that personal code, 204 |
| DELETE | /admin/promoter_programs/:id/memberships/:code_id |
Remove an already inactive assignment, 204; an active code returns 422 |
| POST | /admin/promoter_programs/:id/invitations |
{"email":"lisa@example.com","code":"LISA-CLUB"}; reserve the chosen code and send an invitation linked to this program, 201 |
| DELETE | /admin/promoter_programs/:id/invitations/:invitation_id |
Remove this pending program assignment, preserve the team invitation, 204 |
Program invitation creation returns 201 with a Location header pointing to /admin/invitations.json. The response includes the invitation fields plus promoter_programs, an array of { "id": "PROGRAM_PUBLIC_ID", "name": "Club promoters", "code": "LISA-CLUB" } for its accessible assignments. Co-organizers never receive unrelated program names or codes from a shared invitation. Codes are null for legacy assignments without a reserved code. The promoter email identifies the shop and programs, includes reserved codes and links, and asks the recipient to accept before sharing.
Repeated membership assignment reuses the same code, including previous redemption counts. Inviting a removed member returns 422 and directs the organizer to add the member again instead; it does not reactivate or send an email. The codes value may be omitted for an existing assignment; a different value is rejected. Use the separate code-name edit to rename an existing code. A pending promoter invitation may belong to multiple programs; each program reserves its chosen code immediately as an inactive code. Acceptance assigns and activates those exact codes with current program terms, without another organizer action. Missing, invalid, or already used codes return 422 and do not send an invitation. Removing a pending assignment releases its reserved code. Older invitations without a reserved code still accept without generating a code. An existing invitation with a different role cannot be repurposed by this endpoint.
Terms and attribution
Common terms are edited on the program. Personal code names can still be changed through the promo-code endpoint; independent term/scope/ownership changes return 422. Program-managed codes additionally expose promoter_program_id (public ID) and event_ids in promo-code and promoter-report JSON. For these codes, use the program scope rather than legacy event_id.
Changes affect future redemptions. Existing purchases and reserved checkouts retain their prices and purchased event attribution. In mixed carts only covered event tickets are discounted and attributed; voucher products are excluded. Deactivate a personal code before removing its assignment. Deactivation stops new uses; removal hides the assignment from the program and promoter sharing dashboard while keeping sales history and program totals. Re-adding the person restores the same code. Pausing a program also blocks new uses. Read performance through Promoters.
Optional promoter leaderboard
Create or update a program with leaderboard_enabled: true to show its leaderboard to active promoters assigned to that program. The field defaults to false and is returned in program responses. Set it to false to disable access. The dashboard displays all-time ticket counts and names only, with a top-three podium and the current promoter highlighted. Each program is ranked separately. Other programs, accounts, emails, codes, links, revenue and buyer details are excluded. Pausing a program or deactivating a membership or its program code removes leaderboard access. This is an authenticated HTML dashboard feature; there is no public or JSON leaderboard endpoint.
Co-organizer scope
The same restriction applies to every program, code, membership and invitation action in the dashboard, personal API, assistant and MCP. Existing membership IDs must belong to promoters already visible through accessible codes; invite a new person using their exact email and chosen code. Co-organizers can rename and reactivate program codes, update common terms, and remove inactive assignments. They can remove a pending program invitation, but cannot resend or revoke the entire team invitation or change team roles. Program and People sales exclude unassigned events, including historical purchases made before a program changed its event scope. Revoked assignments take effect on the next request.