Files
hope-events/backend/API_DOCUMENTATION.md
T
joshua 3d381944d2 Initial commit
Next.js + Express event management app for Hope Family Church.
2026-07-23 15:26:47 +02:00

19 KiB

Hope Events — API Documentation

Full interactive docs (with request/response examples) are available at GET /docs?token=<admin-jwt> on the running backend.

All authenticated requests require:

Authorization: Bearer <jwt>

Tokens are issued by POST /api/users/login.


Table of Contents


Users

Method Path Auth Description
POST /api/users public Register new account
POST /api/users/login public Login, returns JWT
POST /api/users/forgot public Request password reset email
POST /api/users/reset public Reset password with token
POST /api/users/activate public Activate account from email link
GET /api/users/profile user+ Own profile
PUT /api/users/profile user+ Update own profile
POST /api/users/revoke-sessions user+ Revoke own sessions
POST /api/users/close-account user+ Deactivate own account
GET /api/users supervisor+ List users — supports ?search=, ?role=, ?isActive=true|false, ?page=, ?limit=
GET /api/users/:id admin Get user by ID
PUT /api/users/:id admin Update user (name, email, role, isActive, phoneNumber)
DELETE /api/users/:id admin Deactivate user (isActive: false)
POST /api/users/:id/revoke-sessions admin Revoke all sessions for a user
POST /api/users/:id/anonymize admin Erase personal data — sets name to "Deleted User", clears email/phone, deactivates account

POST /api/users

{
  "name": "Jane Smith",
  "email": "jane@example.com",
  "password": "securepass123",
  "phoneNumber": "+27821234567"
}

Returns: { _id, name, email, role, token }

POST /api/users/login

{ "email": "jane@example.com", "password": "securepass123" }

Returns: { _id, name, email, role, token }

PUT /api/users/profile

{ "name": "Jane Doe", "phoneNumber": "+27829999999", "notificationPreference": "email" }

notificationPreference: "email" | "whatsapp" | "both"


Events

Method Path Auth Description
GET /api/events public Active, public, upcoming events. Includes isSoldOut: boolean per event.
GET /api/events/all staff+ All events including hidden/past (?includePast=true, ?includeInactive=true)
GET /api/events/admin/all admin Admin view of all events
GET /api/events/:id public Full event detail — options, variants, earlyBirdTiers, attachments, form, availableCount per option/variant
GET /api/events/by-alias/:redirectUrl public Get event by URL alias
POST /api/events supervisor+ Create event
PUT /api/events/:id supervisor+ Update event
DELETE /api/events/:id admin Delete event
POST /api/events/:id/email-attendees supervisor+ Bulk email attendees (dryRun: true to preview)
POST /api/events/:id/email-attendees/schedule supervisor+ Schedule bulk email
POST /api/events/:id/whatsapp-attendees supervisor+ Bulk WhatsApp to attendees (dryRun: true to preview)
POST /api/events/:id/whatsapp-attendees/schedule supervisor+ Schedule bulk WhatsApp
GET /api/events/attachments/status admin Attachment manifest sync status
POST /api/events/attachments/sync admin Re-sync attachment manifests to DB

isSoldOut logic (GET /api/events)

isSoldOut is true when every option on the event that has stockLimit > 0 has sold out its full allocation (computed from non-cancelled RegistrationOption quantities). Events with no limited options are never marked sold out.

POST /api/events

{
  "title": "Family Camp 2025",
  "description": "Annual family camp at the farm.",
  "startDate": "2025-07-10T08:00:00.000Z",
  "endDate": "2025-07-14T17:00:00.000Z",
  "registrationDeadline": "2025-07-01T00:00:00.000Z",
  "goLiveAt": "2025-05-01T00:00:00.000Z",
  "price": 450,
  "picture": "/uploads/events/camp.jpg",
  "redirectUrl": "camp2025",
  "isHidden": false,
  "requiresAuth": true,
  "form": {
    "isRequired": true,
    "fields": [
      { "type": "text", "label": "Dietary requirements", "isRequired": false, "order": 0 }
    ]
  }
}

Event Options & Variants

