What you’ll build
- A form pre-filled with the event’s current values.
- A submit handler that PUTs a formatted payload to the CR API.
- Logic that conditionally appends
itc_retry=trueto trigger a recommendation refresh.
Prerequisites
- An
eid. - The partner’s product / access tier (drives
event_typeand thepartner_productquery param). Read from your client store; the reference webapp readspartnerDetails.productfrom browser storage. - User has Lookout or Wave access.
The mount call chain
| # | Method | Path | Host | Purpose |
|---|---|---|---|---|
| 1 | GET | /events/{eid} | CR API | Raw event payload (includes lineups_raw, description). |
| 2 | GET | /events/{eid}/campaigns | main API | Determines isPublished (any campaign with published === true). Gates whether itc_retry is ever sent. |
Submit
The reference FE uses PUT, not POST:| URL param | Values | When |
|---|---|---|
event_type | prisma or fdlive | Always. Derived from the partner’s access tier and the event’s category (see below). |
partner_product | one of the partner’s registered products | Always. Mismatch ⇒ 422. |
itc_retry | true | Only appended when !isPublished && hasRetryRelevantChanges. Omitted entirely otherwise (not sent as false). |
Two separate mechanisms: dirty-state vs itc_retry
These are unrelated — document and implement them separately.
A. Form dirty-state (fieldsChanged / hasChange): UI-only. Enables
the Save button and prevents empty submits. Never sent to the backend.
B. Recommendation-refresh flag (itc_retry): submit-time logic for CR
recommendation recalculation. Sent only when both hold:
!isPublished— the event has no published campaign (fromGET /events/{eid}/campaigns); andhasRetryRelevantChanges(...)— a recommendation-relevant field changed.
Which field changes count as recommendation-relevant
The reference FE compares (notename↔title mapping, and lineups via
lineups_raw):
The PUT payload is formatted, not the raw CR object
The FE builds the body viaformatData(...) — it does not echo back
the raw CR payload. Key field differences from a naive mapping:
| Field | Shape |
|---|---|
| Event title | name (not title) |
| Lineups | [{ artist_name, artist_type }] (not { name, starts_at }) |
| Setlists | [{ artist_name, track }] |
| Price | scalar price_category (not nested { min, max }) |
status | always "RUNNING" in the payload |
special_ad_category | default form → []; Prisma form → values from the special tab (always an array) |
Deriving event_type
Don’t expose event_type as a UI toggle. The reference FE derives it from:
hasPartialServiceAccessTier()— true when the partner product isPRISMAorFD_FULL_SERVICE, or the editor was opened from Wave (location.state?.from === 'wave') →prisma- otherwise →
fdlive
category also feeds the wave/product branch.)
Reference implementation
Post-save redirect
After a successful CR save, the reference FE:- Performs another CR
GET /events/{eid}, waits ~1s, then useshistory.replace(not push) so/events/edit/{eid}is not left in browser history. - Redirects to the list with the side drawer open:
- Lookout access:
/events?drawer-open=true&event-id={eid}&tab=0 - Wave-only:
/campaigns?drawer-open=true&event-id={eid}(the Wave example also appendstab=0in current FE)
- Lookout access:
- In the drawer, the event is loaded via the main API
GET /events/{eid}, not CR.
tab=0 resets to the first Event Detail tab (insights). The choice of
/events vs /campaigns depends only on Lookout permission, not on
category or where the editor was opened from.
Gotchas
PUT, not POST
PUT, not POST
The save is
cr_api.put(/events/{eid}?...). POST is wrong.Payload is formatted, not the raw CR object
Payload is formatted, not the raw CR object
name not title; lineups artist_name/artist_type; scalar
price_category; status: "RUNNING" always.itc_retry is conditional and omitted when false
itc_retry is conditional and omitted when false
Append
&itc_retry=true only when !isPublished && hasRetryRelevantChanges. Never send itc_retry=false.Dirty-state ≠ itc_retry
Dirty-state ≠ itc_retry
Form dirty-state is UI-only;
itc_retry is a separate submit-time
decision. Don’t conflate them.event_type and partner_product must match the partner
event_type and partner_product must match the partner
The CR API 422s on a mismatch. Derive
event_type from access tier +
category, not a UI toggle.Related
- Event detail — where the Edit menu lives.
- Simulation — same host, event creation.
- Bulk event upload — same host.
- CORS & two-host architecture.