OpenPlanner

Open source · EUPL-1.2 · WCAG 2.2 AA

Video appointments with your team, booked in seconds.

OpenPlanner is the scheduler for video calls on Webex. A participant picks a free time on a page in your branding, gets a confirmation with a calendar invite and joins from the browser. No account, no app, no login. Your team receives the call at the right moment in Webex Contact Center, and personal data is gone 24 hours after the call.

Try the demo Install View the source

0
cookies on this site and on the booking page
24 h
after the call, personal data is purged
2
containers and one SQLite file, nothing more

What booking feels like for a participant

A real appointment page, without the real appointment. Everything works by keyboard. Nothing is stored or sent.

From click to call, and everything behind it

Two tracks through the product: what the participant experiences, from picking a time to the call, and what the team and the administrators hold, from the organisation to the agent who picks up.

The participant

The team and the administrators

Screen recording of the real interface

Recorded by the pipeline at release v0.4.0-rc.86 (2026-11-01), against the real application with test data: from a participant booking to the agent who picks up the call.

No sound; the captions describe what is shown. The steps are also in the tour above as text.

How it works

  1. 1

    You set up a group

    A team is a group: opening hours, staffing per block, holidays, exceptions, texts and branding. All in the admin portal, no code.

  2. 2

    Your website links to the page

    Every group has its own page on your domain. Or you place the calendar as a Web Component right inside your own site.

  3. 3

    The call arrives by itself

    As a work item in Webex Contact Center, or in OpenPlanner's own queue where staff with the agent role pick up the call themselves. Chosen per group.

Everything a call needs, nothing that distracts

Built for teams that serve many people by video: customer service, advisory desks, intake, helpdesks.

  • Booking without friction

    Month calendar, free times, three fields. Move or cancel through a personal link. Works without JavaScript and fully by keyboard.

  • Webex built in

    Every appointment gets its own Webex meeting. The participant joins from the browser, no app or account; your team joins as cohost.

  • Lands where your team works

    The call goes to the queue in Webex Contact Center as a work item. The first free agent picks up. Nobody has to watch a calendar.

  • Talk now

    A group that is open can offer a direct call, with a cap on the number waiting and honest queue feedback.

  • Multitenant with real roles

    One installation, several organisations, each with its own groups, branding, texts and identity provider. Roles and scope come from your IdP; one authorisation matrix decides what everyone sees and does.

  • Groups as skills

    A group is a skill with a subject, a team and a queue. The participant never picks a person; the first free agent picks up. One agent can be in several groups.

  • Privacy by design

    Personal data lives only on the booking and is gone 24 hours after the call. Never in logs, URLs, jobs or error messages.

  • Your brand, your words

    Colours, logo and font per organisation. Every text editable in the portal. Dutch and English out of the box, more locales possible.

  • Messages that arrive

    Confirmation, change, reminder and cancellation by email (with .ics) and SMS. SPF, DKIM and DMARC are checked for you.

  • Agenda and planner

    Admins follow every call live, plan staffing per block and close a group in one action when needed, after which participants pick a new time themselves.

Webex Meetings is the heart. Everything else connects to it.

OpenPlanner is not a video platform and not a contact center. It is the scheduler that ties together the Webex products you already have, and keeps every other system behind a clean adapter.

OpenPlanner

the heart

Webex Meetings

Every appointment is a real Webex meeting through the Meetings API. Browser-direct join, a trial call beforehand, live meeting status in the agenda.

ADR 0017

dispatch

Webex Contact Center

Five minutes before the start the call becomes a work item in the team's queue, carrying only a first name, the join link and routing attributes. Webex Contact Center is the first integration; the layer behind it is provider-neutral, and a group can also work without a contact center through OpenPlanner's own queue.

ADR 0018

alerts

Webex Messaging

A bot tells administrators in Webex when the integration falters, a queue fills up or a check fails. Never personal data, always a link to the portal.

ADR 0082

OIDC

Your identity provider

Administrators sign in through your own IdP (PingFederate, Keycloak, Entra ID, any OIDC provider), per organisation, with roles from the claims.

ADR 0083

channels

Email and SMS

SMTP and a provider-neutral SMS gateway per organisation. Texts per channel and per group editable in the portal.

ADR 0022

machine API

AI agents and bots

A contact-center bot (such as the Webex AI Agent) asks for free times and books on the participant's behalf through an API with scopes and per-client keys.

docs/agent-api.md

Why we call OpenPlanner enterprise-ready

Not because of a list of ticks, but because of how it is built: a core without a framework, every external service behind a port, and rules the pipeline enforces instead of a reviewer.

Architecture plate: what runs and what goes where

One server you manage yourself, two containers and one database. Everything outside it is a system you already have, connected through an adapter.

Who uses it

  • Participantbrowser or widget, no account, no app
  • Administrators and plannersadmin portal, sign-in at your own IdP
  • Agentswork item in Webex Contact Center or the own queue
  • Botsmachine API with OAuth client credentials

