SpeakerWeave is an open-source conference speaker-management platform for running the program side of an event—from call for papers through review, speaker operations, content collection, scheduling, and publication. It is built for teams that want a complete product they can host as-is or adapt behind clear database, auth, email, AI, and integration boundaries.
Building with an AI agent? Start at AGENTS.md. It maps the codebase, invariants, provider swap points, reference stack, and exact quality gates.
Product & API docs: speaker-weave.mintlify.site — source in docs-site/ — cd docs-site && mint dev to preview locally, or connect the repo at mintlify.com to publish it.
Live demo: speakerweave.com — enter the seeded workspace without signing up.
- CFP and conditional forms: multi-page form builder, reusable contact/session fields, show/hide/require rules, routing rules, drafts, and server-side enforcement.
- Review workflows: weighted scale, select, and text criteria; track-aware reviewer assignment; review windows; multiple rounds; anonymized rounds; decisions; and optional AI first-pass triage with human overrides.
- Speaker CRM: event rosters plus an organization-wide people directory, deduplication and merge tools, notes, tags, custom fields, saved segments, history, and a sourcing pipeline.
- Content pipeline: speaker portal tasks, uploads, approval/needs-changes states, comments, immutable file versions, restore, reminders, and ZIP/CSV exports.
- Agenda builder: drag-and-drop, click-to-place, multi-day room grids, live client and server conflict detection, and conflict-free auto-place.
- Public program: schedule and speaker pages, responsive script/iframe widgets, read-only JSON feeds, per-session calendar downloads, and a subscribable iCal feed.
- Per-event branding: colors, heading/body fonts, corner radius, layout and density choices, logo and favicon on every public page and embed — edited in Settings or through the API, agent, and MCP branding tools, scoped per event.
- Multi-organization workspaces: organizer membership across organizations with an in-app workspace switcher, and cross-organization speaker sign-in via emailed links.
- One-click demo doors: organizer, reviewer, and speaker entrances minted with the real magic-link machinery, so a visitor experiences exactly what an invited user would.
- Full REST API: organization-scoped API tokens, a stable
/v1integration surface, and interactive FastAPI OpenAPI docs. - Hosted MCP server: remote Streamable HTTP at
/mcp, with bearer-token access and OAuth 2.1 discovery/PKCE for Claude and ChatGPT connector UIs. - One AI agent in-app and in Slack: in-app Ask and the signed Slack bot share the same provider-neutral runtime, organization-scoped tools, MCP connectors, persisted threads, and permission gate. Approve or deny actions from Slack; Slack conversations remain visible in Ask history.
- Airtable sync: per-organization credentials and upsert syncs for Speakers and Submissions.
- Outbox-backed email: queued invitations and reminders, retry/idempotency handling, Resend delivery, native calendar invitations, and local
.emloutput when no provider key is configured.

Explore the product and enter the seeded conference workspace without signing up.

Build a multi-room agenda with drag-and-drop scheduling and live conflict feedback.

