- Public/private: toggle is_public via PUT /calendars/{id}; generate/clear
public_token and return ical_url when public
- Public feed: GET /cal/{token}/feed.ics (no auth) for subscription in
Google/Apple/Outlook calendars
- Full iCal export: use golang-ical; VALARM, ATTENDEE, all-day (VALUE=DATE),
RRULE, DTSTAMP, CREATED, LAST-MODIFIED
- Full iCal import: parse TZID, VALUE=DATE, VALARM, ATTENDEE, RRULE
- Import from URL: POST /calendars/import-url with calendar_id + url
- Migration: unique index on public_token, calendar_subscriptions table
- Config: BASE_URL for ical_url; Calendar model + API: ical_url field
- Docs: OpenAPI, llms.txt, README, SKILL.md, about/overview
Made-with: Cursor
183 lines
8.1 KiB
JSON
183 lines
8.1 KiB
JSON
{
|
|
"components": {
|
|
"securitySchemes": {
|
|
"BearerAuth": {
|
|
"type": "http",
|
|
"scheme": "bearer",
|
|
"bearerFormat": "JWT",
|
|
"description": "JWT access token obtained from /auth/login or /auth/register"
|
|
},
|
|
"ApiKeyAuth": {
|
|
"type": "apiKey",
|
|
"in": "header",
|
|
"name": "X-API-Key",
|
|
"description": "API key token for agent/programmatic access with scoped permissions"
|
|
}
|
|
},
|
|
"schemas": {
|
|
"Error": {
|
|
"type": "object",
|
|
"required": ["error", "code"],
|
|
"properties": {
|
|
"error": { "type": "string", "description": "Human-readable error message" },
|
|
"code": { "type": "string", "description": "Machine-readable error code", "enum": ["VALIDATION_ERROR", "AUTH_REQUIRED", "AUTH_INVALID", "FORBIDDEN", "NOT_FOUND", "CONFLICT", "RATE_LIMITED", "INTERNAL"] },
|
|
"details": { "description": "Additional error context" }
|
|
}
|
|
},
|
|
"OkResponse": {
|
|
"type": "object",
|
|
"required": ["ok"],
|
|
"properties": {
|
|
"ok": { "type": "boolean", "example": true }
|
|
}
|
|
},
|
|
"PageInfo": {
|
|
"type": "object",
|
|
"required": ["limit", "next_cursor"],
|
|
"properties": {
|
|
"limit": { "type": "integer", "example": 50 },
|
|
"next_cursor": { "type": "string", "nullable": true, "description": "Opaque cursor for next page, null if no more results" }
|
|
}
|
|
},
|
|
"User": {
|
|
"type": "object",
|
|
"required": ["id", "email", "timezone", "created_at", "updated_at"],
|
|
"properties": {
|
|
"id": { "type": "string", "format": "uuid" },
|
|
"email": { "type": "string", "format": "email" },
|
|
"timezone": { "type": "string", "example": "America/New_York", "description": "IANA timezone name" },
|
|
"created_at": { "type": "string", "format": "date-time" },
|
|
"updated_at": { "type": "string", "format": "date-time" }
|
|
}
|
|
},
|
|
"Calendar": {
|
|
"type": "object",
|
|
"required": ["id", "name", "color", "is_public", "created_at", "updated_at"],
|
|
"properties": {
|
|
"id": { "type": "string", "format": "uuid" },
|
|
"name": { "type": "string", "minLength": 1, "maxLength": 80 },
|
|
"color": { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$", "example": "#22C55E" },
|
|
"is_public": { "type": "boolean" },
|
|
"ical_url": { "type": "string", "format": "uri", "description": "Public iCal feed URL (only present when is_public is true)" },
|
|
"role": { "type": "string", "enum": ["owner", "editor", "viewer"], "description": "Current user's role on this calendar" },
|
|
"created_at": { "type": "string", "format": "date-time" },
|
|
"updated_at": { "type": "string", "format": "date-time" }
|
|
}
|
|
},
|
|
"Reminder": {
|
|
"type": "object",
|
|
"required": ["id", "minutes_before"],
|
|
"properties": {
|
|
"id": { "type": "string", "format": "uuid" },
|
|
"minutes_before": { "type": "integer", "minimum": 0, "maximum": 10080 }
|
|
}
|
|
},
|
|
"Attendee": {
|
|
"type": "object",
|
|
"required": ["id", "status"],
|
|
"properties": {
|
|
"id": { "type": "string", "format": "uuid" },
|
|
"user_id": { "type": "string", "format": "uuid", "nullable": true },
|
|
"email": { "type": "string", "format": "email", "nullable": true },
|
|
"status": { "type": "string", "enum": ["pending", "accepted", "declined", "tentative"] }
|
|
}
|
|
},
|
|
"Attachment": {
|
|
"type": "object",
|
|
"required": ["id", "file_url"],
|
|
"properties": {
|
|
"id": { "type": "string", "format": "uuid" },
|
|
"file_url": { "type": "string", "format": "uri" }
|
|
}
|
|
},
|
|
"Event": {
|
|
"type": "object",
|
|
"required": ["id", "calendar_id", "title", "start_time", "end_time", "timezone", "all_day", "created_by", "updated_by", "created_at", "updated_at"],
|
|
"properties": {
|
|
"id": { "type": "string", "format": "uuid" },
|
|
"calendar_id": { "type": "string", "format": "uuid" },
|
|
"title": { "type": "string", "minLength": 1, "maxLength": 140 },
|
|
"description": { "type": "string", "nullable": true },
|
|
"location": { "type": "string", "nullable": true },
|
|
"start_time": { "type": "string", "format": "date-time", "description": "UTC start time in RFC3339" },
|
|
"end_time": { "type": "string", "format": "date-time", "description": "UTC end time in RFC3339" },
|
|
"timezone": { "type": "string", "example": "America/Asuncion", "description": "Original IANA timezone" },
|
|
"all_day": { "type": "boolean" },
|
|
"recurrence_rule": { "type": "string", "nullable": true, "description": "RFC5545 RRULE string", "example": "FREQ=WEEKLY;BYDAY=MO,WE,FR" },
|
|
"is_occurrence": { "type": "boolean", "description": "True if this is an expanded recurrence occurrence" },
|
|
"occurrence_start_time": { "type": "string", "format": "date-time", "nullable": true },
|
|
"occurrence_end_time": { "type": "string", "format": "date-time", "nullable": true },
|
|
"created_by": { "type": "string", "format": "uuid" },
|
|
"updated_by": { "type": "string", "format": "uuid" },
|
|
"created_at": { "type": "string", "format": "date-time" },
|
|
"updated_at": { "type": "string", "format": "date-time" },
|
|
"reminders": { "type": "array", "items": { "$ref": "#/components/schemas/Reminder" } },
|
|
"attendees": { "type": "array", "items": { "$ref": "#/components/schemas/Attendee" } },
|
|
"tags": { "type": "array", "items": { "type": "string" } },
|
|
"attachments": { "type": "array", "items": { "$ref": "#/components/schemas/Attachment" } }
|
|
}
|
|
},
|
|
"Contact": {
|
|
"type": "object",
|
|
"required": ["id", "created_at", "updated_at"],
|
|
"properties": {
|
|
"id": { "type": "string", "format": "uuid" },
|
|
"first_name": { "type": "string", "nullable": true },
|
|
"last_name": { "type": "string", "nullable": true },
|
|
"email": { "type": "string", "format": "email", "nullable": true },
|
|
"phone": { "type": "string", "nullable": true },
|
|
"company": { "type": "string", "nullable": true },
|
|
"notes": { "type": "string", "nullable": true },
|
|
"created_at": { "type": "string", "format": "date-time" },
|
|
"updated_at": { "type": "string", "format": "date-time" }
|
|
}
|
|
},
|
|
"APIKeyResponse": {
|
|
"type": "object",
|
|
"required": ["id", "name", "created_at"],
|
|
"properties": {
|
|
"id": { "type": "string", "format": "uuid" },
|
|
"name": { "type": "string" },
|
|
"created_at": { "type": "string", "format": "date-time" },
|
|
"revoked_at": { "type": "string", "format": "date-time", "nullable": true },
|
|
"token": { "type": "string", "description": "Raw token, only returned once on creation" }
|
|
}
|
|
},
|
|
"CalendarMember": {
|
|
"type": "object",
|
|
"required": ["user_id", "email", "role"],
|
|
"properties": {
|
|
"user_id": { "type": "string", "format": "uuid" },
|
|
"email": { "type": "string", "format": "email" },
|
|
"role": { "type": "string", "enum": ["owner", "editor", "viewer"] }
|
|
}
|
|
},
|
|
"BusyBlock": {
|
|
"type": "object",
|
|
"required": ["start", "end", "event_id"],
|
|
"properties": {
|
|
"start": { "type": "string", "format": "date-time" },
|
|
"end": { "type": "string", "format": "date-time" },
|
|
"event_id": { "type": "string", "format": "uuid" }
|
|
}
|
|
},
|
|
"WorkingHourSlot": {
|
|
"type": "object",
|
|
"required": ["start", "end"],
|
|
"properties": {
|
|
"start": { "type": "string", "example": "09:00", "pattern": "^\\d{2}:\\d{2}$" },
|
|
"end": { "type": "string", "example": "17:00", "pattern": "^\\d{2}:\\d{2}$" }
|
|
}
|
|
},
|
|
"TimeSlot": {
|
|
"type": "object",
|
|
"required": ["start", "end"],
|
|
"properties": {
|
|
"start": { "type": "string", "format": "date-time" },
|
|
"end": { "type": "string", "format": "date-time" }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|