Tutorial: Set Up Your Own Sandbox Team
The best way to understand how a team exists in Attend is to create one. In this tutorial you'll stand up your own client with a Flex and a Premium schema, wire them through code, and sell a seat in your own sandbox — the same exercise new engineers do in onboarding.
Unlike Trace a Purchase (a code read), this one is hands-on — you'll run real commands and click through Cortex, about 45–60 minutes. It teaches the moving parts; for the terse copy-paste recipe, keep How to onboard a new team open alongside.
What you need: the stack running locally (How to run the stack locally),
Cortex super-user access, and a DB client (DBeaver) on the dev RDS. We'll write everything as
tm_sandbox_<name>_* — substitute your own name.
Getting Cortex access: dev Cortex authenticates via staging FusionAuth (
auth-staging.attendevents.com). Internal users get an invite email (titled "partner dashboard" — the same login serves both), set an 8–12-char mixed-case password, and are then marked super-user. Don't depend on thefusionAuthSourceid (it changes if the account is recreated). Ask the staging admin if the invite doesn't arrive.
⚠️ The dev RDS is shared — only insert your own rows; don't touch anyone else's.
Step 1 — A team is two database rows (there's no "create" button)
"Where does a team even come from?"
In the dashboard schema (cortex_dashboard_dev), insert one tbl_clients row (your client)
and one tbl_client_schema row per schema — a Flex one and a Premium one. (Exact SQL is in
the how-to, Step 1.)
Things to notice:
- Cortex has no UI for this — clients/schemas are created directly in the DB. Cortex only
reads
tbl_clients/tbl_products/tbl_client_schemato build its workspace picker. - Register both schemas under the Flex product. Premium is administered under Flex in
Cortex (Suites & Premium Boxes) — there's no Premium product UI, so a row under the
Premiumproduct dead-ends the picker. (Chapter 02 is the client→product→schema model; the how-to callout explains the premium gotcha.)
✅ Checkpoint: you can explain the client → product → schema hierarchy and why a premium
schema is registered under the Flex product.
Step 2 — Build the schema's tables from the code
"The schema is named, but it's empty."
Call the builder on flex-v3-backend (POST :5011/builder/schema, once per schema). It reads
the TypeORM entity definitions and creates every tbl_* table — no migration files, no copying
from another schema.
Things to notice:
- Only flex-v3-backend has a team-schema builder; cortex-backend writes into the same tables but never creates them. One builder call gives both backends a schema to work with.
- The tables come out empty — Cortex and the inventory build fill them later (Chapter 17).
✅ Checkpoint: you can say where a team schema's tables come from (entities, via the builder) and which backend owns that.
Step 3 — Teach the backends your schema exists (the whitelist)
"Why won't the app accept my schema?"
Add a team config for each schema in both backends and both relevant frontends (5 files
total — see the how-to's table). Each backend validates the X-Team-Schema header against a
hard-coded TEAM_CONFIGS whitelist; an unknown schema is rejected.
Things to notice:
- This is why a new team is a code change, not just data — the whitelist is the SQL-injection barrier (Chapter 18 and the schema-safe-code how-to).
- flex-v3-backend needs both schemas (it's the shared fan backend for Flex and Premium); the Flex frontend gets the Flex config, the Premium frontend gets the Premium one.
- Use sandbox credentials (
dsn: 'sandbox',siteName: 'integ3') — Chapter 27 explains why those four values are the prod/sandbox switch.
✅ Checkpoint: you can explain why the same schema must be registered in multiple repos, and what
the TEAM_CONFIGS whitelist protects against.
Step 4 — Find your workspace in Cortex
"Is it alive?"
Restart both backends — the team whitelist (TEAM_CONFIGS) is a compiled-in constant built from
static imports, so a newly added config file is only picked up on process start (it's not a
refreshable runtime cache). Then in Cortex Change workspace → your org → Flex → your Flex
workspace. Both your Flex and Premium schemas appear under Flex.
✅ Checkpoint: your client appears in the org picker and you can enter the workspace — the DB rows
- code configs have met.
Step 5 — Stand up a sellable program
"Now actually sell something."
This is the full Cortex flow — and where the real-run gotchas live (the how-to's Step 4 callout spells them out):
- Events — Flex Events → Import → codes SS1–SS15 → Fetch from Ticketmaster → Import. They arrive as Drafts, often dated in the past (stale sandbox).
- Future-date each event while it's a Draft — the date locks once published.
- Create a venue (Venues & Maps) — name + map name; the map image is optional, because seats come from the TM manifest, not an uploaded map. Assign it to each event, then publish the events.
- Create the program (Build-Your-Own, seat-level) → add your published events → set a class name (Inventory Configuration) → Configure Pricing (non-zero) → Build Inventory (this pulls the seats from TM) → Publish.
- Buy a seat in your fan app at
/<your-schema>/events_home— sandbox card4444 3333 2222 1111, CVV111, any future expiry, any US zip.
✅ Checkpoint: a published program with built inventory exists, and you completed a sandbox purchase against your own schema.
What you now know
You can take a team from nothing to selling: two dashboard rows → a built schema → code configs that whitelist it → a Cortex-configured program → real seats pulled from the vendor. You've touched every layer — DB, both backends, both frontends, and the admin UI — and seen exactly where each one's responsibility starts and stops.
Next: now that you have a live sandbox, run Trace a Purchase against your own schema to connect this setup to the code that powers it.