Run structured, track-aware reviews and make program decisions from one workspace.
The browser never connects directly to the database. The web tier — nginx in the reference deployment, or the included Cloudflare Worker — serves the React/Vite build and proxies application API, public feed, MCP, and OAuth requests to FastAPI. FastAPI uses supabase-py as a PostgREST client with a Supabase service-role key; all tenant queries are scoped in the application by org_id, with database RLS enabled as a backstop. When enabled, the API lifespan also runs the email outbox worker.
Browser / embedded widget / MCP client
|
v
+------------------+
| nginx + React | static Vite SPA
| /api /public | reverse proxy
| /mcp /oauth |
+--------+---------+
|
v
+------------------+ signed Events + Interactivity
| FastAPI | <-------------------------------- Slack
| REST + MCP | --------------------------------> Slack API
| agent.run_turn |
| OAuth + worker |
+---+----------+---+
| |
PostgREST| | HTTPS
v v
+----------------+ Resend / OpenAI /
| Supabase | Anthropic / Airtable
| Postgres |
| Storage |
+----------------+
The reference production shape is two application services—api/ and web/—plus Supabase. The outbox worker is an in-process background task, not a third deployment.
- Python 3.12 with
venvandpip - Node.js 20 with npm
- PostgreSQL plus
psql; the shortest path for this implementation is a Supabase project because the API expects PostgREST, a service-role key, and Supabase Storage - A Supabase
portal-filesStorage bucket marked public if you want speaker uploads or the full demo seed
The SQL uses the btree_gist and citext extensions. Several migrations (014, 015, 016, 019) also apply grants to Supabase's anon, authenticated, and service_role roles. A plain PostgreSQL deployment is viable, but it needs equivalent roles/grants and either PostgREST + compatible Storage or a replacement for the Supabase client/storage adapter.
git clone https://github.com/Brandonmchu/speakerweave.git
cd speakerweave
python3.12 -m venv api/venv
source api/venv/bin/activate
pip install -r api/requirements.txt
cd web
npm ci
cd ..cp api/.env.example api/.env
cp web/.env.example web/.envAt minimum, replace these values in api/.env:
SUPABASE_URL: the PostgREST project URLSUPABASE_SERVICE_API_KEY: the service-role key; never expose it to the browserSUPABASE_JWT_SECRET: a long HS256 secret used to verify organizer JWTs and mint local demo tokensPORTAL_SESSION_SECRET: a separate long secret for speaker/reviewer/submitter session cookies
The API example contains every environment variable read by the Python code, including optional integrations and operational defaults. For local dev, leave VITE_BACKEND_URL empty so Vite proxies to http://localhost:8000; leave VITE_CLERK_PUBLISHABLE_KEY unset to use the built-in dev-token flow. To enable Clerk, set that build-time web variable and configure a Clerk JWT template named supabase that is HS256-signed with SUPABASE_JWT_SECRET and includes aud: authenticated plus an org_id claim.
From the repository root, use the database's direct PostgreSQL connection string—not SUPABASE_URL, which is the HTTP PostgREST URL:
DATABASE_URL='postgresql://postgres:password@host:5432/postgres'
for migration in api/migrations/*.sql; do
psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -f "$migration"
doneThe zero-padded filenames make the shell loop apply every migration in order, including the Slack-to-agent thread mapping. All migrations are intended to be safe to re-run. Before seeding, create a public Storage bucket named portal-files in Supabase Dashboard → Storage.
cd api
source venv/bin/activate
python -m scripts.seed_demo seed
python scripts/mint_dev_token.pyseed resets and repopulates the known demo rows in org_dev; do not use that organization for production data. For the full multi-event demo workspace (four extra conferences at different lifecycle stages), run python -m scripts.seed_aie_events full instead — it reseeds the flagship event and then the extras in one pass. The second command prints a short-lived organizer JWT for /dev-login. You can also use /demo, which obtains the same kind of token from the deliberately public org_dev demo endpoint — and the landing page offers reviewer and speaker demo doors minted with the same magic-link machinery (/public/demo-entry/{organizer|reviewer|speaker}).
Terminal one:
cd api
source venv/bin/activate
uvicorn main:app --reload --host 0.0.0.0 --port 8000Terminal two:
cd web
npm run devOpen http://localhost:5173/demo. API health is at http://localhost:8000/health, and FastAPI's generated docs are at http://localhost:8000/docs.
Railway is the reference, but any two-service host works:
- API service: root directory
api; install withpip install -r requirements.txt; start withuvicorn main:app --host 0.0.0.0 --port $PORT; health check/health; run migrations as a release/one-off job. - Web service: root directory
web; build the included Dockerfile. Set runtimeBACKEND_URLto the public API origin with no trailing slash. Keep build-timeVITE_BACKEND_URLempty for nginx's same-origin proxy, and passVITE_CLERK_PUBLISHABLE_KEYat build time if Clerk is enabled. - Cross-service URLs: on the API, set
FRONTEND_URLandPUBLIC_APP_URLto the public web origin,PUBLIC_API_URLto the directly reachable API origin, andCORS_ALLOWED_ORIGINSto an explicit comma-separated allowlist. - Workers: set
OUTBOX_WORKER_ENABLED=1to drain queued mail. Multiple uvicorn workers are supported by optimistic row claims, although in-process rate limits are divided by the configured worker count.
The web tier also ships as a Cloudflare Worker (web/wrangler.jsonc + web/worker/index.js): static assets served from Cloudflare's edge with Brotli and SPA fallback, and the same /api, /public, /mcp, and OAuth proxy contract as the nginx image — SSE chat streaming included. Point BACKEND_URL in wrangler.jsonc at your API origin, then:
cd web && npm run build && npx wrangler deployThe reference deployment runs live at https://speakerweave-web.brandon-c2f.workers.dev against the same API and database as the primary site.
| Layer | This implementation | Swap guidance |
|---|---|---|
| Database | Supabase Postgres, Supabase Storage, and supabase-py/PostgREST |
Use whatever you'd like — just point the data layer at compatible PostgREST or replace its client and storage adapter. In this implementation, we use SUPABASE_URL plus SUPABASE_SERVICE_API_KEY with Supabase Postgres. |
| Auth | Clerk in the SPA; HS256 JWT verification in FastAPI | Use whatever you'd like — just point web token acquisition at a JWT issuer that supplies an org_id claim and signs with SUPABASE_JWT_SECRET. In this implementation, we use Clerk's supabase JWT template; the dev-token flow needs no external auth. |
One send_email boundary in api/services/mailer.py |
Use whatever you'd like — just point the outbox worker at your provider implementation of that function. In this implementation, we use Resend and write local .eml files when its key is absent. |
|
| Hosting | Two Railway services: uvicorn API and nginx/static SPA — plus a ready-made Cloudflare Workers web tier (web/wrangler.jsonc) |
Use whatever you'd like — just point a Python container or uvicorn service and a static SPA host at one another. In this implementation, we use Railway, with an edge deployment on Cloudflare Workers. |
| AI | Optional provider-neutral agent runtime (OpenAI Agents SDK or Anthropic) shared by in-app Ask and Slack; separate AI triage and /api/assistant/chat boundaries |
Swap the agent model loop in api/agent/runtime_openai.py or runtime_anthropic.py while preserving agent/service.run_turn, its event protocol, tool registry, thread persistence, and permission gate. api/services/assistant.py remains only for /api/assistant/chat. Without either provider key, the agent surfaces stay dormant and triage falls back to reviewer-score heuristics. See docs/chat-agent.md. |
| Integrations | Optional, per-organization Airtable settings; signed Slack Events API + Interactivity transport | Replace integrations at their service boundaries. Slack must remain a transport over api/agent/service.run_turn, with signature verification, the same endpoint for events and approval buttons, and organization-scoped thread mappings. Core conference workflows remain available without Slack or Airtable. |
Open /developers on any deployment for the endpoint reference and copyable examples. An organizer creates a token under Settings → API tokens; the raw dais_… value is shown once and only its SHA-256 hash is stored. Send it as x-access-token to /v1:
curl https://speakerweave.com/v1/events \
-H 'x-access-token: dais_your_api_token'The stable integration API covers events, submissions/sessions, speakers/contacts, schedules, tracks, formats, rooms, content status, and evaluation summaries. The application itself is also RESTful, and its complete generated OpenAPI explorer is available at the API service's /docs.
The Python 3.11+ companion CLI exposes conference operations as the sw command. Install it from a checkout, authenticate with an organization API token, and start with the event or submission views:
pipx install ./cli
sw auth login
sw events list
sw submissions list --status pendingIt supports submission decisions, speaker CSV import, scheduling, content reminders, AI triage, one-shot assistant questions, and an interactive assistant REPL. See cli/README.md for the full command reference, configuration precedence, JSON output, and its separate test suite.
API tokens also authenticate the hosted Streamable HTTP MCP endpoint. Put this in the MCP JSON configuration used by Claude Code/Desktop or another client that supports remote HTTP servers and custom headers:
{
"mcpServers": {
"speakerweave": {
"type": "http",
"url": "https://speakerweave.com/mcp",
"headers": {
"Authorization": "Bearer dais_your_api_token"
}
}
}
}For claude.ai, Claude for Work, or ChatGPT connector UIs, add a custom connector with only https://speakerweave.com/mcp. The client discovers /.well-known/oauth-protected-resource and /.well-known/oauth-authorization-server, dynamically registers, uses authorization code + PKCE, and opens SpeakerWeave's approval page. Paste an API token from Settings there; SpeakerWeave exchanges it for short-lived OAuth access and rotating refresh tokens without storing or forwarding the raw API token. PUBLIC_APP_URL must be the externally visible origin for this flow.
An Every-style agent panel lives on the right side of every organizer page: threads, streaming answers, @-mention any submission/speaker/session as context, clickable entity badges that route through the app, agent-driven navigation ("show me the unstaffed sessions"), and inline Approve/Deny confirmation before anything sensitive (emails, decisions, publishing, deletes) happens.
It is off until you add a key — set OPENAI_API_KEY (default runtime: OpenAI Agents SDK, gpt-5.6-luna at xhigh reasoning effort) or ANTHROPIC_API_KEY, and the same harness runs on either provider. No key, no UI, no agent-backed Slack answers. The shared runtime lives in api/agent/; in-app Ask lives in web/src/agent/, while Slack enters through the service boundary. Connect Every — or any MCP server your org runs — to give the agent external tools. Full details, including what to adjust when running it on Claude instead: docs/chat-agent.md.
The Slack bot is the same agent as in-app Ask: the same provider, built-in tools, connected MCP servers, persisted threads, and permission gate. Mentions and DMs can run full agent turns; sensitive actions arrive as native Approve and Deny buttons with a 300-second window. Slack conversations are mapped to agent_threads, so they appear in Ask history.
The app registers as a Slack Agent: the AGENT badge, the split-pane chat surface with suggested prompts, a native "is thinking…" status while a turn runs, and New chat starting a fresh agent conversation.
Create a Slack app from api/slack_manifest.json (for self-hosting, swap both https://speakerweave.com request URLs for your public origin first). The manifest configures the scopes (including assistant:write for the agent surface and users:read for speaker attribution), the bot events (app_mention, assistant_thread_started, message.im), Interactivity on the same signed URL, the agent view, and an unlocked messages tab; Socket Mode stays off. An app icon ships at assets/slack-app-icon-1024.png.
Install the app, invite it to channels where it should answer mentions, then configure:
SLACK_SIGNING_SECRETSLACK_BOT_TOKENSLACK_DEFAULT_ORGOPENAI_API_KEYorANTHROPIC_API_KEYfor the selected agent provider
Channel replies remain in their Slack thread. A top-level DM resumes the most recent mapped agent thread for that DM channel. The current implementation binds one deployment to one Slack workspace and SLACK_DEFAULT_ORG; add a workspace-to-organization installation table before serving multiple Slack workspaces from one deployment. See the complete Slack agent guide.
Configure Airtable per organization in Settings → Integrations. The personal access token needs access to the target base and these scopes:
data.records:readdata.records:writeschema.bases:readschema.bases:writeif SpeakerWeave should create theSpeakersandSubmissionstables
The sync upserts speakers by email and submissions by friendly ID. AIRTABLE_API_KEY and AIRTABLE_BASE_ID are only an environment fallback for org_dev; production organizations store masked, server-side configuration in org_integrations.
Settings generates script and iframe snippets for /e/{event_slug}/schedule and /e/{event_slug}/speakers, with track, accent-color, and compact-layout options. The script loader at /public/program/{event_slug}/embed.js auto-resizes its iframe. The same public data is available as JSON under /public/program/{event_slug}/{schedule|speakers}, and the complete accepted, scheduled program is available at /public/program/{event_slug}/calendar.ics.
Clone https://github.com/Brandonmchu/speakerweave and stand it up for our organization.
Use the following instead of the reference choices:
- Hosting: [leave blank] # e.g. Fly.io, Render, AWS, bare VM — needs a Python container + static SPA + Postgres reachability
- Database: [leave blank] # any Postgres; run migrations/ in order; we use Supabase's PostgREST client, so either use Supabase or swap services/supabase_client
- Auth: [leave blank] # any JWT issuer with an org_id claim (HS256, SUPABASE_JWT_SECRET); reference impl is Clerk; the dev-token flow needs nothing
- Email: [leave blank] # any provider; implement one send function in services/mailer.py; reference impl is Resend
- Domain: [leave blank]
Then: run the test suites (api: pytest; web: vitest), seed a demo workspace, and give me the admin URL and an API token.
No preferences? Tell your agent: use the reference stack exactly as documented in AGENTS.md section (e).
.
├── api/
│ ├── main.py # FastAPI assembly, middleware, routes, MCP mount, worker lifecycle
│ ├── app/core/ # settings, logging, and response security
│ ├── routes/ # organizer, public, REST v1, OAuth, Slack, portal, review routes
│ ├── services/ # domain services and external-provider boundaries
│ ├── migrations/ # ordered PostgreSQL schema/data migrations, NNN_description.sql
│ ├── scripts/seed_demo.py # deterministic, resettable demo workspace
│ ├── mcp_server.py # hosted MCP tools/resources and auth boundary
│ ├── slack_manifest.json # importable Slack app manifest
│ └── tests/ # pytest suite
├── cli/
│ ├── src/speakerweave_cli/ # Python 3.11+ `sw` command package
│ └── tests/ # isolated CLI pytest suite with mocked HTTP
├── web/
│ ├── src/ # React application, pages, API clients, and shared logic
│ ├── tests/ # Vitest + Testing Library suite
│ ├── nginx/default.conf # SPA serving, reverse proxy, caching, and embed headers
│ ├── worker/ + wrangler.jsonc # Cloudflare Workers edge web tier
│ ├── Dockerfile # Vite build and nginx runtime
│ └── package.json # web scripts and dependencies
├── docs/ # in-repo deep dives (chat agent) and README screenshots
├── docs-site/ # Mintlify documentation site (speaker-weave.mintlify.site)
└── assets/ # brand assets (Slack app icon)
# API
cd api
venv/bin/python -m pytest -q
venv/bin/ruff check .
# Web
cd ../web
npx tsc --noEmit
npm run build
npm test -- --runThe form-rule and schedule-conflict engines have matching Python and TypeScript fixtures so browser behavior and server validation are exercised against the same cases.
SpeakerWeave is available under the MIT License.
Built for the Kill My SaaS challenge through an agentic build process: human product direction paired with AI agents implementing, reviewing, and testing the system.