Method Path Auth Description
POST /api/events/:id/options supervisor+ Add option to event
PUT /api/events/options/:id supervisor+ Update option (name, price, stockLimit, variants, earlyBirdTiers)
DELETE /api/events/options/:id admin Delete option (fails if registrations exist)
POST /api/events/options/:id/variants supervisor+ Add variant to option
PUT /api/events/variants/:id supervisor+ Update variant
DELETE /api/events/variants/:id admin Delete variant

POST /api/events/:id/options

{
  "name": "Adult",
  "price": 450,
  "isMainTicket": false,
  "stockLimit": 100
}

PUT /api/events/options/:id — with variants and early-bird tiers

{
  "name": "Adult",
  "price": 450,
  "stockLimit": 100,
  "variants": [
    { "name": "Standard", "price": null, "stockLimit": 60, "order": 0 },
    { "name": "VIP", "price": 600, "stockLimit": 40, "order": 1 }
  ],
  "earlyBirdTiers": [
    { "deadline": "2025-06-01T00:00:00.000Z", "price": 350, "stockLimit": 30, "order": 0, "variantId": null }
  ]
}

stockLimit: 0 means unlimited. Early-bird tiers can be option-level (variantId: null) or variant-level (variantId: "<uuid>").


Event Attachments

Method Path Auth Description
GET /api/events/:id/attachments public List attachments for event
POST /api/events/:id/attachments supervisor+ Upload attachment (multipart/form-data, field file)
DELETE /api/events/:eventId/attachments/:id supervisor+ Delete attachment

Accepted file types: .pdf, .doc, .docx, .xls, .xlsx, .ppt, .pptx, .txt, .csv, .zip. Max 10 MB.


Registrations

Method Path Auth Description
POST /api/registrations optional Create registration
GET /api/registrations/myregistrations user+ Own registrations
GET /api/registrations/:id optional Get registration by ID
PUT /api/registrations/:id/options user+ Update selected options
DELETE /api/registrations/:id user+ Cancel registration (blocked if positive payments exist unless admin)
GET /api/registrations/:id/forms/draft optional Get form draft
PUT /api/registrations/:id/forms/draft optional Save form draft
POST /api/registrations/:id/forms/responses optional Submit form responses
PUT /api/registrations/:id/forms/responses supervisor+ Replace form responses
GET /api/registrations staff+ All registrations
PUT /api/registrations/:id staff+ Update registration status
GET /api/registrations/event/:eventId staff+ Registrations for an event
POST /api/registrations/manual supervisor+ Manual registration

POST /api/registrations

{
  "eventId": "ev-uuid-...",
  "options": [
    { "eventOptionId": "opt-uuid-...", "quantity": 2 },
    { "eventOptionId": "opt-uuid-...", "quantity": 1, "variantId": "variant-uuid-..." }
  ]
}

For unauthenticated (guest) registration on events with requiresAuth: false:

{
  "eventId": "ev-uuid-...",
  "options": [...],
  "guestName": "Jane Smith",
  "guestEmail": "jane@example.com",
  "guestPhone": "+27821234567"
}

Cancellation rules

Actor Condition Result
Owner No positive payments 200 — status set to cancelled
Owner Any positive payment exists 403 — must contact organisation
Admin Any state 200 — always allowed
Anyone Already cancelled 400

Payments

Method Path Auth Description
POST /api/payments supervisor+ Record manual payment (cash/EFT)
POST /api/payments/yoco-checkout user+ Initiate Yoco checkout; re-evaluates early-bird pricing first — returns { priceUpdated: true, newTotal } if prices changed
GET /api/payments/mypayments user+ Own payments
GET /api/payments/:id user+ Payment by ID
GET /api/payments/registration/:registrationId user+ Payments for a registration
GET /api/payments supervisor+ All payments
GET /api/payments/event/:eventId staff+ Payments for an event
PUT /api/payments/assign-donation supervisor+ Assign a donation payment to a registration
POST /api/payments/refund supervisor+ Create refund record
GET /api/payments/admin/stats admin Payment statistics

POST /api/payments (manual)

{
  "registrationId": "reg-uuid-...",
  "amount": 450,
  "method": "cash",
  "note": "Paid at the door"
}

POST /api/payments/yoco-checkout

