Docs
Meetings

Book a meeting with a contact

POSThttps://api.norreach.app/v1/meetings

Books 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.

meetings:writeRole must allow: meetings.ownIdempotency-Key requiredSupports dry_runReaches peopleOperation id: book_meeting
POST/meetings
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"
}'
Book a meeting with a contact
{
  "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

Authorizationstringheaderrequired

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

dry_runbooleanquery

Run every check and return what would happen, changing nothing.

Headers

Idempotency-Keystringheaderrequired

Any unique string (≤ 255). The same key with the same request returns the first answer for 24 hours.

Body

application/json

contact_idstring (uuid)required

An id (UUID).

startstring (date-time)required

One of the slots[].start of GET /availability.

meeting_type_idstring (uuid)

An id (UUID).

host_idstring (uuid)

An id (UUID).

notesstring

Response

201application/json— Book a meeting with a contact

Errors

Errors are application/problem+json with a stable code.

StatusCodeMeaning
400bad_requestThe request couldn't be read.
400unknown_fieldThe request has a field this operation doesn't know.
400idempotency_key_requiredThis operation needs an Idempotency-Key header.
401unauthorizedA valid API key is required.
403insufficient_scopeThis API key doesn't have the scope this operation needs.
403feature_not_enabledThe API isn't switched on for this workspace.
403role_forbiddenThe key's creator's role can't do this.
404not_foundThat doesn't exist.
409idempotency_in_progressA request with this Idempotency-Key is still running.
409conflictThat conflicts with the current state.
409outreach_pausedOutreach is paused for this workspace.
413too_largeThe request body is too large.
422validation_failedThe request has invalid fields.
422idempotency_mismatchThis Idempotency-Key was used with a different request.
429rate_limitedToo many requests. Slow down and try again.
500server_errorSomething went wrong on our side.
503unavailableThe API isn't available right now. Try again shortly.