Your server

  • HTTPS proxyCaddy: Let's Encrypt or your own certificate
  • webparticipant pages, admin portal, API
  • workerscheduled jobs: meetings, messages, work items, purging
  • SQLite (WAL)personal data only on the appointment, erased 24 hours after the call
  • host updaterchecks the signature, takes a backup, rolls back on its own

Containers run without root on a read-only filesystem.

Systems you already have

  • Webex Meetingsa real meeting per appointment
  • Webex Contact Centerwork item with first name, join link and routing
  • Identity providerOIDC: PingFederate, Keycloak, Entra ID
  • Email and SMSSMTP and SMS gateway, per organisation
  • Log destinationssyslog, Splunk, Elastic or HTTP
  • GitHub releasessigned images with an SBOM
  • Webex Messagingalerts to administrators, without personal dataon the roadmap

What goes where

  1. The participant books over HTTPS; the data stays in the database on your server.
  2. The worker creates the Webex meeting and sends the confirmation and reminders by email or SMS.
  3. Five minutes before the start the contact center gets a work item with only the first name, the join link and the routing attributes.
  4. Administrators sign in at your own identity provider; every decision goes to the audit log, which you can ship to your own log systems.
  5. Updates arrive as signed images; the host updater checks the signature before it switches.

Simplified. The full plate, with every flow and the decision behind it, is in the repository. System context and containers

Architecture and building blocks

Channels

  • Participant pages and widget
  • Admin portal
  • Machine API for bots
  • Worker for scheduled jobs

Shared building blocks

  • Authorisation matrix
  • Translations nl and en
  • Settings register
  • Audit log and events

Domain

  • Booking and capacity
  • Groups and staffing
  • Rights and scope
  • Privacy rules

Pure Python, no framework, ≥ 90% test coverage

Ports and adapters

  • VideoProviderWebex Meetings
  • ContactCenterDispatcherWebex Contact Center, own queue
  • MessageSenderSMTP, SMS gateway
  • IdentityProviderOIDC: PingFederate, Keycloak, Entra ID
  • EventSinksyslog, Splunk, Elastic
  • TransactionSQLite
The arrows point one way. On every pull request import-linter refuses an import that breaks these layers. Architecture with diagrams
  • One core, replaceable edges

    Another contact center or video platform is a new adapter behind an existing port. The domain, the screens and the texts do not change.

  • Enforced, not agreed

    Layers, types, rights and privacy rules are checked on every pull request. What does not hold never reaches main.

  • One source per subject

    Rights, settings and the portal's structure each live in one place; their documentation is generated from the code.

  • Recorded decision by decision

    More than eighty ADRs with context, choice, settings and build status. An architect reads why things are the way they are, and what does not run yet.

Scalable, honestly

  1. runs today

    Now: one host, well above the design target

    Proxy, web and worker on one VM with SQLite in WAL mode. Readers do not block each other, a booking is a few rows under a write lock, several workers share the job queue. One installation serves many organisations. Scaling is vertical first: more CPU, memory and a faster disk.

  2. runs today

    A recorded limit

    The design target is about 80 bookings per working day. ADR 0036 records when we go further: above 2,000 bookings per working day, at a p95 above one second, with a stricter availability requirement, or when a client requires managed Postgres.

  3. designed

    After that: designed, not built yet

    Postgres with row locks, several web instances behind the proxy, workers on any host and shared storage for rate limits. All database access already sits behind one port in one adapter module, so the domain, the routes and the screens stay the same.

ADR 0036: scaling envelope and the path beyond one VM

Security and operations

  • One authorisation matrix

    Role × permission × level in one place. Every admin route declares one permission, every query filters by scope, every denial is in the audit log. No exception for superusers.

  • Multitenant, separated per organisation

    Every query filters by the role's scope; an organisation admin never sees another organisation, a group admin never another group. The same matrix drives the menu, the buttons and the API.

  • Single sign-on, no detour

    OIDC with PKCE, state and nonce, asymmetric algorithms only, eight-hour session. No local accounts, no break-glass login, on purpose.

  • An audit log you can forward

    Logins, changes and denials as structured events, to syslog, Splunk or Elastic. Without personal data.

  • Signed releases

    Images signed keyless with cosign, an SBOM (CycloneDX) on every release, CVE scans with trivy and grype, hashes in the lockfile. The installation verifies the signature.

  • A strict pipeline

    mypy strict, ruff, bandit, semgrep, gitleaks, mutation testing, schemathesis, OWASP ZAP, Playwright with axe-core and Lighthouse. Everything blocks the merge.

  • Light to run

    Two containers, one SQLite file, HTTPS with automatic certificates or your own proxy. Updates and release channel from the portal. The backup is one file.

  • Accessible, provably

    WCAG 2.2 AA on every public page, in every language, tested again on every pull request. No time limits, no CAPTCHA puzzles.

  • Configurable, not hard-coded

    Every threshold, limit and period is a setting with a class, a default and bounds. What is a protection cannot be switched off from the portal.

