The Simulation page creates a brand-new event in the Customer Representation model — name, description, category, venue, capacity, date/time, price, lineups, setlists, optional special-ad category. Use it to probe Affinity behaviour without affecting real data. This page is feature-flagged behind SHOW_SIMULATION and gated on the user’s Permissions.prisma / Permissions.fdlive permissions. If your integration doesn’t expose simulation, skip it.

What you’ll build

  • A form for the event fields above.
  • Multi-date support — one form, N entries.
  • Submit handler that POSTs once per entry.

Prerequisites

  • An authenticated user.
  • X-Preferred-Partner-Id set.
  • User has Permissions.prisma (for product events) or fdlive permission.

The call chain

On mount: nothing. The form is purely client-side until submit. On submit (one call per date entry):
MethodURLHostNotes
POST/events/?event_type=prisma|fdliveCR APIBody = the event payload. No /v3/ prefix.

Body shape

{
  "name":               "Probe — Test Concert",
  "description":        "...",
  "venue":              "Olympiahalle",
  "capacity":           12000,
  "date":               "2026-09-15",
  "time":               "20:00",
  "city":               "München",
  "category":           "concert",
  "hall":               "Main Hall",
  "price":              { "min": 25, "max": 145 },
  "lineups":            [{ "name": "Headliner", "starts_at": "21:00" }],
  "setlists":           [],
  "tz":                 "Europe/Berlin",
  "special_ad_category": null
}
special_ad_category is only sent when the user selected the “special” tab in the form (housing / employment / credit / social-issues / etc.).

Reference implementation

import { fdCr } from "./fd-cr";

export async function createSimulationEvent(
  payload: SimulationPayload,
  eventType: "prisma" | "fdlive",
) {
  return fdCr.post(`/events/?event_type=${eventType}`, payload);
}

// Multi-date submit
export async function submitSimulationForm(
  rows: SimulationPayload[],
  eventType: "prisma" | "fdlive",
) {
  const results = await Promise.allSettled(
    rows.map(r => createSimulationEvent(r, eventType)),
  );
  const failed = results.filter(r => r.status === "rejected");
  if (failed.length > 0) {
    throw new Error(`${failed.length} of ${rows.length} entries failed`);
  }
  return results;
}
After successful submit, the reference webapp navigates to / (the Dashboard).

Gotchas

POST /events/?event_type=... on the CR API — not /v3/events/ on the main API. Different host.
There is no bulk endpoint here. For multi-date entries, one POST per row. If any fails partway, you have a partial commit — surface this clearly to the user.
Don’t expose event_type as a UI knob. Derive prisma from Permissions.prisma, otherwise fdlive.
The reference webapp shows the same common.errors.inServerResponse message for all failures and writes the actual error to Sentry. Surface the real error.<code> to the user instead — it’s more useful.