first commit

Made-with: Cursor
This commit is contained in:
Michilis
2026-02-28 02:17:55 +00:00
commit 41f6ae916f
92 changed files with 12332 additions and 0 deletions

899
about/details.md Normal file
View File

@@ -0,0 +1,899 @@
# Calendar & Contacts API
## details.md
This document defines exact endpoint contracts: request/response schemas, field constraints, pagination formats, examples, and implementation notes so a developer can build the API and a frontend without guessing.
All timestamps are RFC3339 strings.
All stored times are UTC.
---
# 1. Conventions
## 1.1 Base URL
* Local: [http://localhost:8080](http://localhost:8080)
* Production: [https://api.example.com](https://api.example.com)
All endpoints are under the root path.
---
## 1.2 Authentication Headers
JWT:
* Authorization: Bearer <access_token>
API key:
* X-API-Key: <api_key_token>
If both are present, JWT takes precedence.
---
## 1.3 Standard Response Envelope
This API returns plain JSON objects.
For list endpoints, a consistent list envelope is required.
List response envelope:
{
"items": [ ... ],
"page": {
"limit": 50,
"next_cursor": "opaque-or-null"
}
}
---
## 1.4 Cursor Pagination
Query params:
* limit (optional, default 50, max 200)
* cursor (optional)
Cursor meaning:
* Opaque base64url string encoding the tuple:
* last_sort_time (RFC3339)
* last_id (uuid)
Sorting rule for paginated lists:
* Primary: start_time asc (or created_at for contacts)
* Secondary: id asc
If cursor is provided:
* Return records strictly greater than the tuple.
---
## 1.5 Error Format
All error responses:
{
"error": "Human readable message",
"code": "MACHINE_READABLE_CODE",
"details": "Optional string or object"
}
HTTP mapping:
* 400 VALIDATION_ERROR
* 401 AUTH_REQUIRED / AUTH_INVALID
* 403 FORBIDDEN
* 404 NOT_FOUND
* 409 CONFLICT
* 429 RATE_LIMITED
* 500 INTERNAL
---
# 2. Data Schemas
## 2.1 User
{
"id": "uuid",
"email": "string",
"timezone": "string",
"created_at": "RFC3339",
"updated_at": "RFC3339"
}
Constraints:
* email lowercase
* timezone must be IANA timezone name, default "UTC"
---
## 2.2 Calendar
{
"id": "uuid",
"name": "string",
"color": "string",
"is_public": true,
"role": "owner|editor|viewer",
"created_at": "RFC3339",
"updated_at": "RFC3339"
}
Constraints:
* name 1..80
* color is hex like "#RRGGBB" (optional)
---
## 2.3 Event
{
"id": "uuid",
"calendar_id": "uuid",
"title": "string",
"description": "string|null",
"location": "string|null",
"start_time": "RFC3339-UTC",
"end_time": "RFC3339-UTC",
"timezone": "string",
"all_day": false,
"recurrence_rule": "string|null",
"created_by": "uuid",
"updated_by": "uuid",
"created_at": "RFC3339",
"updated_at": "RFC3339",
"reminders": [
{"id": "uuid", "minutes_before": 10}
],
"attendees": [
{"id": "uuid", "user_id": "uuid|null", "email": "string|null", "status": "pending|accepted|declined|tentative"}
],
"tags": ["string"],
"attachments": [
{"id": "uuid", "file_url": "string"}
]
}
Constraints:
* title 1..140
* timezone IANA name
* recurrence_rule must be valid RFC5545 RRULE when present
---
## 2.4 Contact
{
"id": "uuid",
"first_name": "string|null",
"last_name": "string|null",
"email": "string|null",
"phone": "string|null",
"company": "string|null",
"notes": "string|null",
"created_at": "RFC3339",
"updated_at": "RFC3339"
}
Constraints:
* At least one of: first_name, last_name, email, phone must be present
---
# 3. Endpoint Contracts
## 3.1 Auth
### POST /auth/register
Request:
{
"email": "[user@example.com](mailto:user@example.com)",
"password": "string",
"timezone": "America/Asuncion"
}
Rules:
* timezone optional
* server creates default calendar
Response 200:
{
"user": { ...User },
"access_token": "string",
"refresh_token": "string"
}
Errors:
* 400 VALIDATION_ERROR
* 409 CONFLICT (email already exists)
---
### POST /auth/login
Request:
{
"email": "[user@example.com](mailto:user@example.com)",
"password": "string"
}
Response 200:
{
"user": { ...User },
"access_token": "string",
"refresh_token": "string"
}
Errors:
* 401 AUTH_INVALID
---
### POST /auth/refresh
Request:
{
"refresh_token": "string"
}
Response 200:
{
"access_token": "string",
"refresh_token": "string"
}
Errors:
* 401 AUTH_INVALID
---
### POST /auth/logout
Request:
{
"refresh_token": "string"
}
Response 200:
{
"ok": true
}
---
### GET /auth/me
Response 200:
{
"user": { ...User }
}
---
## 3.2 API Keys
### POST /api-keys
Request:
{
"name": "My agent key",
"scopes": {
"calendars": ["read", "write"],
"events": ["read", "write"],
"contacts": ["read", "write"],
"availability": ["read"],
"booking": ["write"]
}
}
Response 200:
{
"id": "uuid",
"name": "My agent key",
"created_at": "RFC3339",
"token": "RAW_TOKEN_RETURNED_ONCE"
}
---
### GET /api-keys
Response 200:
{
"items": [
{"id": "uuid", "name": "string", "created_at": "RFC3339", "revoked_at": "RFC3339|null"}
],
"page": {"limit": 50, "next_cursor": null}
}
---
### DELETE /api-keys/{id}
Response 200:
{
"ok": true
}
---
## 3.3 Users
### GET /users/me
Response 200:
{
"user": { ...User }
}
---
### PUT /users/me
Request:
{
"timezone": "America/Asuncion"
}
Response 200:
{
"user": { ...User }
}
---
### DELETE /users/me
Response 200:
{
"ok": true
}
---
## 3.4 Calendars
### GET /calendars
Response 200:
{
"items": [ ...Calendar ],
"page": {"limit": 50, "next_cursor": null}
}
---
### POST /calendars
Request:
{
"name": "Work",
"color": "#22C55E"
}
Response 200:
{
"calendar": { ...Calendar }
}
---
### GET /calendars/{id}
Response 200:
{
"calendar": { ...Calendar }
}
---
### PUT /calendars/{id}
Request:
{
"name": "Work Calendar",
"color": "#22C55E",
"is_public": false
}
Rules:
* is_public only owner
Response 200:
{
"calendar": { ...Calendar }
}
---
### DELETE /calendars/{id}
Response 200:
{
"ok": true
}
---
## 3.5 Calendar Sharing
### POST /calendars/{id}/share
Request:
{
"target": {"email": "[other@example.com](mailto:other@example.com)"},
"role": "editor"
}
Response 200:
{
"ok": true
}
---
### GET /calendars/{id}/members
Response 200:
{
"items": [
{"user_id": "uuid", "email": "string", "role": "owner|editor|viewer"}
],
"page": {"limit": 50, "next_cursor": null}
}
---
### DELETE /calendars/{id}/members/{user_id}
Response 200:
{
"ok": true
}
---
## 3.6 Events
### GET /events
Query:
* start (required) RFC3339
* end (required) RFC3339
* calendar_id (optional)
* search (optional)
* tag (optional)
* limit, cursor
Response 200:
{
"items": [ ...Event ],
"page": {"limit": 50, "next_cursor": "string|null"}
}
Notes:
* Must include expanded recurrence occurrences inside requested range.
* For recurrence expansion, include occurrences as separate items with:
* id = master event id
* occurrence_start_time, occurrence_end_time (recommended fields)
Recommended recurrence occurrence representation:
{
"id": "uuid-master",
"is_occurrence": true,
"occurrence_start_time": "RFC3339",
"occurrence_end_time": "RFC3339",
...base event fields...
}
---
### POST /events
Request:
{
"calendar_id": "uuid",
"title": "Meeting",
"description": "Project sync",
"location": "Zoom",
"start_time": "2026-03-01T14:00:00-03:00",
"end_time": "2026-03-01T15:00:00-03:00",
"timezone": "America/Asuncion",
"all_day": false,
"recurrence_rule": null,
"reminders": [10, 60],
"tags": ["work", "sync"]
}
Response 200:
{
"event": { ...Event }
}
Errors:
* 403 FORBIDDEN
* 400 VALIDATION_ERROR
---
### GET /events/{id}
Response 200:
{
"event": { ...Event }
}
---
### PUT /events/{id}
Request:
{
"title": "Updated title",
"start_time": "2026-03-01T15:00:00-03:00",
"end_time": "2026-03-01T16:00:00-03:00",
"recurrence_rule": "FREQ=WEEKLY;BYDAY=MO,WE,FR"
}
Response 200:
{
"event": { ...Event }
}
---
### DELETE /events/{id}
Response 200:
{
"ok": true
}
---
## 3.7 Event Reminders
### POST /events/{id}/reminders
Request:
{
"minutes_before": [5, 15, 60]
}
Response 200:
{
"event": { ...Event }
}
---
### DELETE /events/{id}/reminders/{reminder_id}
Response 200:
{
"ok": true
}
---
## 3.8 Attendees
### POST /events/{id}/attendees
Request:
{
"attendees": [
{"email": "[guest@example.com](mailto:guest@example.com)"},
{"user_id": "uuid"}
]
}
Response 200:
{
"event": { ...Event }
}
---
### PUT /events/{id}/attendees/{attendee_id}
Request:
{
"status": "accepted"
}
Rules:
* Organizer can edit any attendee
* Attendee can edit own status
Response 200:
{
"event": { ...Event }
}
---
### DELETE /events/{id}/attendees/{attendee_id}
Response 200:
{
"ok": true
}
---
## 3.9 Contacts
### GET /contacts
Query:
* search (optional)
* limit, cursor
Response 200:
{
"items": [ ...Contact ],
"page": {"limit": 50, "next_cursor": "string|null"}
}
---
### POST /contacts
Request:
{
"first_name": "Jane",
"last_name": "Doe",
"email": "[jane@example.com](mailto:jane@example.com)",
"phone": "+595981000000",
"company": "Example SA",
"notes": "Met at event"
}
Response 200:
{
"contact": { ...Contact }
}
---
### GET /contacts/{id}
Response 200:
{
"contact": { ...Contact }
}
---
### PUT /contacts/{id}
Request:
{
"notes": "Updated notes"
}
Response 200:
{
"contact": { ...Contact }
}
---
### DELETE /contacts/{id}
Response 200:
{
"ok": true
}
---
## 3.10 Availability
### GET /availability
Query:
* calendar_id (required)
* start (required)
* end (required)
Response 200:
{
"calendar_id": "uuid",
"range_start": "RFC3339",
"range_end": "RFC3339",
"busy": [
{"start": "RFC3339", "end": "RFC3339", "event_id": "uuid"}
]
}
---
## 3.11 Booking Links
### POST /calendars/{id}/booking-link
Request:
{
"duration_minutes": 30,
"buffer_minutes": 0,
"timezone": "America/Asuncion",
"working_hours": {
"mon": [{"start": "09:00", "end": "17:00"}],
"tue": [{"start": "09:00", "end": "17:00"}],
"wed": [{"start": "09:00", "end": "17:00"}],
"thu": [{"start": "09:00", "end": "17:00"}],
"fri": [{"start": "09:00", "end": "17:00"}],
"sat": [],
"sun": []
},
"active": true
}
Response 200:
{
"token": "string",
"public_url": "[https://app.example.com/booking/](https://app.example.com/booking/)<token>",
"settings": { ...same-as-request }
}
---
### GET /booking/{token}/availability
Query:
* start
* end
Response 200:
{
"token": "string",
"timezone": "America/Asuncion",
"duration_minutes": 30,
"slots": [
{"start": "RFC3339", "end": "RFC3339"}
]
}
---
### POST /booking/{token}/reserve
Request:
{
"name": "Visitor Name",
"email": "[visitor@example.com](mailto:visitor@example.com)",
"slot_start": "RFC3339",
"slot_end": "RFC3339",
"notes": "Optional"
}
Response 200:
{
"ok": true,
"event": { ...Event }
}
Errors:
* 409 CONFLICT if slot no longer available
---
## 3.12 ICS
### GET /calendars/{id}/export.ics
Response:
* Content-Type: text/calendar
* Body: ICS format
---
### POST /calendars/import
Request:
* multipart/form-data
* calendar_id (uuid)
* file (.ics)
Response 200:
{
"ok": true,
"imported": {
"events": 12
}
}
---
# 4. Validation Constraints Summary
Users:
* email unique
* password >=10 chars
Calendars:
* name 1..80
* color valid hex
Events:
* title 1..140
* end_time > start_time
* timezone valid IANA
* reminders minutes_before must be 0..10080
Contacts:
* at least one identifying field
---
# 5. Implementation Notes (Go)
## Handler Layer
* Parse JSON
* Validate basic constraints
* Pass to service
## Service Layer
* Permission enforcement
* Ownership validation
* Time conversion to UTC
* Recurrence validation and expansion
* Reminder job scheduling
* Transaction management for booking reservations
## Repository Layer
* sqlc queries
* No business logic
---
# 6. Frontend and Agent Integration Guarantees
* The API must remain consistent in response shape.
* List endpoints always return items + page.
* All objects include created_at/updated_at.
* Calendar list includes role.
* Event list returns occurrences for recurrence within range.
* Booking endpoints require no auth.
---
End of details.md

501
about/logic.md Normal file
View File

@@ -0,0 +1,501 @@
# Calendar & Contacts API
## logic.md
This document defines the COMPLETE business logic layer of the Calendar & Contacts API.
It explains:
* Permission enforcement
* Ownership rules
* Validation rules
* Recurrence engine behavior
* Reminder processing
* Availability calculation
* Booking logic
* Transaction boundaries
* Audit requirements
* Edge case handling
This file MUST be treated as the authoritative source for backend logic.
---
## GLOBAL INVARIANTS
1. All timestamps stored in UTC.
2. All API timestamps are RFC3339 strings.
3. Ownership is ALWAYS derived from auth context.
4. Soft deletes are enforced everywhere.
5. No endpoint may leak data across users.
6. All mutations must write audit log entries.
---
1. AUTHENTICATION & CONTEXT
---
Every authenticated request must result in a RequestContext struct:
RequestContext:
* user_id (uuid)
* auth_method ("jwt" | "api_key")
* scopes (if api_key)
JWT auth:
* Validate signature
* Validate expiration
* Extract user_id
API key auth:
* Hash provided key
* Lookup in api_keys
* Ensure revoked_at is NULL
* Load scopes
If neither provided → AUTH_REQUIRED
If invalid → AUTH_INVALID
---
2. PERMISSION MODEL
---
Calendar roles:
* owner
* editor
* viewer
Permission matrix:
CALENDAR ACTIONS
* owner: full access
* editor: read calendar, CRUD events
* viewer: read-only
EVENT ACTIONS
* owner/editor: create/update/delete
* viewer: read
CONTACT ACTIONS
* Only owner of contacts can CRUD
BOOKING
* Only owner can create booking links
API KEY SCOPE ENFORCEMENT
Each endpoint maps to required scope.
Example:
* GET /calendars → calendars:read
* POST /events → events:write
* GET /contacts → contacts:read
If scope missing → FORBIDDEN
---
3. USER LOGIC
---
REGISTER
* Email lowercase
* Must be unique
* Password >=10 chars
* Hash with bcrypt cost >=12
* Create default calendar
* Return tokens
DELETE USER
* Soft delete user
* Soft delete calendars
* Soft delete events
* Soft delete contacts
* Revoke API keys
---
4. CALENDAR LOGIC
---
CREATE
* owner_id = authenticated user
* name required (1..80)
* color optional hex validation
LIST
Return calendars where:
* owner_id = user_id
OR
* calendar_members.user_id = user_id
Include role in response.
DELETE
* Only owner
* Soft delete calendar
* Soft delete all related events
SHARING
* Only owner can share
* Cannot share with self
* Upsert membership row
REMOVE MEMBER
* Only owner
* Cannot remove owner
---
5. EVENT LOGIC
---
CREATE EVENT
Validation:
* calendar exists
* user has editor or owner role
* title 1..140
* end_time > start_time
* timezone valid IANA
Time handling:
* Convert start_time to UTC
* Convert end_time to UTC
* Store original timezone string
Overlap rule:
* Overlap allowed by default
* Booking system enforces no-overlap
If recurrence_rule provided:
* Validate via rrule-go
* Store string
If reminders provided:
* Insert reminders
* Schedule jobs
UPDATE EVENT
* Same permission check
* Re-validate time constraints
* If recurrence changed → validate again
* Reschedule reminders
DELETE EVENT
* Soft delete
---
6. RECURRENCE ENGINE
---
Recurring events are NOT pre-expanded.
Storage:
* recurrence_rule string on master event
Expansion occurs ONLY during:
* GET /events
* GET /events/{id}/occurrences
Algorithm:
For each event in DB where:
* event.start_time <= range_end
If no recurrence_rule:
* Include if intersects range
If recurrence_rule present:
* Initialize rrule with DTSTART = event.start_time
* Generate occurrences within [range_start, range_end]
* For each occurrence:
* Check against exceptions
* Create virtual occurrence object
Occurrence response must include:
* is_occurrence = true
* occurrence_start_time
* occurrence_end_time
Exceptions table:
* event_id
* exception_date
* action ("skip")
If occurrence matches exception_date → skip
---
7. REMINDER PROCESSING
---
Reminder scheduling rule:
For each reminder (minutes_before):
trigger_time = event.start_time - minutes_before
If trigger_time > now:
* Enqueue Asynq job
Job payload:
* event_id
* reminder_id
* user_id
Worker logic:
* Load event
* If event.deleted_at != NULL → abort
* Send notification (webhook/email placeholder)
* Retry on failure with exponential backoff
On event update:
* Cancel old reminder jobs (if supported)
* Recompute schedule
---
8. CONTACT LOGIC
---
Contacts are strictly per-user.
CREATE
* Must have at least one identifying field
* email validated if present
SEARCH
* Case-insensitive match on:
* first_name
* last_name
* email
* company
DELETE
* Soft delete
---
9. AVAILABILITY LOGIC
---
Endpoint requires:
* calendar_id
* start
* end
Permission:
* viewer or higher
Busy condition:
An event is busy if:
(event.start_time < range_end)
AND
(event.end_time > range_start)
Recurring events:
* Expand occurrences
* Apply same intersection rule
Return busy blocks sorted by start.
---
10. BOOKING SYSTEM LOGIC
---
CREATE BOOKING LINK
* Only owner
* Generate secure random token
* Store configuration
PUBLIC AVAILABILITY
* No auth
* Load booking link
* Validate active
* Compute working-hour windows
* Subtract busy blocks
* Apply buffer_minutes before/after events
RESERVATION
* Begin DB transaction
* Re-check slot availability with overlap query
* If conflict → ROLLBACK + CONFLICT
* Insert event
* Commit
Overlap query inside transaction:
SELECT 1 FROM events
WHERE calendar_id = ?
AND deleted_at IS NULL
AND start_time < slot_end
AND end_time > slot_start
FOR UPDATE
This ensures race safety.
---
11. CURSOR PAGINATION
---
Cursor format:
base64url(last_sort_time|last_id)
Decoding:
* Split by pipe
* Use as tuple comparison
Events sorting:
ORDER BY start_time ASC, id ASC
Contacts sorting:
ORDER BY created_at ASC, id ASC
Limit enforcement:
* Default 50
* Max 200
---
12. AUDIT LOG LOGIC
---
Every mutation writes:
entity_type
entity_id
action
user_id
timestamp
Actions examples:
* CREATE_EVENT
* UPDATE_EVENT
* DELETE_EVENT
* SHARE_CALENDAR
* DELETE_CONTACT
Audit writes MUST NOT fail main transaction.
If audit insert fails:
* Log error
* Continue response
---
13. ERROR RULES
---
AUTH_REQUIRED → no credentials
AUTH_INVALID → invalid token
FORBIDDEN → permission denied
NOT_FOUND → entity not found or not accessible
VALIDATION_ERROR → invalid input
CONFLICT → overlap or duplicate
INTERNAL → unexpected error
Never expose internal DB errors directly.
---
14. TRANSACTION RULES
---
Transactions REQUIRED for:
* Booking reservation
* Event creation with reminders
* Event update affecting reminders
* Deleting calendar (cascade soft delete)
Service layer must manage transactions.
Repository layer must not auto-commit.
---
15. PERFORMANCE REQUIREMENTS
---
All list endpoints:
* Must use indexed columns
* Must use pagination
* Must avoid N+1 queries
Recurring expansion must be bounded by requested range.
Maximum recurrence expansion window allowed per request:
* 1 year (recommended safeguard)
If range exceeds safeguard → VALIDATION_ERROR
---
This is the authoritative backend logic specification.

389
about/overview.md Normal file
View File

@@ -0,0 +1,389 @@
# Calendar & Contacts API
## 1. Purpose
This system is a productiongrade Calendar and Contacts REST API written in Go. It is designed for:
* Human users (web/mobile frontends)
* AI agents (programmatic automation)
* Future SaaS expansion
* Highintegrity multiuser environments
The API must be stateless, secure, permissionenforced, timezonesafe, and scalable.
This document defines EXACTLY what must be built.
---
# 2. System Architecture
Client (Web / Mobile / Agent)
HTTP REST API (Go + Chi)
Service Layer (Business Logic)
Repository Layer (SQL via sqlc)
PostgreSQL
Optional components:
* Redis (rate limiting + job queue)
* Background Worker (reminders)
* S3-compatible storage (attachments)
* WebSocket server (real-time updates)
---
# 3. Core Design Principles
1. Stateless authentication (JWT or API key)
2. Strict ownership validation
3. All timestamps stored in UTC
4. RFC3339 format for API
5. Soft deletion instead of hard deletion
6. No trust in client-provided ownership fields
7. Clear separation between handlers, services, repositories
8. Indexes on all high-query columns
---
# 4. Technology Stack
Language: Go 1.22+
Router: Chi
Database: PostgreSQL 15+
Query Layer: sqlc
Migrations: golang-migrate
Auth: JWT (github.com/golang-jwt/jwt/v5)
Password hashing: bcrypt
UUID: google/uuid
Background jobs: Asynq (Redis)
WebSockets: gorilla/websocket (optional)
RRULE: rrule-go
Storage: S3 compatible (MinIO client)
---
# 5. Authentication Model
Two authentication modes must be implemented.
## 5.1 User Authentication
* Email + password
* Password hashed with bcrypt (cost 12+)
* JWT access token (15 min expiration)
* Refresh token (730 days)
JWT payload:
{
"user_id": "uuid",
"exp": unix_timestamp
}
All protected endpoints require:
Authorization: Bearer <token>
Middleware must:
* Validate signature
* Validate expiration
* Inject user_id into request context
---
## 5.2 Agent Authentication (API Keys)
Agents must be able to:
* Create account
* Generate API key
* Perform scoped operations
API keys:
* Random 32+ byte token
* Only hash stored in DB
* Sent via header:
X-API-Key: <token>
Scopes example:
{
"calendars": ["read", "write"],
"events": ["read", "write"],
"contacts": ["read", "write"]
}
Middleware must validate scope before allowing access.
---
# 6. Data Model Overview
## Users
* id (uuid)
* email (unique)
* password_hash
* timezone
* is_active
* created_at
* updated_at
* deleted_at
## API Keys
* id (uuid)
* user_id
* name
* key_hash
* scopes (jsonb)
* created_at
* revoked_at
## Calendars
* id (uuid)
* owner_id
* name
* color
* is_public
* public_token
* created_at
* updated_at
* deleted_at
## Calendar Members
* calendar_id
* user_id
* role (owner/editor/viewer)
## Events
* id (uuid)
* calendar_id
* title
* description
* location
* start_time (UTC)
* end_time (UTC)
* timezone
* all_day
* recurrence_rule
* created_by
* updated_by
* created_at
* updated_at
* deleted_at
## Event Reminders
* id
* event_id
* minutes_before
## Event Attendees
* id
* event_id
* user_id (nullable)
* email (nullable)
* status
## Contacts
* id
* owner_id
* first_name
* last_name
* email
* phone
* company
* notes
* created_at
* updated_at
* deleted_at
---
# 7. Full REST Endpoint Specification
All endpoints return JSON.
All errors return structured error format.
Error format:
{
"error": "string",
"code": "string",
"details": "optional"
}
---
# AUTH
POST /auth/register
POST /auth/login
POST /auth/refresh
POST /auth/logout
GET /auth/me
---
# API KEYS
POST /api-keys
GET /api-keys
DELETE /api-keys/{id}
---
# USERS
GET /users/me
PUT /users/me
DELETE /users/me
---
# CALENDARS
GET /calendars
POST /calendars
GET /calendars/{id}
PUT /calendars/{id}
DELETE /calendars/{id}
Sharing
POST /calendars/{id}/share
GET /calendars/{id}/members
DELETE /calendars/{id}/members/{user_id}
---
# EVENTS
GET /events
GET /events/{id}
POST /events
PUT /events/{id}
DELETE /events/{id}
Filters:
GET /events?start=...&end=...
GET /events?calendar_id=...
GET /events?search=...
GET /events?tag=...
---
# REMINDERS
POST /events/{id}/reminders
DELETE /events/{id}/reminders/{id}
---
# ATTENDEES
POST /events/{id}/attendees
PUT /events/{id}/attendees/{id}
DELETE /events/{id}/attendees/{id}
---
# CONTACTS
GET /contacts
POST /contacts
GET /contacts/{id}
PUT /contacts/{id}
DELETE /contacts/{id}
GET /contacts?search=...
---
# AVAILABILITY
GET /availability?calendar_id=...&start=...&end=...
---
# BOOKING
POST /calendars/{id}/booking-link
GET /booking/{token}/availability
POST /booking/{token}/reserve
---
# ICS
GET /calendars/{id}/export.ics
POST /calendars/import
---
# 8. Index Requirements
Required indexes:
* events(calendar_id, start_time)
* events(start_time)
* calendars(owner_id)
* contacts(owner_id)
* api_keys(user_id)
---
# 9. Performance Rules
* All list endpoints paginated (limit + cursor)
* No N+1 queries
* All joins explicitly defined
* Recurring events expanded lazily
* Reminder queries indexed
---
# 10. Security Requirements
* Rate limiting middleware
* UUID validation
* Input validation
* SQL injection safe queries
* Password complexity enforcement
* API key hashing
---
# 11. Development Phases
Phase 1
* Auth
* Calendars
* Events
* Contacts
Phase 2
* Recurrence
* Reminders
* Sharing
Phase 3
* Booking links
* Availability
* ICS
* WebSockets
---
This file defines the full system scope.
The next file (logic.md) will define detailed business rules, validation logic, permission flows, recurrence logic, reminder processing, and booking behavior.