Skip to main content

Group scheduling (Doodle style) vs public booking

Group scheduling (Doodle-style) vs public booking

Status: Adopted (product + engineering decisions) Last updated: April 7, 2026 Related: 03-PUBLIC-SCHEDULING.md (Calendly-style booking)

1. Chosen model (Phase 1 vs Phase 2)

Decision

| Phase | Capability | Implementation |
|-------|--------------|----------------|
| **Phase 1 (now)** | Group time poll (“which slot works for everyone?”) | **External:** [Doodle](https://doodle.com) opened from **My Calendar** toolbar (`WorkspaceCalendarManager`). No AlignMint OAuth to Doodle; staff use their own Doodle account. |
| **Phase 1 (existing)** | One-to-one / one-to-many **fixed-slot booking** | **Native:** Public scheduling (`PublicSchedulingPage`, `get-public-availability`, `book-scheduling-slot`). |
| **Phase 2 (future)** | Full **native** group poll + finalize inside AlignMint | New tables and edge functions (see §3). Optional Doodle deprecation for orgs that prefer in-app only. |

Rationale: Public booking is already native and aligned with workspace_events. Doodle’s group-poll UX and notifications are mature; linking out avoids OAuth, ToS, and API surface until product demand justifies a native build.

Privacy / compliance (Phase 1): The toolbar control is a normal https://doodle.com/ navigation in a new tab. AlignMint does not embed Doodle, proxy requests, or send user or org identifiers to Doodle; any account or data there is between the user and Doodle under their terms.

Explicit non-choice for Phase 1: No Doodle API/embed with AlignMint-held credentials.

2. Calendar integration scope (Google / internal calendar)

Read path (busy / free)

| Use case | Scope | Notes |
|----------|--------|------|
| **Public booking** | Host’s connected Google Calendar | Already described in scheduling settings: busy times excluded from bookable slots where sync applies. |
| **Future native group poll** | Host + optionally voters | Phase 2: when **proposing** poll options, suggest slots that respect host `calendar_integrations` busy data and `workspace_events`. |

Write path (create / invite)

| Milestone | Who gets a calendar artifact | Mechanism |
|-----------|------------------------------|-----------|
| **MVP (current)** | Host | `workspace_events` from booking; Google push where integration exists (existing flows). |
| **Phase 2a** | Host only on poll finalize | Single `workspace_event` + optional Google event for organizer. |
| **Phase 2b** | Invitees | Requires attendee emails and expanded Google Calendar scopes or transactional email with ICS; separate security/product review. |

Principle: AlignMint remains orchestrator: internal workspace_events is canonical; Google Calendar is a sync target for the connected user, not the only source of truth.

3. Native group poll — proposed data model (Phase 2)

> Not migrated yet. This section is the implementation blueprint when Phase 2 is approved.

Tables (PostgreSQL / Supabase)

`scheduling_polls` — one organizer poll per row.

| Column | Type | Notes |
|--------|------|--------|
| `id` | `uuid` | PK |
| `organization_id` | `uuid` | FK `organizations`, matches header entity |
| `created_by` | `uuid` | FK `auth.users` |
| `title` | `text` | |
| `description` | `text` | nullable |
| `status` | `text` | `draft` \| `open` \| `closed` \| `finalized` |
| `closes_at` | `timestamptz` | nullable; optional auto-close |
| `final_option_id` | `uuid` | nullable; FK → `scheduling_poll_options` when finalized |
| `workspace_event_id` | `uuid` | nullable; FK → `workspace_events` after finalize |
| `public_token` | `text` | unique, high-entropy; for unauthenticated voters |
| `created_at` / `updated_at` | `timestamptz` | |

`scheduling_poll_options` — candidate slots.

| Column | Type | Notes |
|--------|------|--------|
| `id` | `uuid` | PK |
| `poll_id` | `uuid` | FK `scheduling_polls` ON DELETE CASCADE |
| `start_at` | `timestamptz` | |
| `end_at` | `timestamptz` | |
| `sort_order` | `int` | default 0 |

`scheduling_poll_votes` — one row per voter per option (or use JSONB for compact storage; normalized is easier for tallies).

| Column | Type | Notes |
|--------|------|--------|
| `id` | `uuid` | PK |
| `poll_id` | `uuid` | FK |
| `option_id` | `uuid` | FK `scheduling_poll_options` |
| `voter_email` | `text` | required for public votes; normalize lowercase |
| `voter_name` | `text` | optional |
| `response` | `text` | `yes` \| `no` \| `maybe` |
| `created_at` | `timestamptz` | |

Unique constraint: (poll_id, option_id, voter_email) to prevent double votes per slot.

RLS (sketch)

  • `scheduling_polls`: SELECT/UPDATE for created_by = auth.uid() or org admins; INSERT for authenticated org members where policy matches workspace_events-style org scoping.
  • Public `SELECT`/`INSERT` votes: via edge function using service role + validation on public_token, not direct anon RLS on votes (simpler and safer).

Finalize flow

1. Organizer selects winning scheduling_poll_options.id (or system picks by “most yes”). 2. Edge function finalize-scheduling-poll: verify organizer, set final_option_id, status = finalized, create `workspace_events` row (times from option, organization_id, created_by). 3. Optionally enqueue Google Calendar create for host (reuse patterns from event builder / booking). 4. Set workspace_event_id on poll for traceability.

Edge functions (Phase 2)

| Function | Purpose |
|----------|---------|
| `get-public-poll` | Load poll + options by `public_token` (no auth) |
| `submit-poll-vote` | Validate token, upsert vote |
| `finalize-scheduling-poll` | Auth organizer; create `workspace_event`; update poll |

4. UI reference (Phase 1)

  • My Calendar toolbar: external link button “Group poll (Doodle)”https://doodle.com/ (new tab). Copy instructs users to add the agreed time back via New Event or booking flows.

Related code


Synced from IFMmvp-Frontend documentation: workspace/04-GROUP-SCHEDULING-DOODLE.md

Ready to Get Started?

See how Alignmint can simplify your nonprofit's operations. Schedule a free demo with our team and we'll walk you through everything.

Questions? Email us at steven@getalignmint.org

Ready to get started?Start Plus Trial