--- title: "Use your own lobby" description: Keep your own game tiles, rows and search, and fill them with provider games from your OneHazel account. --- # Use your own lobby Surfaces can fill a block on your page for you. If you already have a lobby, you may not want that. Headless mode gives you the same provider and game list as plain data, and you draw it yourself. ## When to use this - You already have a lobby with your own tiles, rows and search, and you want a provider's games to appear in it. - You want to decide where the Play button goes and how the game window opens. If your page has no lobby, use the [Surfaces block](/connect/surfaces) instead. It draws everything for you. Which providers appear, and which of their games, is still set in the app under **Surfaces**. Nothing here changes that. ## Install Use the same script tag as the block, with one extra attribute. Use your own operator id, shown in the app under **Surfaces**. ```html ``` Nothing renders. The script adds no styles and fills no `data-oh-surface` block. Your page asks for the data when it wants it. ## Read the games ```js window.OneHazel.ready.then(function () { return window.OneHazel.lobby.games(); }).then(function (lobby) { lobby.vendors.forEach(function (vendor) { // draw vendor.name and vendor.games in your own tiles }); }); ``` `games()` answers `{ vendors }`. The script fetches the list once and reuses it, so calling `games()` again does not make a second request. Each vendor has these fields: | Field | What it is | |---|---| | `key` | The provider's short id. You pass it back when you launch a game. | | `name` | The provider's display name. | | `mode` | `demo` for play money. `real` for a provider you already play for real money. | | `games` | The games to show, as listed below. | | `logoDomain` | Optional. The provider's website, for logo lookups. | Each game has these fields: | Field | What it is | |---|---| | `code` | The game's id. You pass it back when you launch. | | `name` | The game's display name. | | `category` | Optional. One of `slots`, `live`, `crash`, `virtuals`, `table`, `other`. | | `thumbnail_url` | Optional. An https image address. | Treat the optional fields as possibly missing. If the list cannot be read, `games()` rejects with an object whose `code` is one of: | Code | Meaning | |---|---| | `surfaces_disabled` | Surfaces is not switched on for your account yet. | | `operator_unknown` | The operator id is missing or not recognised. Check it for typos. | | `unavailable` | OneHazel answered with an error. The object also has the HTTP `status`. | | `network` | The request did not get through. | A failed load also fires an `oh:error` event on `document` with the same `code`. ## Launch a game Ask for the game address inside the player's click, then open it yourself. ```js playButton.addEventListener('click', function () { window.OneHazel.lobby.launchUrl('vendor-key', 'game-code', { language: 'en' }) .then(function (url) { window.open(url, '_blank', 'noopener'); }) .catch(function (err) { // show your own message; err.code says what went wrong }); }); ``` `launchUrl(key, code, options)` uses a vendor `key` and a game `code` from the list. `language` is optional and defaults to English. It resolves to an https address. It never opens a window or changes your page, so the click and the window are yours. Some browsers block a window opened after a wait. If yours does, open a blank window straight away in the click, then set its address when the promise resolves. The launch is a play-money demo. No wallet is called and no player data is sent. If the launch fails, the promise rejects with an object: | Field | What it is | |---|---| | `code` | What went wrong, for example `game_unknown`, `invalid_request`, `unavailable` or `network`. | | `message` | A short human-readable line, when OneHazel sent one. | | `retryable` | `true` when trying again may work. Only set when OneHazel sent it. | | `status` | The HTTP status, on an `unavailable` answer that carried no detail. | `game_unknown` means the provider is not switched on for demo, or the game is not in its list. A reload of the list usually fixes it. The script also rejects with `bad_game_url` if the answer is not an https address. ## From your server instead You can skip the script and call the same two routes from your own back end. Both are public and need no key. ```bash curl -i https://api.onehazel.com/onehazel-api/embed/v1/op_xxxxx/manifest ``` The manifest is cached for 60 seconds and carries a strong `ETag`. Send it back in `If-None-Match` and an unchanged manifest answers `304` with no body. The games are under `surfaces.lobby.vendors`, with the same fields as above. ```bash curl -X POST https://api.onehazel.com/onehazel-api/embed/v1/op_xxxxx/demo-launch \ -H 'Content-Type: application/json' \ -d '{"key":"vendor-key","game_code":"game-code","language":"en"}' ``` The answer is `{ "game_url": "https://..." }`. A failure answers `{ "code", "message", "retryable" }` with the codes in the table above. Optional `client_type` can be `DESKTOP` or `MOBILE`. Both routes are rate limited per caller, so cache the manifest on your side rather than calling it for every page view. For staging, copy the base address shown in the staging app's snippet. ## Keep it fresh Call `window.OneHazel.refresh()` to fetch the list again. It resolves to the new manifest. If the fetch fails, the script keeps the last list it read, fires `oh:error`, and resolves with that kept list, so your page keeps working. It resolves `null` only when no list was ever read. After it resolves, call `games()` to read the list. The list changes when you switch a provider on or off, or choose its games, in the app. A reload of your page picks that up, and so does `refresh()`. ## What this is not - It does not launch real-money games yet. That arrives with the player bridge. - It sends no per-player data and needs no player login. - It does not import games into your own system. You read the list when you need it.