# The Concierge — a real app, end to end You've built a tenant, deployed a page, and verified the trust claims. This is the capstone: a **complete reference app** that puts it all together — a private evening **Concierge** that knows a great deal about its user, discloses only the **minimum** to an outside commercial service, and still works with it. > **Get Started path:** [Vault Spaces](./vault-spaces.md) → [Deploy it](./deploy.md) → [Verify it](./verify.md) → **this**. --- ## What you're building One neutral page embeds two isolated iframes: - **Your private Space** — the branded Concierge, on *your* tenant. It knows the user (their city, their taste, their budget, who they're going out with) — all of it sealed in the vault, none of it on the page. - **A commercial event finder** — an ordinary third-party app with its *own* backend and its *own* data key. It knows nothing about the user. The agent calls one host tool, `search_events`. **Only a minimal, neutral query crosses** the boundary — `city`, `date`, a broad category, an attendee *count* — never a name, never the conversation. The commercial side returns real events; the agent gets them back **in the same turn** and recommends the one that fits the private context. That is **capability-mediated disclosure**: the app that holds the data discloses a capability, not the data. The seam is a single origin-checked `postMessage` — see [Embed a Space](./embed.md) and [Tools & widgets](./tools-widgets.md). The app is otherwise ready to run. Two pieces of config — one on each side of that boundary — bring it up. ## 1. Get a key from SeatGeek (the commercial side) The event finder is a normal app with its own data source. Get a free **SeatGeek Platform** client: 1. Sign up at [seatgeek.com/account/develop](https://seatgeek.com/account/develop) and create an app. 2. Copy the **Client ID** — the short `NjA2…` string. **Not** the 64-hex *client secret*: that's for OAuth flows this app doesn't use, and SeatGeek rejects it with `403 Invalid client credentials`. 3. Set it as a secret on the commercial app — `SEATGEEK_CLIENT_ID` (a Cloudflare Pages secret, or an env var wherever that page's backend runs) — and redeploy so it's picked up. This key lives entirely on the commercial side — **Witbitz never sees it**, which is the whole point: the outside service keeps its own credentials, the private Space keeps its own, and neither reaches across. Without a key the reference adapter falls back to deterministic mock events, so you can watch the flow before you wire real data. > One SeatGeek quirk worth knowing: its free-text `q` is a literal title match, so a genre word like `jazz` returns almost nothing. Pass genres as the **category** (the reference adapter maps them to SeatGeek's `type` param) and let the agent pick the fitting act out of the real list it gets back. ## 2. Add your origin to your tenant Your Concierge creates **tenant-attributed** Spaces, and the data plane is **fail-closed**: a tenant admits a member's browser **only** from an origin it has declared. Until you add yours, every read (`poll`, `state`, `turn`) is refused — the room stamps to your tenant, but nothing loads. This is the participation gate ([Identity & admission](./identity-and-admission.md#the-owner-rule)); confidentiality is still the room key, always. Declare the origin the Concierge is served from, authenticated with your tenant secret key: ``` PUT /v1/tenant/webhooks Authorization: Bearer { "origins": ["https://your-concierge.example.com"] } ``` Use the **exact** scheme + host the Concierge iframe loads from — that's the `Origin` the platform checks — and add **every** origin you serve it from (a preview alias and production are two distinct origins). You can also pass `origins` when you first register the tenant. See the [Platform API](./api-reference.md) for the full `/v1/tenant` surface. ## That's it Load the page. The Concierge auto-creates its Space **on your tenant** — every room now carries your `tenantId`, so the rooms it makes are inventoried and reclaimable, not anonymous ([Vault Spaces](./vault-spaces.md)). Ask it to find something to do, and watch the neutral query — and *only* that — cross to the event finder, real events come back, and the agent recommends from your side of the wall: the one recommendation the event service could never make, because it never learned who the user is. ## Next - **Reference:** [Embed a Space](./embed.md) · [Tools & widgets](./tools-widgets.md) · [The SDK](./sdk.md) · [Platform API](./api-reference.md). - **Check it, too:** [Verify it yourself](./verify.md) — the same admission gate and egress lock cover this app. > The Concierge now also runs as an **app-faced Space** — the Event Finder full-screen with the room as a pull-up card, including per-room resume and the sign-in patterns: see [App-faced Spaces](./app-spaces.md).