Complete reference for TimelyOne API endpoints.
TimelyOne uses cookie-based authentication with optional password protection.
1. User logs in → POST /api/auth/login
2. Server sets httpOnly cookie
3. Subsequent requests include cookie
4. Middleware validates session
These endpoints do NOT require authentication:
GET /book/[slug]- Public booking pagePOST /api/booking-link/book- Create booking (public)POST /api/auth/login- Login endpointGET /api/setup- Setup check (redirects if needed)
All other endpoints require authentication. Unauthenticated requests receive:
- Status:
401 Unauthorized - Redirect to
/loginfor browser requests
Check setup status and get current user.
Authentication: Public
Response (Setup Incomplete):
{
"setupComplete": false,
"user": null
}Response (Setup Complete):
{
"setupComplete": true,
"user": {
"id": "user_123",
"email": "user@example.com",
"name": "John Doe",
"timezone": "America/New_York"
}
}Complete first-time setup (creates user).
Authentication: Public
Request Body:
{
"name": "John Doe",
"email": "user@example.com",
"timezone": "America/New_York",
"password": "optional_password" // Optional
}Response (Success):
{
"success": true,
"user": {
"id": "user_123",
"email": "user@example.com",
"name": "John Doe",
"timezone": "America/New_York"
}
}Response (Error):
{
"error": "User already exists"
}Status Codes:
200- Success400- Invalid request409- User already exists
Get current user profile.
Authentication: Required
Response:
{
"id": "user_123",
"email": "user@example.com",
"name": "John Doe",
"timezone": "America/New_York",
"password": true // Indicates if password is set
}Update user profile.
Authentication: Required
Request Body:
{
"name": "Jane Doe",
"timezone": "Europe/London"
}Response:
{
"success": true,
"user": {
"id": "user_123",
"email": "user@example.com",
"name": "Jane Doe",
"timezone": "Europe/London"
}
}Set or update password.
Authentication: Required
Request Body (New Password):
{
"newPassword": "new_secure_password"
}Request Body (Change Password):
{
"currentPassword": "old_password",
"newPassword": "new_secure_password"
}Response:
{
"success": true,
"message": "Password updated successfully"
}Error Responses:
401- Current password incorrect400- Invalid password format
Delete user account and all data.
Authentication: Required
Response:
{
"success": true,
"message": "Account deleted successfully"
}Note: This is a destructive operation that deletes:
- User record
- All calendar connections
- All synced events
- All booking links
- All bookings
Login with email and password.
Authentication: Public
Request Body:
{
"email": "user@example.com",
"password": "user_password"
}Response (Success):
{
"success": true,
"user": {
"id": "user_123",
"email": "user@example.com",
"name": "John Doe"
}
}Response (Error):
{
"error": "Invalid email or password"
}Status Codes:
200- Success (sets httpOnly cookie)401- Invalid credentials404- User not found
Logout and clear session.
Authentication: Required
Response:
{
"success": true,
"message": "Logged out successfully"
}Get all connected calendar accounts.
Authentication: Required
Query Parameters:
userId(required): User ID
Response:
{
"connections": [
{
"id": "conn_123",
"provider": "google",
"email": "user@gmail.com",
"status": "ACTIVE",
"connectedAt": "2024-01-01T00:00:00Z",
"lastSyncAt": "2024-01-15T12:00:00Z",
"calendars": [
{
"id": "cal_456",
"name": "user@gmail.com",
"isSelected": true,
"color": "#3b82f6",
"eventCount": 42
}
]
}
]
}Initiate calendar connection (OAuth).
Authentication: Required
Request Body:
{
"provider": "google" // or "microsoft"
}Response:
{
"redirectUrl": "https://oauth.composio.dev/..."
}Usage: Open redirectUrl in new window for OAuth flow.
Disconnect a calendar account.
Authentication: Required
Request Body:
{
"connectionId": "conn_123",
"force": false // Optional: skip confirmation
}Response (Has Booking Links):
{
"error": "This calendar connection has active booking links",
"requiresConfirmation": true,
"affectedBookingLinks": [
{
"title": "Book a Meeting",
"slug": "john-doe",
"isActive": true,
"url": "/book/john-doe"
}
],
"message": "Disconnecting will affect 1 booking link(s)..."
}Response (Success):
{
"success": true,
"message": "Connection disconnected successfully"
}Status Codes:
200- Success409- Booking links conflict (requires force=true)404- Connection not found
Sync events from connected calendars.
Authentication: Required
Request Body:
{
"userId": "user_123",
"forceFullSync": false // Optional: ignore sync tokens
}Response:
{
"success": true,
"totalCreated": 5,
"totalUpdated": 12,
"totalDeleted": 2,
"connectionResults": [
{
"connectionId": "conn_123",
"email": "user@gmail.com",
"created": 3,
"updated": 8,
"deleted": 1
}
]
}OAuth callback handler (called by Composio after authorization).
Authentication: Required
Query Parameters:
- Handled automatically by OAuth flow
Response: Redirects to /connections
Get all calendar events.
Authentication: Required
Query Parameters:
userId(required): User IDstart(optional): Start date filter (ISO 8601)end(optional): End date filter (ISO 8601)
Response:
{
"events": [
{
"id": "event_123",
"title": "Team Meeting",
"description": "Weekly standup",
"location": "Conference Room A",
"startTime": "2024-01-15T10:00:00Z",
"endTime": "2024-01-15T11:00:00Z",
"isAllDay": false,
"meetLink": "https://meet.google.com/abc-defg-hij",
"calendarId": "cal_456",
"calendarName": "user@gmail.com",
"calendarColor": "#3b82f6",
"attendees": ["attendee@example.com"],
"status": "confirmed"
}
]
}Create a new calendar event.
Authentication: Required
Request Body:
{
"calendarId": "cal_456",
"title": "New Meeting",
"description": "Meeting description",
"location": "Office",
"startTime": "2024-01-20T14:00:00Z",
"endTime": "2024-01-20T15:00:00Z",
"isAllDay": false,
"addGoogleMeet": true,
"attendees": ["guest@example.com"]
}Response:
{
"success": true,
"event": {
"id": "event_789",
"title": "New Meeting",
"meetLink": "https://meet.google.com/xyz-abcd-efg",
// ... other event fields
}
}Status Codes:
200- Success400- Invalid request404- Calendar not found
Update an existing event.
Authentication: Required
Request Body:
{
"title": "Updated Title",
"startTime": "2024-01-20T15:00:00Z",
"endTime": "2024-01-20T16:00:00Z"
}Response:
{
"success": true,
"event": {
"id": "event_789",
// ... updated fields
}
}Delete an event.
Authentication: Required
Response:
{
"success": true,
"message": "Event deleted successfully"
}Update calendar settings (e.g., toggle sync).
Authentication: Required
Request Body:
{
"isSelected": true // Enable/disable sync for this calendar
}Response:
{
"success": true,
"calendar": {
"id": "cal_456",
"name": "user@gmail.com",
"isSelected": true
}
}Get user's booking link configuration.
Authentication: Required
Query Parameters:
userId(required): User ID
Response:
{
"bookingLink": {
"id": "booking_123",
"slug": "john-doe",
"title": "Book a Meeting with John",
"description": "Select a time that works for you",
"calendarId": "cal_456",
"isActive": true,
"durations": [15, 30, 60],
"defaultDuration": 30,
"bufferTime": 10,
"timezone": "America/New_York",
"workingHours": {
"monday": { "start": "09:00", "end": "17:00" },
"tuesday": { "start": "09:00", "end": "17:00" },
// ... other days
}
}
}Create or update booking link.
Authentication: Required
Request Body:
{
"slug": "john-doe",
"title": "Book a Meeting with John",
"description": "Select a time that works for you",
"calendarId": "cal_456",
"isActive": true,
"durations": [15, 30, 60],
"defaultDuration": 30,
"bufferTime": 10,
"timezone": "America/New_York",
"workingHours": {
"monday": { "start": "09:00", "end": "17:00" },
"tuesday": { "start": "09:00", "end": "17:00" }
}
}Response:
{
"success": true,
"bookingLink": {
"id": "booking_123",
"slug": "john-doe",
// ... other fields
}
}Status Codes:
200- Success (created or updated)400- Invalid request409- Slug already taken
Get available time slots for booking.
Authentication: Public
Query Parameters:
slug(required): Booking link slugdate(required): Date to check (YYYY-MM-DD)duration(required): Meeting duration in minutes
Response:
{
"availableSlots": [
{
"start": "2024-01-20T09:00:00Z",
"end": "2024-01-20T09:30:00Z"
},
{
"start": "2024-01-20T10:00:00Z",
"end": "2024-01-20T10:30:00Z"
}
],
"timezone": "America/New_York"
}Create a new booking (public endpoint).
Authentication: Public
Request Body:
{
"slug": "john-doe",
"guestName": "Jane Smith",
"guestEmail": "jane@example.com",
"guestNotes": "Looking forward to discussing the project",
"startTime": "2024-01-20T10:00:00Z",
"duration": 30
}Response:
{
"success": true,
"booking": {
"id": "booking_456",
"startTime": "2024-01-20T10:00:00Z",
"endTime": "2024-01-20T10:30:00Z",
"meetLink": "https://meet.google.com/abc-defg-hij",
"guestName": "Jane Smith",
"guestEmail": "jane@example.com"
},
"message": "Booking confirmed! Check your email for details."
}Status Codes:
200- Success400- Invalid request404- Booking link not found409- Time slot unavailable (conflict)
All errors follow this format:
{
"error": "Error message",
"details": "Additional context (optional)"
}200- Success400- Bad Request (invalid input)401- Unauthorized (authentication required)403- Forbidden (insufficient permissions)404- Not Found (resource doesn't exist)409- Conflict (e.g., duplicate slug, time conflict)500- Internal Server Error
Missing Required Field:
{
"error": "Missing required field: title"
}Invalid Format:
{
"error": "Invalid date format",
"details": "Expected ISO 8601 format"
}Database Error:
{
"error": "Failed to create event",
"details": "Database connection error"
}{
"success": true,
"data": { /* resource data */ },
"message": "Optional success message"
}{
"items": [ /* array of resources */ ],
"total": 42,
"page": 1,
"pageSize": 20
}Not currently implemented. All list endpoints return full results.
All dates and times use ISO 8601 format:
2024-01-15T10:30:00Z # UTC
2024-01-15T10:30:00-05:00 # With timezone offset
- Dates stored in UTC in database
- API accepts ISO 8601 with timezone
- Responses include timezone information
- Booking API converts to user's timezone
Currently not implemented. For self-hosted single-user application, rate limiting is typically handled at reverse proxy level.
Not currently implemented. Future feature for real-time sync.
- Always handle errors: Check for error responses
- Validate input: Check required fields before sending
- Use ISO 8601: For all date/time values
- Include timezone: Explicitly specify timezone when creating events
- Check sync status: Before displaying calendar data
- HTTPS only: Use HTTPS in production
- Validate cookies: Don't manipulate auth cookies
- Sanitize input: Escape user input on frontend
- Rate limit: Implement at reverse proxy level
- CORS: Configure appropriately for your domain
- Cache responses: Cache calendar data on frontend
- Batch requests: Combine multiple operations when possible
- Use incremental sync: Prefer
forceFullSync: false - Optimize queries: Filter by date range when possible
// 1. Get available slots
const slots = await fetch(
`/api/booking-link/availability?slug=john-doe&date=2024-01-20&duration=30`
)
const { availableSlots } = await slots.json()
// 2. Create booking
const booking = await fetch('/api/booking-link/book', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
slug: 'john-doe',
guestName: 'Jane Smith',
guestEmail: 'jane@example.com',
startTime: availableSlots[0].start,
duration: 30
})
})
const result = await booking.json()
console.log(result.booking.meetLink)const event = await fetch('/api/calendar/events', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
calendarId: 'cal_456',
title: 'Team Sync',
startTime: '2024-01-20T14:00:00Z',
endTime: '2024-01-20T15:00:00Z',
addGoogleMeet: true,
attendees: ['team@example.com']
})
})
const { event: createdEvent } = await event.json()
console.log(createdEvent.meetLink)- Initial API release
- Calendar connection management
- Event CRUD operations
- Booking link system
- Authentication system
- Webhook support for real-time updates
- Batch event operations
- Recurring event management API
- Email notification triggers
- Calendar export (ICS format)
For implementation details, see ARCHITECTURE.md.
For troubleshooting, see TROUBLESHOOTING.md.