# Slotflow — Complete API Reference for AI Agents > Slotflow is scheduling infrastructure for AI agents. REST API at https://api.slotflow.dev/v1. Authenticate with Bearer token: `Authorization: Bearer sk_live_xxx`. ## Authentication All requests require an API key: ``` Authorization: Bearer sk_live_xxxxxxxxxxxxxxxxxxxx ``` ## Endpoints ### POST /v1/humans Create a human (person whose time can be booked). Request: ```json { "name": "Sarah Chen", "email": "sarah@acme.com", "role": "sales_rep", "timezone": "America/New_York" } ``` Required: name, timezone. Optional: email, role. Response 201: ```json { "id": "uuid", "name": "Sarah Chen", "email": "sarah@acme.com", "role": "sales_rep", "timezone": "America/New_York", "active": true, "created_at": "2026-03-10T10:00:00.000Z" } ``` ### GET /v1/humans List all active humans. Response 200: ```json { "humans": [{ "id": "uuid", "name": "...", "timezone": "...", "active": true, "created_at": "..." }], "total": 1 } ``` ### GET /v1/humans/:id Get a human with availability rules. Response 200: ```json { "id": "uuid", "name": "...", "timezone": "...", "active": true, "availability_rules": { "working_days": [1,2,3,4,5], "work_start": "09:00", "work_end": "17:00", "meeting_durations": [30,60] }, "created_at": "..." } ``` availability_rules is null if not configured. ### DELETE /v1/humans/:id Soft delete (sets active=false, preserves bookings). Response 200: `{ "deleted": true, "id": "uuid" }` ### PUT /v1/humans/:id/availability Set availability rules (full replace). Request: ```json { "working_days": [1,2,3,4,5], "work_start": "09:00", "work_end": "17:00", "meeting_durations": [30,60] } ``` All fields required. working_days: 1=Monday through 7=Sunday. Times in HH:MM in human's timezone. Response 200: Human object with availability_rules populated. ### POST /v1/humans/:id/overrides Create a schedule override. Blocks remove availability; opens add extra availability. All-day block example: ```json { "type": "block", "title": "Vacation", "all_day": true, "start_date": "2026-12-23", "end_date": "2026-12-27" } ``` Time-specific recurring block: ```json { "type": "block", "title": "Team standup", "all_day": false, "start_date": "2026-03-01", "start_time": "14:00", "end_time": "15:00", "recurrence": { "freq": "weekly", "days": [2] } } ``` Open override (extra availability): ```json { "type": "open", "title": "Saturday overtime", "all_day": false, "start_date": "2026-03-01", "start_time": "10:00", "end_time": "14:00", "recurrence": { "freq": "weekly", "days": [6] } } ``` Required: type (block|open), title, all_day, start_date. When all_day=false: start_time, end_time required. Optional: end_date, recurrence ({ freq: "weekly"|"monthly", interval, days, day_of_month, week, day }). Response 201: Override object with id, human_id, type, title, all_day, start_date, end_date, start_time, end_time, recurrence, created_at. ### GET /v1/humans/:id/overrides List all schedule overrides. Response 200: `{ "overrides": [...], "total": 5 }` ### DELETE /v1/humans/:id/overrides/:overrideId Delete a schedule override. Response 200: `{ "deleted": true, "id": "uuid" }` ### GET /v1/humans/:id/slots — CORE ENDPOINT Get available booking slots. This is what your agent calls to find available times. Query params (all required): date_from (YYYY-MM-DD), date_to (YYYY-MM-DD), duration (integer, minutes). Example: GET /v1/humans/uuid/slots?date_from=2026-03-16&date_to=2026-03-20&duration=30 Response 200: ```json { "human_id": "uuid", "duration": 30, "timezone": "America/New_York", "slots": [{ "starts_at": "2026-03-16T14:00:00.000Z", "ends_at": "2026-03-16T14:30:00.000Z" }], "total": 16 } ``` All times in UTC. timezone field is informational for display. Empty slots array (not error) when no availability. Duration must match one of human's meeting_durations. Slot computation: base schedule → apply block overrides → add open overrides → deduplicate → filter confirmed bookings → return available. ### POST /v1/bookings — BOOKING ENDPOINT Create a booking. Always call GET /slots first. Request: ```json { "human_id": "uuid", "starts_at": "2026-03-16T14:00:00.000Z", "duration": 30, "attendee_name": "Alex Rivera", "attendee_email": "alex@startup.io", "metadata": { "lead_id": "lead_123", "conversation_id": "conv_456" } } ``` Required: human_id, starts_at, duration, attendee_name. Optional: attendee_email, metadata (arbitrary JSON). Response 201: ```json { "id": "uuid", "human_id": "uuid", "starts_at": "2026-03-16T14:00:00.000Z", "ends_at": "2026-03-16T14:30:00.000Z", "duration_minutes": 30, "attendee_name": "Alex Rivera", "attendee_email": "alex@startup.io", "status": "confirmed", "metadata": { "lead_id": "lead_123" }, "created_at": "..." } ``` Error 409 SLOT_UNAVAILABLE: Slot booked by another request. Retry with next slot. ### GET /v1/bookings List bookings with filters. Query params (all optional): human_id (uuid), status (confirmed|cancelled), date_from (YYYY-MM-DD), date_to (YYYY-MM-DD), limit (default 50, max 100), offset (default 0). Response 200: `{ "bookings": [...], "total": 847, "limit": 50, "offset": 0 }` ### DELETE /v1/bookings/:id Cancel a booking (sets status=cancelled, fires booking.cancelled webhook). Response 200: `{ "id": "uuid", "status": "cancelled" }` ### POST /v1/webhooks Register a webhook URL. Request: ```json { "url": "https://your-agent.com/webhooks", "events": ["booking.confirmed", "booking.cancelled"] } ``` Required: url, events (non-empty array). Valid events: booking.confirmed, booking.cancelled. Response 201: `{ "id": "uuid", "url": "...", "events": [...], "active": true, "created_at": "..." }` ### GET /v1/webhooks List registered webhooks. ### DELETE /v1/webhooks/:id Remove a webhook. ## Webhook Payload Format POSTed to your URL on booking events: ```json { "event": "booking.confirmed", "created_at": "2026-03-10T14:05:00.000Z", "data": { "id": "uuid", "human_id": "uuid", "starts_at": "...", "ends_at": "...", "duration_minutes": 30, "attendee_name": "...", "attendee_email": "...", "status": "confirmed", "metadata": { "lead_id": "..." } } } ``` Retry schedule: immediate → 5 min → 60 min. Max 3 attempts. Success = 2xx response. ## Error Codes | Code | Status | When | |------|--------|------| | UNAUTHORIZED | 401 | Invalid API key | | NOT_FOUND | 404 | Resource not found | | SLOT_UNAVAILABLE | 409 | Slot booked by another request (retry with next slot) | | INVALID_DURATION | 422 | Duration not in meeting_durations | | OUTSIDE_WORKING_HOURS | 422 | Slot outside schedule (use GET /slots first) | | SCHEDULE_BLOCKED | 422 | Slot in block override (use GET /slots first) | | LIMIT_REACHED | 402 | Booking quota exhausted | | VALIDATION_ERROR | 422 | Missing/malformed fields | Error format: `{ "error": { "code": "...", "message": "...", "status": 123 } }` ## Agent Best Practices 1. Always call GET /slots before POST /bookings 2. Handle 409 SLOT_UNAVAILABLE with retry (re-query slots, try next one) 3. Use metadata to pass workflow context (lead_id, ticket_id, conversation_id) 4. Check error code field (stable), not message field (may change) 5. Slots are returned in UTC — convert to local time for display using the timezone field ## Plans - Free: $0/mo, 100 bookings, 1 human, 2 webhooks - Starter: $79/mo, 1,000 bookings, 5 humans, 10 webhooks - Growth: $299/mo, 10,000 bookings, 25 humans, unlimited webhooks - Overage: $19/500 bookings (auto-purchased on paid plans with auto-purchase enabled)