What your integration needs
Find your row, then read only the track that applies to it. Most partners are in the top half of this table and never need a key at all.
| What you're building | What you need from us | Which track |
|---|---|---|
| The checkout drawer | An event ID, with checkout enabled on that event. Your organization ID for branding. | Registration & checkout |
| Event listings from the public API | An organization ID — a UUID. Nothing else. | Registration & checkout |
| Event-card fragments | An organization ID. | Registration & checkout |
| Hosted storefront & booking | An organization ID, and your branding set on your org record. | Registration & checkout |
| The org parent portal | An organization ID. It's one link. | Registration & checkout |
| Clearance inside registration | Nothing to integrate — but tell us your requirement set so we configure what gets collected. | Registration & checkout |
| Candidate clearance API | An organization ID and a Partner API key. | Partner API |
| Staffing & jobs API | A Partner API key (the same key covers clearance) and a webhook endpoint. | Partner API |
The common case needs no API key
Registration and checkout — what most partners come for — is entirely key-free. The public event endpoints are unauthenticated because they only ever return published, public event data, and the checkout drawer authenticates the parent, not you. So the whole integration is one UUID and no secret management.
Keys exist for the server-to-server products, clearance and staffing, where you're acting on your own behalf rather than rendering a parent's purchase.
Email us this
This is step one for everybody, so it's at the top rather than buried at the bottom. Paste it, fill in what applies, and we can usually create everything in one pass instead of three rounds of email. Send it to support@thegoodgame.com.
Organization name: e.g. Riverside Sports
Primary contact + email: who we send the organization ID to
Products we want: registration & checkout / clearance / staffing
Way for each: hosted / embedded-SDK / API
Checkout mode: single-camp (default) / multi-camp (enable Multi-Camp Cart)
Domains we'll embed on: e.g. www.riversidesports.com, camps.riversidesports.com
Branding: logo file, display name, the word you use for events
("camps", "clinics", "sessions"), Meta Pixel ID if any
Clearance requirements: which waivers, forms and documents each event must collect,
and how strictly each policy must be accepted
(informational / checkbox / drawn signature)
Webhook URL: https://… (must answer 2xx within 15 seconds)
Scopes needed: e.g. jobs:write, coaches:read, offers:write
Staging as well? say yes — and ask for a staging organization AND a staging
event, or your first request 404s for boring reasons
Not sure which products or which way? Ways to integrate explains the three shapes, and Example integrations shows four businesses picking different mixes — find the one that looks like yours and copy it.
Three steps, no key
The whole track, for selling registrations on your own site or ours. If this is all you're doing, you're finished after your first booking — everything past the next rule is about the Partner API and does not apply to you.
-
Email us and get your organization ID
Send the block above. You get back a UUID, and we set your branding on your org record at the same time.
The organization ID is not a secret and grants nothing on its own — it's a view filter. It's safe in client-side code, in a URL, and in your public HTML.
-
Ask us to enable checkout on the events you're selling
An event needs a checkout configuration before it can take a registration. Without one the drawer opens and says registration isn't open — the single most common "why doesn't this work", and not a code problem.
Choose your checkout mode: single-camp checkout is the default (Multi-Camp Cart OFF). For several camps from your organization in one cart, one checkout and one charge, ask your TGG contact to turn Multi-Camp Cart ON for your organization in TGG admin. This is separate from enabling checkout on each event: it is not a per-event setting or a code flag. Preview both options.
-
Drop in the loader and mark your buttons
Two lines, and that's the entire embedded integration. See the checkout drawer for the JavaScript API, or hosted storefront if you'd rather just link to us and write no code at all.
HTML<script src="https://my-v2.thegoodgame.com/embed.js" defer></script> <button data-tgg-checkout data-event-id="{event_id}">Register Now</button>The example above opens checkout directly for one camp. When Multi-Camp Cart is enabled, the existing loader also shows the cart widget without a partner deploy. Use the Add to cart buttons and cart API for your custom camp cards, and preview cart styling for colors, placement and labels. Widget styling does not turn the checkout mode on or off.
Test on staging first by swapping the loader to
staging.my-v2.thegoodgame.com/embed.js with a staging event ID — see
Environments — then walk the
go-live checklist before your first real registration.
Your first calls
Once you have an organization ID, this is your entire read surface for building a catalogue. No key, no headers, no signing — these endpoints are unauthenticated because they only return published, public event data.
# 1. Your organization's events, for a listing page.
curl "{API_BASE_URL}/api/v1/organizations/{organization_id}/events?size=5"
# 2. One event in full — schedule, venue, pricing, requirements, FAQs.
curl "{API_BASE_URL}/api/v1/events/{event_id}"
# 3. Live pricing and availability — the same bootstrap our own checkout reads,
# so your page can't advertise a session the drawer will refuse.
curl "{API_BASE_URL}/api/v1/ec/events/{event_id}/checkout-config"
Every field, plus two response-shape gotchas worth knowing before you write your parser, is in the API section.
Four more steps
Only for clearance and staffing. These come in addition to getting your organization — you still start with the same email.
-
We issue your API key
You can't mint your own. It's shown to you exactly once at creation and stored only as a SHA-256 hash, so if you lose it we can't recover it — only replace it. Details in API keys in detail.
-
Build against staging
Staging is a separate database: your production organization ID, event IDs and keys do not exist there. Ask for a staging organization and at least one staging event when you ask for the key.
-
Register your webhook URL
Send us an HTTPS endpoint; we generate your signing secret and can fire a test delivery at it, so you can prove your verifier works before a real event depends on it. See Registering a webhook — the test payload has a shape that catches people out.
-
We verify your sandbox and promote you to live
We confirm four things, then mark you live. Sandbox & promotion covers what that does and, more importantly, what it doesn't.
API keys in detail
Worth reading before you write your secret-management code — two of these will surprise you.
curl "{API_BASE_URL}/api/v1/partner/jobs" \
-H "Authorization: Bearer tgg_pk_live_xxxxxxxxxxxxxxxxxxxxxxxx"
# or, if a bearer header is awkward in your stack:
curl "{API_BASE_URL}/api/v1/partner/jobs" \
-H "X-API-Key: tgg_pk_live_xxxxxxxxxxxxxxxxxxxxxxxx"
| Property | How it works |
|---|---|
| Format |
tgg_pk_live_ followed by 24 random base64url characters. Malformed
keys are rejected before any lookup, so a truncated key gives you
401 Missing or malformed API key rather than something confusing.
|
| Shown once | Displayed at creation and never again — we store only its SHA-256 hash, so we cannot recover or re-read it. Keys are told apart by the label you give us when we create one, so name them for where they run ("prod-api", "staging-ci") rather than expecting us to identify them later. |
| Scopes | Recorded on the key. On staffing and clearance they are not enforced yet — a valid key reaches every partner route regardless of what it was granted. Ask for the narrow set anyway, and treat the key as fully privileged in your threat model until enforcement ships. |
| Expiry |
Optional. If set, the key stops working at that moment with 401.
|
| Rotation | Multiple live keys per partner are fine, so rotate without downtime: ask for the new key, cut over, then have us revoke the old one. Revocation is immediate. |
| Rate limit | Your record carries 60 requests/minute. It isn't enforced today, but it's the budget we'll hold you to — don't design for burst capacity you'd lose. |
| Org suspension |
Access is per partner organization, not per key. If your org is suspended, every
key returns 401 immediately — so a sudden total 401
across all keys is an account question, not a code bug.
|
Every key says live, including your
staging key
The _live_ in tgg_pk_live_… is part of the fixed prefix, not
an environment marker. There is no tgg_pk_test_. Your staging key and
your production key are indistinguishable by inspection —
the environment is the base URL, never the key.
So don't write a guard that infers environment from the key string, and do label the two clearly in your own secret store. Pointing a staging deploy at the production base URL with a production key will not fail loudly; it will just work, against real data.
Registering a webhook
For staffing. Signing, verification code and the retry ladder are in Reference → Webhooks; this is just the setup sequence.
-
Send us an HTTPS endpoint
It needs to answer any
2xxwithin 15 seconds. Do the real work asynchronously — acknowledge first, process after, or you'll trip retries you didn't need. -
We generate your signing secret
You don't choose it. Note that if no webhook URL is configured, events are silently skipped rather than queued — so an unset URL looks exactly like "nothing happened".
-
Ask us to fire a test delivery
Cheapest proof that your endpoint, TLS and signature check all work. Two gotchas: it arrives as
offer.accepted(there is no distinct test type), and its body is{ "test": true, "message": "…", "timestamp": "…" }— noofferId. Checkdata.testbefore you dereference an offer payload. -
Confirm you verified the signature, not just the status code
A test that returns
200from a handler that never checked the signature has proven nothing. Verify against the raw request body — parsing and re-serialising the JSON changes the bytes, and the HMAC will not match.
Rotation has no dual-signing window
When we rotate your secret, the previous one is retained on your record but deliveries are signed with the new secret from that moment on — we do not send two signatures or accept the old one during a grace period.
So make your verifier accept either secret across a rotation, deploy that first, then ask us to rotate, then drop the old one. Rotating before your verifier accepts both means every delivery in between fails its signature check and retries until it dead-letters.
Sandbox & promotion
Your partner organization moves through three stages, and we track four concrete items to get you from one end to the other.
| Stage | What it means |
|---|---|
created |
Your record exists. Keys can already be issued, and they already work. |
sandbox |
You've proven the integration end to end against staging, including a delivered test webhook. |
live |
We've promoted you, and the date is recorded. |
The four items we watch
The stage is a label, not a gate
This is the easiest thing to get wrong about our onboarding.
Your key works from the moment your partner organization is
active
— which is immediately. Nothing checks the stage before serving a request, so a key
issued at created can hit production endpoints straight away.
So you're never blocked waiting on promotion — but "we haven't gone live yet" is not a safety net. A staging key pointed at the production base URL acts on production data on day one. Keep the environments separate on your side.
We can see how your integration is behaving
Your partner record carries live counters — offers sent, accepted, declined and pending, your accept rate, webhook deliveries succeeded and failed, how many are sitting in the dead-letter queue, and when we last saw any activity from you. We can also replay a failed delivery on request.
So when something looks wrong, ask before you instrument: it's usually faster for us to read you the delivery history than for you to add logging and discover we never sent it.
Environments and the last mile
Environments
Two environments, staging and production. They share no data — organizations, events,
keys and bookings all exist independently in each, so ask for staging copies of anything
you need to test against. Base URLs are in
Reference → Base URLs; staging is the same host with
a staging. prefix. Staging runs Stripe in test mode: use test cards, never
a real one.
The embed loader derives everything from its own src, so switching
environments in an embedded integration is one URL swap:
staging.my-v2…/embed.js instead of my-v2…/embed.js. Don't mix
them — a staging loader with a production event ID simply won't find the event.
Go-live checklist
Worth walking before your first real registration, not after it. The last two items only apply if you're on the Partner API track.
booking-complete handler fires
your analytics and does something useful with the bookingId.
X-TGG-Delivery-Id.