Appearance
Events API
API endpoints for creating and managing events within organizations.
Overview
The Events API allows authenticated users to create, update, and retrieve events within their organizations. All endpoints require authentication and appropriate organization membership.
Authentication
All event endpoints require session-based authentication. Include the session cookie received from successful login:
Cookie: session=<session-cookie>Base URL
/organizations/:organizationId/eventsAll event operations are scoped to a specific organization.
Endpoints
Create Event
Creates a new event in the specified organization.
http
POST /organizations/:organizationId/events
Content-Type: application/jsonPath Parameters
| Parameter | Type | Description |
|---|---|---|
organizationId | string (UUID) | The organization ID where the event will be created |
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Event name (1-255 characters) |
description | string | No | Event description (max 5000 characters) |
targetDate | string | No | ISO 8601 date string for the event |
status | string | No | Event status: BACKLOG, PLANNING, PLANNED, IN_PROGRESS, COMPLETED, CANCELLED (default: BACKLOG) |
eventType | string | No | ONE_OFF or RECURRING (default: ONE_OFF) |
metadata | object | No | Event metadata (see Metadata Schema) |
address | object | No | Event address (see Address Schema) |
Example Request
json
{
"name": "Annual Gala",
"description": "Corporate fundraising event",
"targetDate": "2024-12-31T00:00:00.000Z",
"metadata": {
"category": "other",
"customCategory": "Corporate",
"guestCount": {
"approximate": 500
},
"budgetRange": {
"min": 10000,
"max": 50000,
"currency": "CAD"
}
},
"address": {
"streetAddress": "123 Main St",
"city": "Toronto",
"state": "ON",
"postalCode": "M5V 3A8",
"country": "CA"
}
}Success Response
Status: 201 Created
json
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Annual Gala",
"description": "Corporate fundraising event",
"organizationId": "650e8400-e29b-41d4-a716-446655440000"
}
}Error Responses
400 Bad Request - Validation error
json
{
"errors": [
{
"field": "name",
"message": "The name field is required",
"rule": "required"
}
]
}403 Forbidden - Insufficient permissions
json
{
"errors": [
{
"message": "You do not have permission to create events in this organization"
}
]
}500 Internal Server Error - Server error
json
{
"errors": [
{
"message": "Failed to create event"
}
]
}Update Event
Updates an existing event with partial data. Only provided fields will be updated.
http
PATCH /organizations/:organizationId/events/:id
Content-Type: application/jsonPath Parameters
| Parameter | Type | Description |
|---|---|---|
organizationId | string (UUID) | The organization ID that owns the event |
id | string (UUID) | The event ID to update |
Request Body
All fields are optional. Only include fields you want to update.
| Field | Type | Description |
|---|---|---|
name | string | Event name (1-255 characters) |
description | string | Event description (max 5000 characters) |
targetDate | string | ISO 8601 date string |
status | string | Event status: BACKLOG, PLANNING, PLANNED, IN_PROGRESS, COMPLETED, CANCELLED |
metadata | object | Event metadata (deep merged with existing) |
address | object | Event address (creates or updates) |
Metadata Deep Merging
When updating metadata, new fields are merged with existing metadata without overwriting unspecified fields:
json
// Existing metadata
{
"category": "wedding",
"guestCount": { "approximate": 500 },
"budgetRange": { "min": 10000, "max": 50000 }
}
// Update request
{
"metadata": {
"guestCount": { "approximate": 600 }
}
}
// Resulting metadata (category and budgetRange preserved)
{
"category": "wedding",
"guestCount": { "approximate": 600 },
"budgetRange": { "min": 10000, "max": 50000 }
}To remove a metadata field, set it to null:
json
{
"metadata": {
"budgetRange": null
}
}Example Request
json
{
"name": "Updated Event Name",
"metadata": {
"guestCount": {
"approximate": 600
}
}
}Success Response
Status: 200 OK
json
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Updated Event Name",
"description": "Corporate fundraising event",
"organizationId": "650e8400-e29b-41d4-a716-446655440000"
}
}Error Responses
400 Bad Request - Validation error 403 Forbidden - Insufficient permissions 404 Not Found - Event not found 500 Internal Server Error - Server error
Get Event
Retrieves detailed information about a specific event.
http
GET /organizations/:organizationId/events/:idPath Parameters
| Parameter | Type | Description |
|---|---|---|
organizationId | string (UUID) | The organization ID that owns the event |
id | string (UUID) | The event ID to retrieve |
Success Response
Status: 200 OK
json
{
"data": {
"id": "550e8400-e29b-41d4-a716-446655440000",
"name": "Annual Gala",
"description": "Corporate fundraising event",
"organizationId": "650e8400-e29b-41d4-a716-446655440000",
"createdAt": "2024-01-01T00:00:00.000Z",
"updatedAt": "2024-01-15T10:30:00.000Z"
}
}Error Responses
400 Bad Request - Invalid event ID 403 Forbidden - Insufficient permissions 404 Not Found - Event not found
Data Schemas
Metadata Schema
The metadata object supports flexible event details:
typescript
{
category?: 'wedding' | 'birthday' | 'graduation' | 'proposal' | 'other'
customCategory?: string | null // Used when category is 'other'
guestCount?: {
approximate?: number | null // Single estimate (0-99999)
min?: number | null // Range minimum
max?: number | null // Range maximum
} | null
budgetRange?: {
min?: number | null // Minimum budget in dollars
max?: number | null // Maximum budget in dollars
currency?: string | null // ISO 4217 code (defaults to 'CAD')
} | null
}Metadata Examples
Simple event with category:
json
{
"metadata": {
"category": "wedding"
}
}Event with custom category:
json
{
"metadata": {
"category": "other",
"customCategory": "Corporate Team Building"
}
}Event with guest count estimate:
json
{
"metadata": {
"guestCount": {
"approximate": 150
}
}
}Event with guest count range:
json
{
"metadata": {
"guestCount": {
"min": 100,
"max": 200
}
}
}Event with budget range:
json
{
"metadata": {
"budgetRange": {
"min": 5000,
"max": 10000,
"currency": "USD"
}
}
}Complete metadata example:
json
{
"metadata": {
"category": "wedding",
"guestCount": {
"approximate": 150
},
"budgetRange": {
"min": 15000,
"max": 30000,
"currency": "CAD"
}
}
}Address Schema
The address object captures event location details:
typescript
{
streetAddress?: string // Primary street address (max 200 chars)
streetAddress2?: string // Secondary line (apt, suite, etc., max 200 chars)
city: string // Required city name (1-100 chars)
state?: string // State/province/region (2-100 chars)
postalCode?: string // Postal/ZIP code (validated per country)
country?: string // ISO 3166-1 alpha-2 code (US, CA, GB, etc.)
}Address Examples
Minimal address (city only):
json
{
"address": {
"city": "Toronto"
}
}Full Canadian address:
json
{
"address": {
"streetAddress": "123 Main St",
"streetAddress2": "Suite 400",
"city": "Toronto",
"state": "ON",
"postalCode": "M5V 3A8",
"country": "CA"
}
}US address:
json
{
"address": {
"streetAddress": "456 Broadway",
"city": "New York",
"state": "NY",
"postalCode": "10013",
"country": "US"
}
}International address:
json
{
"address": {
"streetAddress": "10 Downing Street",
"city": "London",
"postalCode": "SW1A 2AA",
"country": "GB"
}
}Authorization
Organization Membership
All event operations require the authenticated user to be a member of the target organization. The API checks membership before allowing any operation:
- Create: User must be a member of the organization specified in the URL
- Update: User must be a member of the organization that owns the event
- View: User must be a member of the organization that owns the event
Future Enhancements
Role-based permissions are planned but not yet implemented. Currently, all organization members have equal access to event operations.
Rate Limiting
Event API endpoints are subject to the global API rate limits:
- Limited to 100 requests per authenticated user per minute
- Limited to 10 requests per ip per minute if not authenticated
Validation Rules
Field Constraints
| Field | Constraint |
|---|---|
name | 1-255 characters, required |
description | Max 5000 characters |
status | Must be one of: BACKLOG, PLANNING, PLANNED, IN_PROGRESS, COMPLETED, CANCELLED |
metadata.category | Must be one of: wedding, birthday, graduation, proposal, other |
metadata.customCategory | Max 100 characters |
metadata.guestCount.approximate | 0-99999 |
metadata.guestCount.min | 0-99999 |
metadata.guestCount.max | 0-99999 |
metadata.budgetRange.min | Minimum 0 |
metadata.budgetRange.max | Minimum 0 |
metadata.budgetRange.currency | Valid ISO 4217 currency code |
address.streetAddress | Max 200 characters |
address.streetAddress2 | Max 200 characters |
address.city | 1-100 characters, required |
address.state | 2-100 characters |
address.postalCode | Format validated per country code |
address.country | Valid ISO 3166-1 alpha-2 code |
Custom Validators
- Currency codes - Validates against ISO 4217 standard (e.g., CAD, USD, EUR, GBP)
- Country codes - Validates against ISO 3166-1 alpha-2 standard (e.g., CA, US, GB, FR)
- Postal codes - Format validation based on country code (e.g., Canadian postal codes require format A1A 1A1)
Default Values
When creating events, the following defaults are applied:
| Field | Default Value |
|---|---|
status | BACKLOG |
eventType | ONE_OFF |
metadata | {} (empty object) |
description | null |
targetDate | null |
metadata.budgetRange.currency | CAD (if budgetRange is provided without currency) |
Common Use Cases
Create a simple event
http
POST /organizations/650e8400-e29b-41d4-a716-446655440000/events
Content-Type: application/jsonjson
{
"name": "Team Lunch"
}Create an event with full details
http
POST /organizations/650e8400-e29b-41d4-a716-446655440000/events
Content-Type: application/jsonjson
{
"name": "Annual Conference",
"description": "Company-wide annual conference",
"targetDate": "2024-09-15T09:00:00.000Z",
"metadata": {
"category": "other",
"customCategory": "Conference",
"guestCount": {
"min": 200,
"max": 300
},
"budgetRange": {
"min": 50000,
"max": 100000,
"currency": "USD"
}
},
"address": {
"streetAddress": "Convention Center",
"city": "San Francisco",
"state": "CA",
"country": "US"
}
}Update event status
http
PATCH /organizations/650e8400-e29b-41d4-a716-446655440000/events/550e8400-e29b-41d4-a716-446655440000
Content-Type: application/jsonjson
{
"status": "IN_PROGRESS"
}Update guest count
http
PATCH /organizations/650e8400-e29b-41d4-a716-446655440000/events/550e8400-e29b-41d4-a716-446655440000
Content-Type: application/jsonjson
{
"metadata": {
"guestCount": {
"approximate": 250
}
}
}Add address to existing event
http
PATCH /organizations/650e8400-e29b-41d4-a716-446655440000/events/550e8400-e29b-41d4-a716-446655440000
Content-Type: application/jsonjson
{
"address": {
"streetAddress": "789 Event Plaza",
"city": "Vancouver",
"state": "BC",
"postalCode": "V6B 4Y8",
"country": "CA"
}
}Notes
- All timestamps are returned in ISO 8601 format with UTC timezone
- Event IDs and organization IDs are UUID v7 (generated by
BaseModelWithUuid@beforeCreatehook). Note: Better Auth-managed tables (sessions, accounts) use UUID v4. - Address updates are upserts - if an address exists, it's updated; otherwise, a new one is created
- Metadata updates use deep merging to preserve unspecified fields
- To remove metadata fields, explicitly set them to
null
See Also
- Event Planning Data Models — Full entity schemas and relationships
- Event Planning Backend — Implementation patterns