# 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 <access_token>
```

### 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 <access_token>" \
  -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: <token>
```

## 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
