POST /v1/booking-proposals
requires akidempotentaudited decision
Create a durable booking proposal for calendar-owner approval.
- Proposal access is scoped to the internal agent. Multiple active API keys for the same agent can read the same proposal; a different agent cannot.
- Proposals expire at the earlier of 24 hours after creation or the end of the requested window.
- Broad requested windows return a curated review set, not exhaustive availability. Use preferred_times for up to 3 specific choices.
Request
POST /v1/booking-proposals HTTP/1.1
Authorization: Bearer ak_01HX7QQM…
Content-Type: application/json
{
"title": "Demo call with Acme",
"meeting_class": "recruiting_interview",
"duration_minutes": 45,
"requested_window": { "start": "2026-07-08T09:00:00Z", "end": "2026-07-08T17:00:00Z" },
"owner_email": "alex@acme.com",
"calendar_type": "work",
"description": "…"
}owner_emailcalendar_typetitlereqdescriptionmeeting_classreqduration_minutesreqattendeesattendees[].emailreqattendees[].displayNamerequested_windowreqrequested_window.startreqrequested_window.endreqpreferred_timespreferred_times[].startreqpreferred_times[].endreqResponse
Returns 201 Created with the response body below. Successful booking arbitration writes an audit row you can query through the audit log.
201 Created · example
HTTP/1.1 201 Created
Content-Type: application/json
{
"proposal_id": "7a8f9d77-c4b4-4f4d-96c6-4e41a82e408e",
"status": "pending_owner_decision",
"title": "Demo call with Acme",
"description": null,
"meeting_class": "demo",
"duration_minutes": 30,
"attendees": [{ "email": "guest@example.com" }],
"requested_window": {
"start": "2026-06-10T09:00:00Z",
"end": "2026-06-10T17:00:00Z"
},
"expires_at": "2026-06-11T09:00:00.000Z",
"calendar_owner": "owner@example.com",
"requesting_agent": "sales-agent",
"resolved_calendar_type": "work",
"candidate_limit": 20,
"available_valid_candidate_count": 42,
"valid_candidate_count": 20,
"candidates_truncated": true,
"candidate_set": "curated",
"candidates": [
{
"id": "2479f9ad-96cb-4f15-a2aa-1ad14220d94b",
"start": "2026-06-10T10:00:00.000Z",
"end": "2026-06-10T10:30:00.000Z",
"rank": 100,
"agent_preferred": false,
"status": "valid",
"invalid_reasons": [],
"risk": "free",
"preemptable": null
}
],
"decision": null,
"approved_candidate_id": null,
"rejection_reason": null,
"owner_note": null,
"booking_id": null
}
// Approval happens in Dashboard → Approvals. Agents poll
// GET /v1/booking-proposals/{id} or read pending notifications.Errors
| Code | Description |
|---|---|
201 | Default Response |
400 | Validation failed — body is malformed or missing required fields. |
401 | Missing or invalid API key. Send the raw key as a Bearer token in the Authorization header. |
403 | Agent does not hold the permission scope required for this call (e.g. preempt on a higher-priority class). |
404 | Calendar owner or booking id not found. |
409 | Conflict — the requested slot was already committed at higher priority. Inspect alternatives[] in the response body. |
422 | Validation error — the request body is structurally valid but semantically rejected. Common causes: earliest_start is in the past (PAST_TIME), the window is shorter than the requested duration (WINDOW_TOO_NARROW), the chosen slot is outside the hold window (SLOT_OUTSIDE_HOLD), or an idempotency key was reused with a different body (IDEMPOTENCY_CONFLICT). The error code field names the specific cause. |