> ## Documentation Index
> Fetch the complete documentation index at: https://docs.scan.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Submit a booking

> Ask Scan to choose and book the best live opening for an existing referral

This guide starts from a referral you already created. If you do not have a
`referral_id`, follow [Submit a medical referral](/guides/submit-a-medical-referral)
first.

Auto-book is an opt-in capability. A request returns `403` until Scan enables
`referrals_auto_book` for your account.

**What you send.** A ranking strategy, plus an optional radius, search address,
and completion URL.

**What Scan does.** We rank eligible centers, find live availability, and start
booking asynchronously. You do not select a timeslot.

## Choose a strategy

* `closest` — choose the nearest eligible center that has availability.
* `soonest` — choose the earliest opening within the radius.
* `closest_soonest` — choose the earliest nearby opening, using distance to
  break ties.

`radius` is measured in miles, defaults to `200`, and accepts `1` through `250`.

`address` is a free-form search origin such as
`200 Peachtree St, Atlanta, GA 30303`. Omit it to use the patient address. An
override changes this search only; it does not update the patient.

## Request the booking

**POST `/api/v2/referrals/{referral_id}/book`**

<CodeGroup>
  ```bash cURL theme={"dark"}
  curl -X POST \
    "https://api.staging.scan.com/api/v2/referrals/$REFERRAL_ID/book" \
    -H "Authorization: Bearer $SCAN_API_TOKEN" \
    -H "Content-Type: application/json" \
    -d '{
      "book": {
        "strategy": "closest",
        "radius": 25,
        "address": "200 Peachtree St, Atlanta, GA 30303",
        "webhook_url": "https://partner.example/hooks/scan-booking"
      }
    }'
  ```

  ```ruby Ruby theme={"dark"}
  require "json"
  require "net/http"
  require "uri"

  uri = URI("https://api.staging.scan.com/api/v2/referrals/#{ENV.fetch("REFERRAL_ID")}/book")
  request = Net::HTTP::Post.new(uri)
  request["Authorization"] = "Bearer #{ENV.fetch("SCAN_API_TOKEN")}"
  request["Content-Type"] = "application/json"
  request.body = {
    book: {
      strategy: "closest",
      radius: 25,
      address: "200 Peachtree St, Atlanta, GA 30303",
      webhook_url: "https://partner.example/hooks/scan-booking",
    },
  }.to_json

  http = Net::HTTP.new(uri.host, uri.port)
  http.use_ssl = true
  puts http.request(request).body
  ```

  ```python Python theme={"dark"}
  import os
  import requests

  response = requests.post(
      f"https://api.staging.scan.com/api/v2/referrals/{os.environ['REFERRAL_ID']}/book",
      headers={"Authorization": f"Bearer {os.environ['SCAN_API_TOKEN']}"},
      json={
          "book": {
              "strategy": "closest",
              "radius": 25,
              "address": "200 Peachtree St, Atlanta, GA 30303",
              "webhook_url": "https://partner.example/hooks/scan-booking",
          }
      },
  )
  print(response.json())
  ```

  ```javascript JavaScript theme={"dark"}
  const response = await fetch(
    `https://api.staging.scan.com/api/v2/referrals/${process.env.REFERRAL_ID}/book`,
    {
      method: "POST",
      headers: {
        Authorization: `Bearer ${process.env.SCAN_API_TOKEN}`,
        "Content-Type": "application/json",
      },
      body: JSON.stringify({
        book: {
          strategy: "closest",
          radius: 25,
          address: "200 Peachtree St, Atlanta, GA 30303",
          webhook_url: "https://partner.example/hooks/scan-booking",
        },
      }),
    },
  );
  const result = await response.json();
  ```

  ```php PHP theme={"dark"}
  $referralId = getenv("REFERRAL_ID");
  $body = json_encode([
    "book" => [
      "strategy" => "closest",
      "radius" => 25,
      "address" => "200 Peachtree St, Atlanta, GA 30303",
      "webhook_url" => "https://partner.example/hooks/scan-booking",
    ],
  ]);

  $ch = curl_init("https://api.staging.scan.com/api/v2/referrals/{$referralId}/book");
  curl_setopt_array($ch, [
    CURLOPT_POST => true,
    CURLOPT_HTTPHEADER => [
      "Authorization: Bearer " . getenv("SCAN_API_TOKEN"),
      "Content-Type: application/json",
    ],
    CURLOPT_POSTFIELDS => $body,
    CURLOPT_RETURNTRANSFER => true,
  ]);
  $response = curl_exec($ch);
  ```

  ```go Go theme={"dark"}
  package main

  import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
  )

  func main() {
    payload := map[string]any{"book": map[string]any{
      "strategy": "closest",
      "radius": 25,
      "address": "200 Peachtree St, Atlanta, GA 30303",
      "webhook_url": "https://partner.example/hooks/scan-booking",
    }}
    body, _ := json.Marshal(payload)
    url := fmt.Sprintf(
      "https://api.staging.scan.com/api/v2/referrals/%s/book",
      os.Getenv("REFERRAL_ID"),
    )
    request, _ := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(body))
    request.Header.Set("Authorization", "Bearer "+os.Getenv("SCAN_API_TOKEN"))
    request.Header.Set("Content-Type", "application/json")
    _, _ = http.DefaultClient.Do(request)
  }
  ```
</CodeGroup>

The API returns `202 Accepted` with an `auto_book_request.id`, a `processing`
status, and the current referral snapshot. This means work started; it does not
mean the visit is booked.

A referral accepts one booking attempt at a time. While an attempt is in flight,
or while Scan operations is booking the same visits, a second request returns
`422`.

## Receive completion

Booking can take several minutes. Scan sends
`X-Scan-Event: referral.auto_book_completed` after the attempt reaches a final
result:

```json theme={"dark"}
{
  "event_name": "referral.auto_book_completed",
  "occurred_at": "2026-09-18T23:05:00Z",
  "auto_book_request": {
    "id": "11111111-1111-1111-1111-111111111111",
    "status": "completed",
    "outcome": "booked",
    "strategy": "closest",
    "radius": 25,
    "address": "200 Peachtree St, Atlanta, GA 30303"
  },
  "referral": {
    "id": "22222222-2222-2222-2222-222222222222",
    "visits": [
      {
        "status": "booked",
        "start_time": "2026-09-22T14:30:00Z",
        "location": {
          "name": "Peachtree Imaging"
        }
      }
    ]
  }
}
```

Delivery order:

1. The request's `webhook_url`, when provided.
2. Your account's configured `api_credentials.webhook_url`.
3. No push when neither exists; poll `GET /api/v2/referrals/{referral_id}`
   and inspect `auto_book_request.status` and `auto_book_request.outcome`.

The completion outcome is one of `booked`, `contacted_for_booking`,
`no_availability`, or `booking_failed`. Return any HTTP `2xx` response.

The normal [status changed webhook](/webhooks/status-changed) remains active for
accounts with a configured webhook and continues to report later visit changes.

Full payload: [Requests automatic booking](/api-reference/referrals/requests-automatic-booking).