{
  "registrationId": "reg-uuid-...",
  "successUrl": "https://events.yourchurch.org/payment/success",
  "cancelUrl": "https://events.yourchurch.org/payment/cancel",
  "failureUrl": "https://events.yourchurch.org/payment/failure"
}

Returns: { redirectUrl } or { priceUpdated: true, newTotal, message } if early-bird prices changed.


Tickets

Method Path Auth Description
GET /api/tickets/mytickets user+ Own tickets (excludes cancelled registrations)
GET /api/tickets/:id user+ Ticket by ID
POST /api/tickets/email user+ Email own tickets as PDF
POST /api/tickets/send-to staff+ Send tickets to a specific user
GET /api/tickets/scan-preview/:qrCode staff+ Preview scan (no mark)
GET /api/tickets/qr/:qrCode staff+ Ticket by QR code
POST /api/tickets/scan/:qrCode staff+ Scan/redeem ticket
GET /api/tickets/event/:eventId staff+ All tickets for an event
GET /api/tickets/scans/recent staff+ Recent scan activity
GET /api/tickets/scans/stats staff+ Scan statistics
POST /api/tickets/generate supervisor+ Generate tickets for a registration
GET /api/tickets supervisor+ All tickets
PUT /api/tickets/email-sent admin Bulk mark tickets as emailed

Broadcasts (Email)

Method Path Auth Description
POST /api/broadcasts/preview supervisor+ Dry-run — returns recipient count and sample
POST /api/broadcasts/send supervisor+ Send email broadcast immediately
POST /api/broadcasts/schedule supervisor+ Schedule email broadcast

POST /api/broadcasts/send

{
  "subject": "Camp 2025 — Final reminder",
  "body": "Hi {{name}}, just a reminder that camp starts tomorrow at 08:00.",
  "userIds": ["user-uuid-1", "user-uuid-2"],
  "extraEmails": ["extra@example.com"]
}

Dynamic placeholders: {{name}}, {{event.title}}, {{event.start}}, {{event.link}}, {{balance}}, {{promo.title}}, {{promo.link}}


WhatsApp Broadcasts

Method Path Auth Description
POST /api/whatsapp-broadcasts/preview supervisor+ Dry-run
POST /api/whatsapp-broadcasts/send supervisor+ Send WhatsApp broadcast immediately
POST /api/whatsapp-broadcasts/schedule supervisor+ Schedule WhatsApp broadcast

Scheduled Messages

Method Path Auth Description
GET /api/scheduled-emails supervisor+ List all queued/scheduled jobs
PATCH /api/scheduled-emails/:id supervisor+ Edit scheduled job (reschedule, update content)
DELETE /api/scheduled-emails/:id supervisor+ Cancel a queued job

Automations

Method Path Auth Description
POST /api/automations/schedule supervisor+ Schedule event lifecycle automations (pre-event reminder, thank-you, promo, etc.)

Reports

Method Path Auth Description
POST /api/reports/pdf user+ Generate and download PDF attendance/payment report
POST /api/reports/email user+ Email PDF report to current user

Uploads

Method Path Auth Description
POST /api/uploads/event-image supervisor+ Upload event cover image (multipart/form-data, field image). Returns { url }. Max 5 MB. Formats: jpg, jpeg, png, webp, gif.
POST /api/uploads/logo admin (or unauthenticated during setup) Upload site logo (multipart/form-data, field image). Returns { url }. Max 2 MB. Formats: jpg, jpeg, png, webp, svg.

Sections

Groups of event options shown as labelled sections on the registration page (e.g. "Tickets", "Add-ons", "Accommodation").

Method Path Auth Description
GET /api/sections staff+ List sections
POST /api/sections supervisor+ Create section
PUT /api/sections/:id supervisor+ Update section
DELETE /api/sections/:id admin Delete section

POST /api/sections

{
  "eventId": "ev-uuid-...",
  "name": "Accommodation",
  "allowedOptionIds": ["opt-uuid-1", "opt-uuid-2"]
}

Banner

Method Path Auth Description
GET /api/banner public Get current site-wide banner (text, colour, active flag)
POST /api/banner supervisor+ Set site banner

POST /api/banner

{
  "text": "Camp registrations close Friday!",
  "color": "#f59e0b",
  "isActive": true
}

Forms

