# POST /scheduling-bookings

**Resource:** [Scheduling Bookings](./scheduling-bookings.md)  
**Scopes:** `company:write`  
**Write operation:** yes

Create a booking. Auto-creates CRM opportunity + contact (response field names like deal_id are preserved for backward compatibility). Flat request body (no nested objects). Email format is validated before slot logic runs -- malformed addresses (missing TLD, no domain) return INVALID_EMAIL; invalid attendee emails return INVALID_ATTENDEE_EMAIL. On slot conflict, returns SLOT_UNAVAILABLE error with nearest alternatives. Accepts event type by ID, slug, or name.

## Parameters

| Name | In | Type | Required | Description |
|------|----|------|----------|-------------|
| `event_type_id` | body | string | no | Event type UUID. Or use event_type_slug or event_type_name. |
| `event_type_slug` | body | string | no | Event type slug (e.g. "30-minute-booking"). Alternative to ID. |
| `event_type_name` | body | string | no | Event type name (fuzzy matched). Alternative to ID. |
| `date` | body | string | yes | Booking date YYYY-MM-DD |
| `time` | body | string | yes | Booking time HH:MM (24h format) |
| `timezone` | body | string | no | Customer (booker) timezone. Authoritative for converting date+time to UTC. Default: Australia/Sydney. Accepts IANA names (e.g. "Australia/Perth") or shorthands (e.g. "Perth", "AWST", "WA"). |
| `fullName` | body | string | yes | Customer full name |
| `email` | body | string | yes | Customer email. Must be a valid format with a complete TLD (e.g. "name@example.com"). Truncated addresses like "doug@resolvency." are rejected with INVALID_EMAIL before slot validation runs. |
| `phone` | body | string | no | Customer phone |
| `message` | body | string | no | Customer notes |
| `company_name` | body | string | no | Customer company (used for CRM account) |
| `booker_state` | body | string | no | Customer state/region (e.g. "WA", "NSW"). Written to CRM contact state field if currently empty. |
| `booker_timezone` | body | string | no | Explicit CRM timezone to store on the contact (e.g. "Australia/Perth"). Falls back to timezone if omitted. Written to CRM contact timezone field if currently empty. |
| `attendees` | body | array | no | Additional attendees: [{ "email": "...", "name": "...", "phone": "..." }]. All receive calendar invites. Each attendee email must also be a valid format; invalid entries return INVALID_ATTENDEE_EMAIL. |

## Request example

```bash
curl -X POST \
  "https://api.trustpager.com/functions/v1/api/v1/scheduling-bookings" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "event_type_slug": "30-minute-booking",
    "date": "2026-04-01",
    "time": "10:00",
    "timezone": "Australia/Sydney",
    "fullName": "John Smith",
    "email": "john@example.com",
    "phone": "+61412345678"
  }'
```

## Response example

```json
{
  "data": {
    "success": true,
    "booking": {
      "id": "b1c2d3e4-...",
      "event_type_name": "30 Minute Booking",
      "date": "2026-04-01",
      "time": "10:00",
      "formatted": "Tuesday, 1 April 2026 at 10:00 AM",
      "duration_minutes": 30,
      "status": "confirmed",
      "google_meet_link": "https://meet.google.com/abc-defg-hij",
      "attendees": [{ "email": "colleague@example.com", "name": "Jane Doe" }]
    },
    "crm": {
      "deal_id": "d1e2f3g4-...",
      "contact_id": "c1d2e3f4-...",
      "customer_id": "u1v2w3x4-..."
    }
  }
}
```

---
Base URL: `https://api.trustpager.com/functions/v1/api/v1` — Auth: `Authorization: Bearer YOUR_API_KEY`