Skip to content

API Reference

This document provides a comprehensive reference for all REST API endpoints and WebSocket events.

Base URL

  • Development: http://localhost:3000
  • Production: Configured via VITE_API_URL

Authentication

All protected endpoints require a JWT token in the Authorization header:

Authorization: Bearer <token>

Authentication Endpoints

Login (Admin)

POST /auth/login
Content-Type: application/json

{
  "email": "admin@example.com",
  "password": "secret"
}

Response:

{
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "user": {
        "id": "usr_123",
        "email": "admin@example.com",
        "role": "ADMIN"
    }
}

Login (Staff)

POST /auth/staff/login
Content-Type: application/json

{
  "code": "1234",
  "eventId": "evt_123"
}

Response:

{
    "accessToken": "eyJhbGciOiJIUzI1NiIs...",
    "user": {
        "id": "staff_123",
        "name": "John Volunteer",
        "role": "STAFF",
        "eventId": "evt_abc"
    }
}

Google OAuth

GET /auth/google

Initiates OAuth 2.0 flow. Callback handled at /auth/google/callback.


Events Endpoints

List Events (Admin/Staff)

GET /events
Authorization: Bearer <token>

Response:

[
    {
        "id": "evt_123",
        "slug": "winter-gala-2025",
        "name": "Winter Gala 2025",
        "goalAmount": 50000,
        "status": "active",
        "createdAt": "2025-01-01T00:00:00Z"
    }
]

List Public Events

GET /events/public

Returns only active events with aggregated stats.

Response:

[
    {
        "slug": "winter-gala-2025",
        "name": "Winter Gala 2025",
        "goalAmount": 50000,
        "totalRaised": 25000,
        "donorCount": 150
    }
]

Get Event by Slug

GET /events/:slug

Response:

{
    "id": "evt_123",
    "slug": "winter-gala-2025",
    "name": "Winter Gala 2025",
    "date": "2025-12-15T19:00:00Z",
    "description": "Annual charity gala",
    "goalAmount": 50000,
    "status": "active"
}

Create Event

POST /events
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "Summer Fundraiser",
  "slug": "summer-2025",
  "goalAmount": 25000
}

Update Event

PATCH /events/:id
Authorization: Bearer <token>
Content-Type: application/json

{
  "goalAmount": 30000,
  "status": "active"
}

Delete Event

DELETE /events/:id
Authorization: Bearer <token>

Get Event Staff

GET /events/:id/staff
Authorization: Bearer <token>

Returns list of staff members assigned to the event.

Assign Staff to Event

POST /events/:id/staff/:staffId
Authorization: Bearer <token>

Unassign Staff from Event

DELETE /events/:id/staff/:staffId
Authorization: Bearer <token>

Donation Endpoints

List Donations

GET /donations?eventId=evt_123&status=COMPLETED&limit=50&offset=0
Authorization: Bearer <token>

Query Parameters: | Parameter | Type | Description | |:---|:---|:---| | eventId | string | Filter by event | | status | string | Filter by status (PENDING, COMPLETED, FAILED, CANCELLED, REFUNDED) | | search | string | Search by donor name or email | | limit | number | Number of results (default: 50) | | offset | number | Pagination offset (default: 0) |

Export Donations CSV

GET /donations/export?eventId=evt_123
Authorization: Bearer <token>

Returns a CSV file with donation data.

Create Payment Intent

POST /donations/intent
Content-Type: application/json

{
  "amount": 5000,
  "currency": "eur",
  "metadata": {
    "eventId": "evt_123",
    "donorName": "John Doe",
    "donorEmail": "john@example.com"
  }
}

Response:

{
    "clientSecret": "pi_xxx_secret_yyy"
}

Stripe Webhook

POST /donations/stripe/webhook
Stripe-Signature: <signature>

Handled internally. Validates payment_intent.succeeded events.

PayPal Webhook

POST /donations/paypal/webhook

Handles CHECKOUT.ORDER.COMPLETED events.

Record Offline Donation (Staff)