Method Path Auth Description
GET /api/forms/responses staff+ List all submitted form responses across all events; supports filtering

Yoco Transactions

Raw webhook events from Yoco. Used for reconciling payments that weren't matched automatically.

Method Path Auth Description
GET /api/yoco-transactions supervisor+ All Yoco webhook transactions
GET /api/yoco-transactions/unreconciled supervisor+ Transactions not yet matched to a payment
POST /api/yoco-transactions/:id/reconcile supervisor+ Manually link transaction to a registration
POST /api/yoco-transactions/:id/ignore supervisor+ Mark transaction as intentionally ignored

WhatsApp Admin

Manages the WAWP WhatsApp API instance. Credentials are stored in AppSetting (preferred) or .env (fallback).

Method Path Auth Description
GET /api/whatsapp/config admin Get current WAWP credentials
POST /api/whatsapp/config admin Save WAWP credentials to DB
GET /api/whatsapp/status admin WAWP session status
GET /api/whatsapp/qr admin QR code for WhatsApp device linking
POST /api/whatsapp/create-instance admin Create a new WAWP instance
POST /api/whatsapp/delete-instance admin Delete WAWP instance
POST /api/whatsapp/request-code admin Request pairing code (alternative to QR)
POST /api/whatsapp/logout admin Logout current WAWP session
POST /api/whatsapp/start admin Start WAWP instance
POST /api/whatsapp/restart admin Restart WAWP instance

Webhooks

Method Path Auth Description
POST /api/webhooks/yoco HMAC-verified Yoco payment webhook — verifies X-Yoco-Signature, creates Payment + YocoTransaction, updates registration status, generates tickets
POST /api/webhooks/whatsapp WAWP inbound event webhook

Settings

Runtime configuration stored in the AppSetting table. Sensitive values (SMTP credentials, WAWP token) are stored AES-256-GCM encrypted.

Method Path Auth Description
GET /api/settings public Public settings (org name, tagline, colour, logo, legal keys)
GET /api/settings/all admin All settings — smtp_pass masked as ••••••••, smtp_user decrypted
GET /api/settings/needs-setup public { needsSetup: bool } — true until setup wizard completes
PUT /api/settings admin Upsert settings; smtp_user/smtp_pass encrypted before storage; sending •••••••• for smtp_pass keeps existing value
POST /api/settings/test-smtp admin Test SMTP with provided credentials; sends real test email to admin on success. The admin JWT returned by POST /api/setup/register also qualifies during the setup wizard.

POST /api/settings/test-smtp

{
  "host": "smtp.office365.com",
  "port": 587,
  "secure": false,
  "user": "noreply@yourchurch.org",
  "pass": "app-password-here",
  "from": "noreply@yourchurch.org"
}

Success (200):

{ "message": "SMTP connection verified — a test email has been sent to admin@yourchurch.org" }

Failure (400):

{
  "message": "Authentication failed — check your SMTP username and password.",
  "raw": "530 5.7.57 Client not authenticated to send mail. [JN3P275CA0121...]"
}

The message field is always a human-readable sentence. The raw field is included on errors only and contains the original SMTP error for debugging. Error code 530 (Microsoft "Client not authenticated") maps to the authentication-failure message.


Setup

One-time first-run wizard. Both endpoints are blocked once any user exists and setup is complete.

Method Path Auth Description
POST /api/setup/register public (one-time) Create the first admin account; returns a short-lived setup JWT
POST /api/setup setup token or admin Save initial site settings (org name, branding, SMTP)

Static Files

Path Description
/uploads/events/* Event cover images
/uploads/branding/* Site logo
/uploads/event-files/* Event attachments (PDFs, docs, etc.)
/uploads/tickets-temp/* Temporarily hosted ticket PDFs for WhatsApp delivery (auto-deleted after 5 min)

Auth error responses

Status Body Meaning
401 { message: "Not authorised, no token" } Missing or malformed Authorization header
401 { message: "Not authorised, token failed" } Invalid or expired JWT
401 { message: "Session has been revoked" } Token version mismatch (sessions revoked)
403 { message: "Not authorised as an admin" } Insufficient role

For full interactive docs with expandable examples visit GET /docs?token=<admin-jwt> on the running backend.