Quality and standards, provably

What we claim is in the code, in a test or in the pipeline. For each standard you see how far it is and where the evidence is.

StandardWhat it meansStatusEvidence
WCAG 2.2 AA (EN 301 549) Keyboard flow, axe-core with zero violations and Lighthouse ≥ 95 on every public page, in every language, on every pull request. Manual audit with NVDA and VoiceOver before go-live. tested in CI e2e/pages.json
NL Design System Colours, type and spacing of the participant pages come from NL Design System tokens; organisations adjust them within those tokens. built in docs/widget.md
HTTPS, HSTS and NCSC TLS guidelines HTTPS with HSTS (Forum Standaardisatie comply-or-explain list), TLS 1.2 and 1.3 per the NCSC guidelines. A check in CI and internet.nl in the go-live checklist are planned. partly ADR 0086
SPF, DKIM and DMARC Comply-or-explain standards for email. The portal checks each organisation's sender domain and shows what is missing. built in ADR 0073
OpenID Connect Administrators sign in through their own identity provider: PKCE, state and nonce, asymmetric algorithms only. Tested against Keycloak, PingFederate in production. built in ADR 0013
OpenAPI 3 The API specification is generated from the code, compared on every pull request and attacked with schemathesis. tested in CI docs/api.md
GDPR and privacy by design Personal data only on the booking, purged 24 hours after the call, never in logs. The data-flow diagram is the input for the organisation's DPIA. built in docs/dataflow.md
Building blocks for the BIO An audit log of every change and denial, security events on attacks, an auditor role and export to your log platform. The BIO itself remains the organisation's responsibility. built in ADR 0053
security.txt (RFC 9116) A fixed reporting point for vulnerabilities on every installation, with SECURITY.md in the repository. built in SECURITY.md
Software supply chain Every image signed keyless with cosign, a CycloneDX SBOM with every release, CVE scans with trivy and grype, dependencies pinned with hashes. tested in CI ADR 0006
Standard for Public Code EUPL-1.2, a licence per file through REUSE, decisions public as ADRs, and a publiccode.yml that CI validates and every release updates. The listing on developer.overheid.nl is still open. partly publiccode.yml
Penetration test An external penetration test before go-live and yearly after. Until then: OWASP ZAP on every merge and abuse cases as tests. planned docs/threat-model.md

Coding standards

  • Python 3.12 with full type checking (mypy strict) and ruff for style and common mistakes.
  • A layered architecture, enforced with import-linter: the domain knows no framework.
  • Every module and function with a Google-style docstring, readable by documentation tools.
  • Test coverage ≥ 90% on the domain and translations, mutation score ≥ 80%, abuse cases as tests.
  • Conventional Commits, one ADR per decision with its build status, and every PR answers seven review questions.
  • bandit, semgrep, gitleaks, pip-audit and a licence check on every pull request.

Automatic updates

  • The superuser picks the channel in the portal: stable, or release candidate for a test environment.
  • Install a new version with one click in the portal, or let the installation do it by itself.
  • The host verifies the signature of every image before the update; an unsigned image does not run.
  • Renovate keeps dependencies current weekly, security updates at once; a PR older than 14 days raises an alert.
  • Database migrations run automatically and can always be rolled back.

Open source, open decisions

OpenPlanner is published under the EUPL-1.2, the open-source licence of the European Commission. Not only the code is open: every design decision sits in the repository as an ADR, with what has been built of it.

  • EUPL-1.2

    Use, change and distribute it, commercially too. Compatible with GPL, AGPL and most European procurement rules.

  • Architecture Decision Records

    More than eighty decisions, each with context, choice, settings and build status. You read why things are the way they are, and what does not run yet.

  • Security up front

    A threat model per flow, an abuse case per endpoint, each with a test. Responsible disclosure through SECURITY.md.

  • Contributions welcome

    Issues, discussions and pull requests on GitHub. A CLA keeps the licence clean. Every PR answers the same seven review questions.

View on GitHub Contribute Report a vulnerability

Roadmap

Straight from GitHub: every open step of a decision is an issue. What is listed here is decided but not built yet.

72 open steps in 33 decisions

All open steps on GitHub

Releases

Every merge to main becomes a release candidate on the rc channel. A release is a signed image, an SBOM and this website, in one train.

Latest release

v0.3.0

Latest release candidate

v0.4.0-rc.88

All releases on GitHub Installation guide

On every merge to main and on every release this page is rebuilt with the releases of that moment. Your browser never talks to GitHub, which keeps this site free of third parties. This page was built from commit 61a1a964a390.

Put it on a test server this afternoon

One install script, a Let's Encrypt certificate and a demo group. Or start locally with docker compose and walk the portal as every role.

Read the installation guide Quick start in the README