Short URLs (/b) & Back-Compat Redirects
Buzz public links got shorter. The dashboard now lives under /b instead of /dashboard, registration and custom event-form links are much shorter, and every old link keeps working through automatic redirects.
What changed for hosts
The links you share with attendees and sponsors are now shorter and cleaner:
| Purpose | Old link | New link |
|---|---|---|
| Registration page | /dashboard/book-tickets/<event> |
/b/<event>/register |
| Custom event form | /dashboard/events/<event>/forms/<form> |
/b/<event>/<form> |
| Event proposal | /dashboard/event-proposal |
/b/event-proposal |
| Attendee account / tickets | /dashboard/account/tickets |
/b/account/tickets |
| Check-in scanner | /dashboard/check-in/<event> |
/b/check-in/<event> |
You don't need to update anything you've already shared — old /dashboard/* links redirect to the new /b/* links automatically. The Copy link and View Registration Page buttons on the Buzz Event form already produce the new short links.
One thing to know: a few words are now reserved and can't be used as an event route (because they're top-level app paths). If you try to save an event whose route is one of these, Buzz will ask you to pick another:
account,bookings,tickets,register-interest,check-in,book-tickets,event-proposal,events. Likewise, a custom form route can't beregister(that's the event's registration page).
How it works (for developers)
A request can arrive either as a new /b/* link or as a legacy /dashboard/* link. Legacy links are 301-redirected to the new scheme at the web layer; everything under /b/* is then served by the dashboard SPA, whose Vue router resolves the final page. Registration lives at /b/<event>/register, matched before the /b/<event>/<form> custom-form catch-all so it always wins. The reserved-route guard on Buzz Event prevents an event slug from shadowing a real dashboard route, which is what makes the two-segment URLs safe.
flowchart TD
visitor(["Incoming request"]) --> legacy{"Legacy /dashboard/* link?"}
legacy -->|yes| redirect["301 redirect to /b/*<br/>(website_redirects, hooks.py)"]
legacy -->|no| routerule["/b/* served by dashboard SPA<br/>(website_route_rules)"]
redirect --> routerule
routerule --> spa["Vue router, base /b<br/>static routes matched first"]
spa --> inapp{"Old in-app path?"}
inapp -->|yes| inappredirect["Router redirect to<br/>new route name"]
inappredirect -. re-resolve .-> spa
inapp -->|no| reg{"/<event>/register ?"}
reg -->|yes| booking["Registration page<br/>(matched first)"]
reg -->|no| catchall["Catch-all (declared last)<br/>/<event>/<form> custom form"]
guard[("Reserved-routes guard<br/>(buzz_event.py) blocks<br/>shadowing event slugs")] -. guards .-> catchall
booking --> page(["Page renders"])
catchall --> page
URL scheme
The SPA base path moved from /dashboard to /b (dashboard/src/router.ts → createWebHistory("/b")), and buzz/hooks.py serves the SPA from the new base:
website_route_rules = [
{"from_route": "/b/<path:app_path>", "to_route": "dashboard"},
]
| Area | Old path | New path |
|---|---|---|
| SPA base | /dashboard |
/b |
| Booking | /dashboard/book-tickets/:event |
/b/:event/register |
| Custom form | /dashboard/events/:event/forms/:form |
/b/:event/:form |
| Event proposal (global) | /dashboard/event-proposal |
/b/event-proposal (unchanged path) |
| Payment redirects | /dashboard/...?success=true |
/b/...?success=true |
Back-compat redirects
Old links keep working through two layers.
1. Server-side 301s (buzz/hooks.py) — ordered specific → catch-all, first match wins:
website_redirects = [
{"source": r"/dashboard/events/([^/]+)/forms/([^/]+)", "target": r"/b/\1/\2"},
{"source": r"/dashboard/book-tickets/(.+)", "target": r"/b/\1/register"},
{"source": r"/dashboard/(.*)", "target": r"/b/\1"},
]
2. In-app Vue router redirects (dashboard/src/router.ts) — so deep links and client-side navigations to the old paths still resolve:
{ path: "/book-tickets/:eventRoute", redirect: (to) => ({ name: "event-booking", params: to.params }) },
{ path: "/events/:eventRoute/forms/:formRoute", redirect: (to) => ({ name: "custom-form", params: to.params }) },
Reserved routes
Because event URLs are now /b/<event>/... (event first), an event route must not collide with a top-level dashboard segment, or the event would shadow a real route. buzz/events/doctype/buzz_event/buzz_event.py enforces this on validate:
RESERVED_EVENT_ROUTES = {
"account", "bookings", "tickets", "register-interest",
"check-in", "book-tickets", "event-proposal", "events",
}
RESERVED_FORM_ROUTES = {
"register",
}
If an event's route resolves to a reserved event word, save fails with a clear message. A custom form route can't be register, since /b/<event>/register is the registration page — it's matched before the /b/<event>/<form> catch-all (declared last in the Vue router), so registration always wins and a form named register would otherwise be unreachable.
Files touched
buzz/hooks.py— new/broute rule +website_redirectsdashboard/src/router.ts— base/b, new route names, in-app back-compat redirects, catch-all declared lastbuzz/events/doctype/buzz_event/buzz_event.py—RESERVED_EVENT_ROUTES+RESERVED_FORM_ROUTESguardsbuzz/events/doctype/buzz_event/buzz_event.js— Copy link / web link buttons emit/b/*buzz/api/__init__.py,buzz/api/auth.py,buzz/payments.pycallers,buzz/buzz_marketing/.../buzz_campaign.py,buzz/proposals/.../sponsorship_enquiry.py,buzz/ticketing/.../event_ticket.py— server-generated links/redirects updated to/b/*e2e/page objects and specs — updated to the new paths