Book a meeting with a contact
https://api.norreach.app/v1/meetingsBooks a SAVED contact (with an email address) at one of the free times GET /availability lists, through your booking page: the time is checked again against the hosts' calendars and reserved in one step (two bookings can't take it), the host's calendar gets the event, the contact gets the invitation and their running sequences end. It waits for a person's yes when the workspace's assisted bookings do (see GET /booking-pages). Without meeting_type_id the page's default type; without host_id the type's own rule picks a free host. Members who aren't owners, admins or managers book only meetings they host. A time that isn't free (any more) → 409 conflict with conflict.choices.
curl -X POST "https://api.norreach.app/v1/meetings" \
-H "Authorization: Bearer $NORREACH_API_KEY" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"contact_id": "7d1c3b2a-5e4f-4a1b-9c8d-0e1f2a3b4c5d",
"start": "2026-11-12T09:00:00.000Z",
"meeting_type_id": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e"
}'{
"id": "4a5b6c7d-8e9f-4a0b-9c1d-2e3f4a5b6c7d",
"object": "meeting",
"title": "Intro call: Anna Lindqvist (Example Care AB)",
"status": "pending_approval",
"starts_at": "2026-11-12T09:00:00.000Z",
"ends_at": "2026-11-12T09:30:00.000Z",
"duration_minutes": 30,
"host_id": "0b6f8a52-8d9e-4c1a-b7f3-5e2d9c4a1b00",
"contact_id": "7d1c3b2a-5e4f-4a1b-9c8d-0e1f2a3b4c5d",
"meeting_type_id": "5b6c7d8e-9f0a-4b1c-8d2e-3f4a5b6c7d8e",
"source": "nora",
"attendee": {
"name": "Anna Lindqvist",
"email": "anna@example.com",
"timezone": "Europe/Stockholm",
"company": "Example Care AB"
},
"location_kind": "video",
"join_url": null,
"campaign_id": null,
"created_at": "2026-11-05T10:00:00.000Z",
"updated_at": "2026-11-05T10:00:00.000Z"
}Authorizations
Bearer authentication: send Authorization: Bearer <key>, with an API key or an OAuth access token. The key needs the scope meetings:write. API keys and scopes
Query parameters
Run every check and return what would happen, changing nothing.
Headers
Any unique string (≤ 255). The same key with the same request returns the first answer for 24 hours.
Body
application/json
An id (UUID).
One of the slots[].start of GET /availability.
An id (UUID).
An id (UUID).
Response
201application/json— Book a meeting with a contact
Errors
Errors are application/problem+json with a stable code.
| Status | Code | Meaning |
|---|---|---|
| 400 | bad_request | The request couldn't be read. |
| 400 | unknown_field | The request has a field this operation doesn't know. |
| 400 | idempotency_key_required | This operation needs an Idempotency-Key header. |
| 401 | unauthorized | A valid API key is required. |
| 403 | insufficient_scope | This API key doesn't have the scope this operation needs. |
| 403 | feature_not_enabled | The API isn't switched on for this workspace. |
| 403 | role_forbidden | The key's creator's role can't do this. |
| 404 | not_found | That doesn't exist. |
| 409 | idempotency_in_progress | A request with this Idempotency-Key is still running. |
| 409 | conflict | That conflicts with the current state. |
| 409 | outreach_paused | Outreach is paused for this workspace. |
| 413 | too_large | The request body is too large. |
| 422 | validation_failed | The request has invalid fields. |
| 422 | idempotency_mismatch | This Idempotency-Key was used with a different request. |
| 429 | rate_limited | Too many requests. Slow down and try again. |
| 500 | server_error | Something went wrong on our side. |
| 503 | unavailable | The API isn't available right now. Try again shortly. |