Sign in to see your venue's slug and publishable key in every sample.
Your account has no venue yet, so the samples keep their placeholders. Sign out
Signed in as · . Create a publishable key in the console and reload to see it here. Sign out
Signed in as · . The samples show your venue's publishable key. Sign out
Developer docs
Azure Static Web Apps
From nothing to a live booking page on Azure Static Web Apps — the page, GitHub Actions deployment, the key's origin list and your custom domain.
This tutorial takes a venue with no website at all to a booking page at https://www.your-venue.example, served over HTTPS by Azure Static Web Apps and deployed by GitHub Actions on every push. The page is three files; everything else is configuration you type once.
You need an Azure subscription, a GitHub account, the Azure CLI (2.29 or newer) and the GitHub CLI both signed in, a domain whose DNS records you can edit, and a BookDinePlay operator account with a venue.
The commands use two names throughout; pick your own and keep them the same in every step:
RG=rg-your-venue-site
APP=your-venue-site1. Create the Static Web App
A resource group and the app on the Free plan. The location only decides where the app's configuration is stored — the page itself is served from Azure's global edge:
az login
az group create --name "$RG" --location westeurope
az staticwebapp create --name "$APP" --resource-group "$RG" --location westeurope --sku Free
az staticwebapp show --name "$APP" --resource-group "$RG" --query defaultHostname -o tsvThe last command prints the app's generated hostname, ending in azurestaticapps.net. Keep it: it is the first origin your key will allow and the target of your DNS record later.
2. Create a publishable key for both origins
In the console, open Venue → API keys at app.bookdineplay.com/operator/venue and create a key of type Publishable. In Allowed origins list every origin the page will be served from — the generated hostname from step 1, the custom domain from step 5, and localhost for previewing — comma-separated:
https://<hostname from step 1>, https://www.your-venue.example, http://localhost:*A key's origin list is fixed when the key is created, which is why the custom domain goes in now, before it resolves. To add an origin later, create a new key with the full list, deploy it, then revoke the old one — Rotating a key. Copy the bdp_pk_… key; it goes into the page in the next step, and it is safe there because the list is what protects it (Origins).
3. Write the page
Create a folder for the repository with a site/ folder inside and the three files below:
mkdir -p your-venue-site/site && cd your-venue-site && git init -b mainsite/index.html loads the SDK from the CDN, pinned to an exact version with its integrity hash, and the small script that renders the widget:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Reserve a table</title>
</head>
<body>
<main>
<h1>Reserve your table</h1>
<div id="bookdineplay-widget"></div>
</main>
<script
src="https://cdn.bookdineplay.com/sdk/0.8.0/bookdineplay.js"
integrity="sha384-jWkVHY/3ODRIOP4AZxnLryBhQa58r73TbB8ZBp+1YGqTk++mutYPqmFp53yUIt9p"
crossorigin="anonymous"></script>
<script src="booking.js"></script>
</body>
</html>The integrity value is the hash published next to that version — verify it yourself with:
curl -s https://cdn.bookdineplay.com/sdk/0.8.0/sri.txtIt is a sha384-… string. The browser refuses to run the script if a single byte differs from it, and because the exact-version path never changes, it never will (Pinning an exact version). To upgrade later, change the version in src and the hash together.
site/booking.js renders the widget. It is a separate file rather than an inline <script> so the Content Security Policy below can stay strict:
window.BookDinePlay.renderBookingWidget({
container: '#bookdineplay-widget',
venueSlug: 'your-venue',
apiBaseUrl: 'https://api.bookdineplay.com',
publishableKey: 'bdp_pk_your_publishable_key',
resourceTypes: ['RestaurantTable', 'BilliardTable', 'DartBoard']
});site/staticwebapp.config.json adds response headers to every file the app serves. This policy allows scripts from your site and the CDN, requests to the BookDinePlay API only, and the inline styles the widget injects into its shadow root (Content Security Policy):
{
"globalHeaders": {
"Content-Security-Policy": "default-src 'self'; script-src 'self' https://cdn.bookdineplay.com; connect-src https://api.bookdineplay.com; style-src 'self' 'unsafe-inline'",
"X-Content-Type-Options": "nosniff"
}
}To preview, serve site/ from any local web server and open it through http://localhost — the key allows every localhost port. Opening the file directly (file://) sends Origin: null and is refused. Then commit:
git add . && git commit -m "Booking page"4. Deploy from GitHub Actions
Create the repository on GitHub, read the app's deployment token from Azure and store it as a repository secret — it never appears in the repository itself:
gh repo create your-venue-site --private --source . --push
az staticwebapp secrets list --name "$APP" --resource-group "$RG" --query "properties.apiKey" -o tsv \
| gh secret set AZURE_STATIC_WEB_APPS_API_TOKEN.github/workflows/deploy-site.yml uploads the site/ folder as it is on every push to main:
name: Deploy venue site
on:
push:
branches: [main]
workflow_dispatch:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: Azure/static-web-apps-deploy@v1
with:
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN }}
action: upload
app_location: site
output_location: ""
skip_app_build: trueskip_app_build: true tells the action to upload app_location as it is instead of looking for something to build; output_location must then be empty. Commit the workflow and push; the run takes about a minute:
git add .github && git commit -m "Deploy to Azure Static Web Apps" && git push
gh run list --limit 1Then open https://<hostname from step 1> — the widget is live on the generated hostname.
5. Point your domain at it
At your DNS provider, add a CNAME record for www whose value is the hostname from step 1. Then register the domain with the app; Azure validates the record and issues the certificate:
az staticwebapp hostname set --name "$APP" --resource-group "$RG" --hostname www.your-venue.exampleValidation waits for DNS to propagate — usually minutes, at most the record's TTL. The bare domain (your-venue.example without www) cannot carry a CNAME; it needs TXT validation (--validation-method dns-txt-token, then az staticwebapp hostname show … --query validationToken for the record value) and an ALIAS record — follow Set up an apex domain. Remember that the bare domain is a separate origin: it must have been on the key's list in step 2 as well.
6. Check the live page
Open https://www.your-venue.example. The widget should load and, after a date is picked, list times. If not:
- The widget renders but every request fails with 403
origin-not-allowed— this origin is not on the key's list. Create a new key that has it (step 2) and redeploy with the new key. - The script does not load and the console mentions integrity — the hash does not belong to the version in
src. Re-run thecurlfrom step 3 for exactly that version. - The console reports a Content Security Policy violation — a host is missing from
staticwebapp.config.json; the two BookDinePlay hosts arecdn.bookdineplay.comfor the script andapi.bookdineplay.comfor requests. hostname setkeeps waiting — theCNAMEhas not propagated yet;dig www.your-venue.example CNAME(ornslookup) must return the hostname from step 1 before Azure can validate it.
Next steps
- Booking flow — what the page you just deployed does on every click.
- Theming — the venue's colours on the widget.
- CDN — when the next SDK version is out, how to move your pinned version.