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 - Standard iCalendar format compatibility
- 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
-
Clone the repository and navigate to the project directory.
-
Copy the environment file and configure it:
- Edit
.env with your database credentials:
- Create the database:
- Run the server (migrations run automatically on startup):
The server starts on http://localhost:3019. Swagger UI is available at http://localhost:3019/docs.
API Documentation
Authentication
JWT (for users)
Register or login to receive an access token (15 min) and refresh token:
Use the access token in subsequent requests:
API Keys (for agents)
Create a scoped API key for long-lived programmatic access:
Use the returned token in subsequent requests:
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 |
Project Structure
Architecture
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