POST /donations
Authorization: Bearer <token>
Content-Type: application/json

{
  "amount": 5000,
  "eventId": "evt_123",
  "type": "cash",
  "donorName": "John Doe",
  "donorEmail": "john@example.com"
}

Update Donation

PATCH /donations/:id
Authorization: Bearer <token>
Content-Type: application/json

{
  "donorName": "Jane Doe",
  "donorEmail": "jane@example.com",
  "isAnonymous": false,
  "message": "Updated message"
}

Cancel Donation

POST /donations/:id/cancel
Authorization: Bearer <token>
Content-Type: application/json

{
  "shouldRefund": true
}

Staff Endpoints

List Staff Members

GET /staff
Authorization: Bearer <token>

Response:

[
    {
        "id": "staff_123",
        "code": "1234",
        "name": "John Volunteer",
        "createdAt": "2025-01-01T00:00:00Z"
    }
]

Get Staff Member

GET /staff/:id
Authorization: Bearer <token>

Create Staff Member

POST /staff
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "Jane Volunteer",
  "code": "5678"
}

Update Staff Member

PUT /staff/:id
Authorization: Bearer <token>
Content-Type: application/json

{
  "name": "Jane Updated"
}

Delete Staff Member

DELETE /staff/:id
Authorization: Bearer <token>

Export Endpoints

Export Receipts (ZIP)

GET /export/receipts/zip?eventId=evt_123
Authorization: Bearer <token>

Returns a ZIP file containing PDF receipts for all completed donations.

Get Single Receipt

GET /export/receipts/:id
Authorization: Bearer <token>

Returns a PDF file for a single donation receipt.


Settings Endpoints

Get Global Settings

GET /settings/global

Returns the global configuration (organization, theme, locales).

Update Global Settings

PATCH /settings/global
Authorization: Bearer <token>
Content-Type: application/json

{
  "organization": "My Foundation",
  "payment": {
    "currency": "EUR"
  }
}

Get Event Settings

GET /events/:slug/settings

Returns merged configuration for a specific event (global + event overrides).

Update Event Branding

PATCH /events/:id/branding
Authorization: Bearer <token>
Content-Type: application/json

{
  "assets": {
    "logo": "https://example.com/logo.png"
  },
  "themeVariables": {
    "--primary": "#ff5500"
  }
}

Reset Event Branding

DELETE /events/:id/branding
Authorization: Bearer <token>

Removes all event-specific overrides, reverting to global settings.


Health Check

Check API Health

GET /health

Response:

{
    "status": "ok",
    "info": {
        "database": { "status": "up" },
        "memory_heap": { "status": "up" },
        "redis": { "status": "up" }
    }
}

WebSocket Events

The WebSocket server uses Socket.IO and runs on the same port as the API.

Connection

import { io } from 'socket.io-client'

const socket = io('http://localhost:3000', {
    withCredentials: true,
})

Client → Server Events

Event Payload Description
joinEvent eventId: string Subscribe to event room
socket.emit('joinEvent', 'evt_123')

Server → Client Events

Event Payload Description
donation.created DonationEventPayload New donation received
interface DonationEventPayload {
    id: string
    eventId: string
    amount: number // in cents
    donorName: string // "Anonymous" if hidden
    message?: string
    createdAt: string
}
socket.on('donation.created', (donation) => {
    console.log(`${donation.donorName} donated ${donation.amount / 100}€`)
})

Error Responses

All endpoints return consistent error responses:

{
    "statusCode": 400,
    "message": "Validation failed",
    "error": "Bad Request"
}
Status Code Description
400 Bad Request - Invalid input
401 Unauthorized - Missing/invalid token
403 Forbidden - Insufficient permissions
404 Not Found - Resource doesn't exist
429 Too Many Requests - Rate limited
500 Internal Server Error

Rate Limiting

The API implements rate limiting to prevent abuse:

  • Default: 100 requests per minute per IP
  • Auth endpoints: 10 requests per minute per IP
  • Webhooks: Unlimited (verified by signature)

Exceeded limits return 429 Too Many Requests.