# Calendar & Contacts API A production-grade REST API for calendar management, event scheduling, contacts, availability queries, and public booking links. Built with Go, PostgreSQL, and designed for human users, AI agents, and programmatic automation. ## Features - **Calendars** - Create, share, and manage multiple calendars with role-based access (owner/editor/viewer) - **Events** - Full CRUD with recurring events (RFC 5545 RRULE), reminders, attendees, tags, and attachments - **Contacts** - Personal contact management with search - **Availability** - Query busy/free time across calendars - **Booking Links** - Public scheduling pages with configurable working hours, duration, and buffer time - **ICS Import/Export** - Full RFC 5545 iCalendar support with VALARM, ATTENDEE, TZID, all-day events - **Public iCal Feeds** - Make calendars public and subscribe via iCal URL in any calendar app - **iCal URL Import** - Import events from any external iCal feed URL - **Dual Auth** - JWT tokens for interactive use, scoped API keys for agents and automation - **Background Jobs** - Reminder notifications via Redis + Asynq (optional) ## Tech Stack | Component | Technology | |-----------|-----------| | Language | Go 1.24 | | Router | Chi | | Database | PostgreSQL 15+ | | Query Layer | sqlc | | Migrations | golang-migrate | | Auth | JWT (golang-jwt/jwt/v5) + bcrypt | | Recurrence | rrule-go (RFC 5545) | | Background Jobs | Asynq (Redis) | | API Docs | OpenAPI 3.1.0 + Swagger UI | ## Quick Start ### Prerequisites - Go 1.24+ - PostgreSQL 15+ - Redis (optional, for background reminder jobs) ### Setup 1. Clone the repository and navigate to the project directory. 2. Copy the environment file and configure it: ```bash cp .env.example .env ``` 3. Edit `.env` with your database credentials: ```env DATABASE_URL=postgres://calendarapi:password@localhost:5432/calendarapi?sslmode=disable JWT_SECRET=your-secret-key-change-me SERVER_PORT=3019 ENV=development # REDIS_ADDR=localhost:6379 # uncomment to enable background jobs ``` 4. Create the database: ```bash createdb calendarapi ``` 5. Run the server (migrations run automatically on startup): ```bash go run cmd/server/main.go ``` The server starts on `http://localhost:3019`. Swagger UI is available at `http://localhost:3019/docs`. ## API Documentation | Resource | URL | |----------|-----| | Swagger UI | http://localhost:3019/docs | | OpenAPI Spec | http://localhost:3019/openapi.json | | LLM Reference | [llms.txt](llms.txt) | | Agent Skill | [SKILL.md](SKILL.md) | ## Authentication ### JWT (for users) Register or login to receive an access token (15 min) and refresh token: ```bash # Register curl -X POST http://localhost:3019/auth/register \ -H "Content-Type: application/json" \ -d '{"email": "user@example.com", "password": "securepassword123", "timezone": "UTC"}' # Login curl -X POST http://localhost:3019/auth/login \ -H "Content-Type: application/json" \ -d '{"email": "user@example.com", "password": "securepassword123"}' ``` Use the access token in subsequent requests: ``` Authorization: Bearer ``` ### API Keys (for agents) Create a scoped API key for long-lived programmatic access: ```bash curl -X POST http://localhost:3019/api-keys \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" \ -d '{ "name": "my-agent", "scopes": { "calendars": ["read", "write"], "events": ["read", "write"], "contacts": ["read", "write"], "availability": ["read"], "booking": ["write"] } }' ``` Use the returned token in subsequent requests: ``` X-API-Key: ``` ## API Endpoints ### Auth | Method | Endpoint | Auth | Description | |--------|----------|------|-------------| | POST | `/auth/register` | None | Create account | | POST | `/auth/login` | None | Login | | POST | `/auth/refresh` | None | Refresh JWT tokens | | POST | `/auth/logout` | Yes | Revoke refresh token | | GET | `/auth/me` | Yes | Get current user | ### Users | Method | Endpoint | Description | |--------|----------|-------------| | GET | `/users/me` | Get profile | | PUT | `/users/me` | Update profile | | DELETE | `/users/me` | Delete account | ### API Keys | Method | Endpoint | Description | |--------|----------|-------------| | POST | `/api-keys` | Create API key | | GET | `/api-keys` | List API keys | | DELETE | `/api-keys/{id}` | Revoke API key | ### Calendars | Method | Endpoint | Scope | Description | |--------|----------|-------|-------------| | GET | `/calendars` | calendars:read | List calendars | | POST | `/calendars` | calendars:write | Create calendar | | GET | `/calendars/{id}` | calendars:read | Get calendar | | PUT | `/calendars/{id}` | calendars:write | Update calendar | | DELETE | `/calendars/{id}` | calendars:write | Delete calendar | | POST | `/calendars/{id}/share` | calendars:write | Share calendar | | GET | `/calendars/{id}/members` | calendars:read | List members | | DELETE | `/calendars/{id}/members/{userID}` | calendars:write | Remove member | ### Events | Method | Endpoint | Scope | Description | |--------|----------|-------|-------------| | GET | `/events` | events:read | List events in time range | | POST | `/events` | events:write | Create event | | GET | `/events/{id}` | events:read | Get event | | PUT | `/events/{id}` | events:write | Update event | | DELETE | `/events/{id}` | events:write | Delete event | | POST | `/events/{id}/reminders` | events:write | Add reminders | | DELETE | `/events/{id}/reminders/{reminderID}` | events:write | Remove reminder | | POST | `/events/{id}/attendees` | events:write | Add attendees | | PUT | `/events/{id}/attendees/{attendeeID}` | events:write | Update RSVP | | DELETE | `/events/{id}/attendees/{attendeeID}` | events:write | Remove attendee | ### Contacts | Method | Endpoint | Scope | Description | |--------|----------|-------|-------------| | GET | `/contacts` | contacts:read | List contacts | | POST | `/contacts` | contacts:write | Create contact | | GET | `/contacts/{id}` | contacts:read | Get contact | | PUT | `/contacts/{id}` | contacts:write | Update contact | | DELETE | `/contacts/{id}` | contacts:write | Delete contact | ### Availability | Method | Endpoint | Scope | Description | |--------|----------|-------|-------------| | GET | `/availability` | availability:read | Get busy blocks for a calendar | ### Booking | Method | Endpoint | Auth | Description | |--------|----------|------|-------------| | POST | `/calendars/{id}/booking-link` | booking:write | Create booking link | | GET | `/booking/{token}/availability` | None | Get available slots | | POST | `/booking/{token}/reserve` | None | Reserve a slot | ### ICS | Method | Endpoint | Scope | Description | |--------|----------|-------|-------------| | GET | `/calendars/{id}/export.ics` | calendars:read | Export as ICS | | POST | `/calendars/import` | calendars:write | Import ICS file | | POST | `/calendars/import-url` | calendars:write | Import from iCal URL | | GET | `/cal/{token}/feed.ics` | None (public) | Public iCal feed | ## Project Structure ``` calendarapi/ cmd/server/main.go Entry point internal/ api/ routes.go Route definitions handlers/ HTTP handlers openapi/ OpenAPI spec files + Swagger UI auth/ JWT manager config/ Environment config middleware/ Auth, rate limiting, scope enforcement models/ Domain models repository/ sqlc-generated database queries scheduler/ Asynq background job scheduler service/ Business logic layer utils/ Shared utilities migrations/ SQL migration files sqlc/ sqlc configuration about/ Design specifications overview.md Architecture and data model logic.md Business logic rules details.md Endpoint contracts SKILL.md AI agent integration guide llms.txt LLM-optimized API reference .env.example Environment template go.mod go.sum ``` ## Architecture ``` Client (Web / Mobile / Agent) | HTTP REST API (Chi router) | Middleware (Auth, Rate Limiting, Scope Enforcement) | Handler Layer (Parse request, validate, return JSON) | Service Layer (Business logic, permissions, transactions) | Repository Layer (sqlc queries) | PostgreSQL ``` ## Design Principles - **Stateless** - JWT or API key on every request, no server-side sessions - **UTC everywhere** - All timestamps stored in UTC, original timezone preserved - **Soft deletes** - No data is permanently destroyed - **Strict ownership** - Ownership derived from auth context, never from client input - **Cursor pagination** - Efficient, consistent pagination on all list endpoints - **Scoped API keys** - Fine-grained permission control for agent access - **Race-safe booking** - Database transactions with row locking prevent double-booking ## License MIT