Embed Widget
Drop a Workestra booking calendar straight onto your marketing site — no Calendly subscription, no iframe glue, no extra plan tier.
Embed Widget
The embed widget puts a Workestra booking link inside your own site, instead of sending visitors to workestra.app/book/<slug>. Pick a slot, fill the form, confirm — all without leaving your page.
You'd reach for it when:
- You want a "Book a meeting" button on a pricing page or contact form, not a link that opens a new tab
- You need the booking experience to feel like part of your brand (your fonts, your background, your domain context)
- Your marketing analytics need to fire on the booking event (GA4, GTM, Segment) — the widget broadcasts a parent-page event you can listen for
- You want hard-locked control over which sites can embed your link (a domain whitelist enforced by the browser, not just by hope)
It's free on every plan — same scheduling stack, just with embed affordances turned on.
Screenshot needed — embed widget mounted in-page with a date/time picker and a brand color override
How it differs from sharing a link
| Public booking page | Embed widget | |
|---|---|---|
| URL | workestra.app/book/<slug> | Your own page (e.g. acme.com/contact) |
| Visitor leaves your site? | Yes — opens Workestra | No — stays on your page |
| Your branding? | Workspace theme only | Brand color + your page chrome around it |
| Domain control | Anyone with the URL | Per-domain whitelist enforced via CSP |
| Parent-page events | None | workestra:event_scheduled and friends fire on window |
| Setup | Copy the URL | Copy a snippet + paste into your HTML |
Both routes share the same booking flow under the hood — the same availability rules, pre-meeting form, round-robin pool, and reschedule/cancel tokens. The embed is a stripped-chrome variant of the public page, not a separate product.
Setting it up
Path: /settings/booking-links/<id>/embed. The page has three tabs.
1. Style tab
| Toggle | What it does |
|---|---|
| Embed enabled | Master switch. While off, the embed URL returns 404 and no site can iframe the link, even one on your whitelist. Existing /book/<slug> page is unaffected. |
| Hide event details | Hides the left "host info" pane (name, photo, description). Useful when your marketing page already shows that context. |
| Hide cookie banner | Suppresses the cookie/consent banner inside the iframe. Off by default; flip on if your parent page already asks for consent. |
| Skip landing page | Jumps straight to the slot list instead of the title/description splash. Good for tight in-page mounts. |
2. Allowed domains tab
This is the security boundary. Any domain you add can iframe the link; any domain you don't will be refused by the browser before it can render the widget.
Add origins one at a time:
https://acme.com
https://*.acme.com ← wildcard: accepts blog.acme.com, www.acme.com, etc.
http://localhost:3000 ← for devThe form rejects anything that isn't http:// or https:// (no javascript:, data:, file:, ftp). Origins are normalized — trailing slashes dropped, host lowercased, default ports stripped.
An empty whitelist means "no one can embed." If you turn the master switch on but don't add any domains, the widget will refuse to render anywhere. Workestra defaults to closed — you have to make a deliberate decision about who's allowed.
There's also an Allow any origin toggle gated behind a confirmation dialog with red copy. Use it only for public docs / demo pages where you genuinely want anyone on the internet to be able to iframe. Most teams should leave it off.
3. Snippet tab
Copy-paste HTML. The current Phase 1 release ships inline mode only — a sized <div> plus the loader script:
<div
data-workestra-embed-inline
data-url="https://workestra.app/embed/your-slug"
style="min-width: 320px; height: 700px;"
></div>
<script src="https://workestra.app/embed.js" async></script>Paste it where you want the calendar to appear. The min-width and height are starting hints — the loader auto-resizes the iframe as the booking flow advances (slot pick → form → confirmation), so the page doesn't jump.
The loader (embed.js) is one tiny self-mounting script (~2 KB gzipped). Drop it on the page once; it scans for any data-workestra-embed-inline blocks and mounts them.
Listening for events on your page
When a visitor confirms a booking, the widget emits a CustomEvent on window. You can wire any tag manager or analytics tool to it:
window.addEventListener("workestra:event_scheduled", (e) => {
const { booking_id, attendee, starts_at } = e.detail;
gtag("event", "meeting_booked", {
booking_id,
email: attendee.email,
starts_at,
});
});Other events you can listen for:
| Event | Fires when |
|---|---|
workestra:event_type_viewed | The widget finishes loading and the visitor sees the calendar |
workestra:date_selected | The visitor picks a date in the calendar |
workestra:event_scheduled | A booking is confirmed (the most common one to wire) |
workestra:embed_height | The iframe resizes — you don't usually need this; the loader handles resize itself |
The event.detail payload for event_scheduled includes the booking ID, slug, start/end times, attendee fields (name, email, phone if supplied), and any UTM params present on the parent URL.
What about the popup widget and popup text link?
Calendly users will recognize the floating button ("Book a meeting" button in the corner that opens a modal) and the popup text link ("Schedule a 30-minute meeting" hyperlink that does the same). Both are on the roadmap — Phase 2 of the embed scope.
Today the loader stubs both APIs so any host page that's already coded against them won't crash; it just logs a one-time console.warn("Popup mode not yet implemented"). We'll ship the popup chrome in a follow-up.
Privacy & data
The embed page lives on workestra.app, so cookies set there are first-party to Workestra and third-party to your site. The booking confirmation event fires the attendee's name + email back to your parent page so your CRM / analytics can pick them up — that's intentional, but it's your privacy policy's responsibility to disclose that you're collecting this data via the widget.
We don't pass any other workspace data, any other bookings, or any other attendees. Just the booking that just happened.
Read next
- Booking Links — set up the underlying link before you embed it
- Round-Robin — round-robin links work transparently inside the embed
- Pre-Meeting Forms — form fields work inside the embed exactly the same way
- Calendar Dashboard — embedded bookings show up on your
/calendarlike any other