Skip to main content

WhatsApp Change Request Workflow

This spec defines the future workflow for event changes requested through WhatsApp after an event candidate or listing already exists. It is intentionally design-only: it does not enable production writes, public publishing, social posting, or live calendar connector updates.

Goals

  • Let submitters request corrections, updates, cancellations, or removal review from WhatsApp.
  • Keep Supabase and Mobilis Core as the source of truth.
  • Create an auditable change request before any canonical data changes.
  • Require owner approval where authority is unclear or the change is high impact.
  • Require moderator approval before changes affect public listings or connector update jobs.
  • Make each step idempotent so retries do not duplicate requests, audits, or connector work.

Non-Goals

  • No direct mutation of public listings from WhatsApp message text.
  • No live WordPress, The Events Calendar, EventON, social, or public API writes.
  • No public API or RLS policy grants.
  • No free-text moderator command parser in the first implementation.
  • No automatic approval for trusted submitters until a later explicit issue defines that policy.

Request Types

TypeExamplesDefault Risk
Correctiontypo, price, organizer link, flyer replacementnormal
Schedule updatedate, start time, end time, recurrence notehigh
Venue updatevenue name, address, cityhigh
Cancellationone occurrence canceled, whole event canceledhigh
Removal requestevent should not be listed, duplicate, wrong organizerhigh

High-risk changes always require moderator approval and normally require owner approval unless the requester is already linked to the event owner or original submitter with sufficient trust.

Source Of Truth Boundary

Supabase and Mobilis Core remain the canonical source of truth. WhatsApp is only an intake channel, and external calendars are only sync targets.

The workflow must block these shortcuts:

  • WhatsApp text directly mutating canonical event, listing, or occurrence records
  • WhatsApp text directly creating connector payloads
  • connector inbound state overwriting canonical Mobilis Core state without a staged review record
  • moderator free text acting as approval without an authenticated system action

Approved changes should be applied from a stored, reviewed diff against canonical state. Connector jobs should be built from the approved canonical after-state, not from the original WhatsApp message.

Data Model Proposal

Future implementation should add a dedicated change request table rather than overloading gl_event_candidates or gl_moderation_cases.

Suggested table: gl_event_change_requests

Required fields:

  • id
  • tenant_id
  • request_thread_id
  • request_message_id
  • submitter_id
  • target_candidate_id
  • target_listing_id, nullable until canonical listings exist
  • target_occurrence_ref, nullable for whole-listing changes
  • request_type
  • status
  • risk_level
  • owner_approval_required
  • owner_approval_status
  • moderator_decision
  • before_snapshot_json
  • proposed_diff_json
  • after_preview_json
  • idempotency_key
  • metadata_json
  • created_at
  • updated_at

Recommended statuses:

StatusMeaning
receivedWhatsApp message was classified as a change request.
needs_target_matchThe target event or occurrence is ambiguous.
needs_submitter_infoSubmitter identity or authority is incomplete.
needs_owner_approvalEvent owner approval is required before moderator decision.
needs_moderator_reviewReady for moderator review with before/after diff.
approved_pending_applyModerator approved; canonical update has not been applied.
applied_to_source_of_truthSupabase/Mobilis Core state was updated.
connector_update_readyConnector update package or dry-run job can be created.
rejectedModerator or owner rejected the change.
archivedClosed without action, usually duplicate or stale.

Evidence should continue to use gl_submission_evidence where possible, with change-request linkage in metadata_json or a small join table in a later schema issue. Audit rows should use gl_audit_log for classification, matching, owner decision, moderator decision, canonical apply, and connector handoff.

Workflow

The first implementation should store the change request and moderation case before applying anything. Applying the approved diff should be a separate service function so it can validate state, permissions, and idempotency at the final gate.

Submitter Identification

The system should identify the requester through the normalized WhatsApp submitter model before deciding whether owner approval is required.

Signals to store and compare:

  • normalized phone number and redacted phone hash
  • Twilio From, WaId, ProfileName, and provider message ids
  • existing submitter profile linked to the phone number
  • prior candidate or listing submissions from the same submitter
  • explicit event or review links sent back to the submitter earlier in the thread
  • organizer, venue, or owner records already linked to the submitter

Identification is not authorization. A matching phone number can help route the request and reduce ambiguity, but public changes still require moderator approval. When the requester is not clearly the original submitter, verified organizer, or current owner, set owner_approval_required=true.

Target Matching

Match in this order:

  1. Explicit event or review link in the WhatsApp message.
  2. Existing originating_submission_thread_id, originating_submission_message_id, or whatsapp_submitter_id on a GLD candidate.
  3. Same submitter plus event title/date/venue similarity.
  4. Source URL, ticket URL, flyer URL, or external listing reference.
  5. Moderator-selected match when confidence is low.

If multiple plausible matches exist, set status=needs_target_match and create a moderator follow-up rather than guessing.

Diff Rules

proposed_diff_json should use stable field paths and preserve the old value, proposed value, source evidence, confidence, and risk flag for each change.

Example shape:

{
"fields": [
{
"path": "event_date",
"before": "2026-06-20",
"after": "2026-06-21",
"evidence_id": "uuid",
"confidence": 0.86,
"risk": "high"
}
],
"scope": "single_occurrence",
"summary": "Date changed from June 20 to June 21."
}

Diff generation must not overwrite unknown fields with blanks. Missing or contradictory evidence should produce a review flag, not a destructive patch.

Approval Gates

Owner approval is required when:

  • the requester is not the original submitter, known organizer, or verified owner
  • the change affects date, time, venue, title, cancellation, removal, recurrence scope, or external connector identity
  • the request conflicts with existing source evidence
  • the event has already been published or submitted to a connector

Moderator approval is always required before:

  • updating canonical Supabase/Mobilis Core event state
  • changing an approved candidate
  • creating connector update jobs
  • notifying a submitter that the public listing has changed

Moderator approval should happen through a secure review surface or signed action link that resolves through authenticated moderator permissions and writes audit rows.

Connector Update Seam

After the canonical update is applied, the connector layer may create update jobs with operation=update. Until a later issue explicitly verifies direct write capability, those jobs must remain dry-run only.

Connector update jobs should receive:

  • the approved canonical after-state
  • the change request id
  • the moderation decision id or audit id
  • an idempotency key based on connector, target listing, change request id, operation, and mode
  • metadata proving live_writes_enabled=false

Do not send WhatsApp text directly to connector payload builders. Connector payloads should be built from canonical state after approval.

Idempotency And Audit

Use separate idempotency keys for:

  • inbound provider message ingestion
  • change request creation
  • owner approval recording
  • moderator decision recording
  • canonical apply
  • connector dry-run/update job creation

Every state transition should append gl_audit_log with:

  • actor type and id
  • change request id
  • target candidate/listing/occurrence
  • old status and new status
  • reason
  • evidence ids
  • whether external writes were blocked or dry-run only

Suggested Implementation Issues

Future work can be split into small PRs:

  1. Add gl_event_change_requests schema and audit helpers.
  2. Add WhatsApp change-request intent classification.
  3. Add target matching and ambiguity flags.
  4. Add before/after diff builder and tests.
  5. Add owner approval state and outbound prompts.
  6. Add moderator review/decision service for change requests.
  7. Add canonical apply service behind approval gates.
  8. Add connector update dry-run handoff and hosted-dev smoke coverage.

Each issue should preserve the rule that WhatsApp requests create auditable proposals first; approved system state changes happen only through moderated service workflows.