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

# Find Mutual Availability

> Find time slots where all attendees are available

## Endpoint

```
POST https://api.syncline.run/v1/availability
```

## Request Body

<ParamField body="attendees" type="string[]" required>
  Array of email addresses. Must be exactly 2 for MVP.

  ```json theme={null}
  ["alice@example.com", "bob@example.com"]
  ```
</ParamField>

<ParamField body="duration_minutes" type="integer" default={30}>
  Meeting duration in minutes. Min: 15, Max: 120.
</ParamField>

<ParamField body="date_range" type="object">
  Optional custom date range. Defaults to user's `availability_window_days` preference (14 days).

  <Expandable title="properties">
    <ParamField body="start_date" type="string">
      ISO 8601 date: `2025-11-20`
    </ParamField>

    <ParamField body="end_date" type="string">
      ISO 8601 date: `2025-12-04`
    </ParamField>
  </Expandable>
</ParamField>

<ParamField body="context" type="string">
  Optional meeting context for better title generation.

  Example: `"Introduction call after LinkedIn connection"`
</ParamField>

## Response

<ResponseField name="slots" type="TimeSlot[]">
  Array of 5 time slots, ranked by quality (best first).

  <Expandable title="TimeSlot properties">
    <ResponseField name="start_time" type="string">
      ISO 8601 datetime with timezone
    </ResponseField>

    <ResponseField name="end_time" type="string">
      ISO 8601 datetime with timezone
    </ResponseField>

    <ResponseField name="score" type="number">
      Quality score (0-1). Higher = better match.
    </ResponseField>

    <ResponseField name="timezone" type="string">
      Requester's timezone (e.g., `America/Los_Angeles`)
    </ResponseField>

    <ResponseField name="reason" type="string">
      Human-readable explanation of why this slot scored well
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="total_found" type="integer">
  Total number of slots found (always 5 or less)
</ResponseField>

<ResponseField name="search_window" type="object">
  Date range searched

  <Expandable title="properties">
    <ResponseField name="start" type="string">
      Start date in ISO 8601 format
    </ResponseField>

    <ResponseField name="end" type="string">
      End date in ISO 8601 format
    </ResponseField>

    <ResponseField name="days" type="integer">
      Number of days in window
    </ResponseField>
  </Expandable>
</ResponseField>

## Example Request

<CodeGroup>
  ```bash cURL theme={null}
  curl -X POST https://api.syncline.run/v1/availability \
    -H "X-API-Key: sk_live_abc123..." \
    -H "Content-Type: application/json" \
    -d '{
      "attendees": ["alice@example.com", "bob@example.com"],
      "duration_minutes": 30,
      "context": "Introductory call"
    }'
  ```

  ```python Python theme={null}
  import requests

  response = requests.post(
      'https://api.syncline.run/v1/availability',
      headers={
          'X-API-Key': 'sk_live_abc123...',
          'Content-Type': 'application/json'
      },
      json={
          'attendees': ['alice@example.com', 'bob@example.com'],
          'duration_minutes': 30,
          'context': 'Introductory call'
      }
  )

  data = response.json()
  best_slot = data['slots'][0]  # First slot is best
  ```

  ```javascript JavaScript theme={null}
  const response = await fetch('https://api.syncline.run/v1/availability', {
    method: 'POST',
    headers: {
      'X-API-Key': 'sk_live_abc123...',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      attendees: ['alice@example.com', 'bob@example.com'],
      duration_minutes: 30,
      context: 'Introductory call'
    })
  });

  const data = await response.json();
  const bestSlot = data.slots[0];  // First slot is best
  ```
</CodeGroup>

## Example Response

```json theme={null}
{
  "slots": [
    {
      "start_time": "2025-11-20T10:00:00-08:00",
      "end_time": "2025-11-20T10:30:00-08:00",
      "score": 0.95,
      "timezone": "America/Los_Angeles",
      "reason": "Mid-morning, optimal timezone for both attendees"
    },
    {
      "start_time": "2025-11-21T10:30:00-08:00",
      "end_time": "2025-11-21T11:00:00-08:00",
      "score": 0.93,
      "timezone": "America/Los_Angeles",
      "reason": "Mid-morning, excellent availability"
    },
    {
      "start_time": "2025-11-20T14:00:00-08:00",
      "end_time": "2025-11-20T14:30:00-08:00",
      "score": 0.82,
      "timezone": "America/Los_Angeles",
      "reason": "Early afternoon, good match"
    },
    {
      "start_time": "2025-11-22T11:00:00-08:00",
      "end_time": "2025-11-22T11:30:00-08:00",
      "score": 0.80,
      "timezone": "America/Los_Angeles",
      "reason": "Late morning, acceptable for both"
    },
    {
      "start_time": "2025-11-20T16:00:00-08:00",
      "end_time": "2025-11-20T16:30:00-08:00",
      "score": 0.75,
      "timezone": "America/Los_Angeles",
      "reason": "Late afternoon, available but not ideal"
    }
  ],
  "total_found": 5,
  "search_window": {
    "start": "2025-11-15",
    "end": "2025-11-29",
    "days": 14
  }
}
```

## Error Responses

<Accordion title="Invalid Attendees">
  ```json theme={null}
  {
    "error": {
      "code": "invalid_attendees",
      "message": "Attendee list must contain exactly 2 email addresses",
      "field": "attendees"
    }
  }
  ```

  **Status**: `400 Bad Request`
</Accordion>

<Accordion title="User Not Found">
  ```json theme={null}
  {
    "error": {
      "code": "user_not_found",
      "message": "User alice@example.com has not connected their calendar",
      "field": "attendees",
      "email": "alice@example.com"
    }
  }
  ```

  **Status**: `404 Not Found`

  **Solution**: User must complete OAuth flow at syncline.run
</Accordion>

<Accordion title="No Availability Found">
  ```json theme={null}
  {
    "slots": [],
    "total_found": 0,
    "search_window": {
      "start": "2025-11-15",
      "end": "2025-11-29",
      "days": 14
    },
    "message": "No mutual availability found in 14-day window"
  }
  ```

  **Status**: `200 OK`

  **Note**: This is not an error. Try expanding the date range or adjusting preferences.
</Accordion>

## How Slots Are Ranked

Syncline uses a **smart ranking algorithm** with 5 factors:

<AccordionGroup>
  <Accordion title="1. Timezone Fairness" icon="earth-americas">
    Avoids times outside 8am-6pm for either person.

    **Score impact**: 0.5x penalty for awkward hours
  </Accordion>

  <Accordion title="2. Mid-Morning Bonus" icon="mug-hot">
    10am is the ideal meeting time for most professionals.

    **Score impact**: 1.2x boost for 10am slots
  </Accordion>

  <Accordion title="3. Friday Afternoon Penalty" icon="calendar-xmark">
    People protect Friday afternoons for focus work.

    **Score impact**: 0.7x penalty after 2pm on Fridays
  </Accordion>

  <Accordion title="4. Lunch Hour Penalty" icon="utensils">
    Noon-1pm is typically reserved for lunch.

    **Score impact**: 0.8x penalty for 12pm-1pm
  </Accordion>

  <Accordion title="5. Recency Bonus" icon="clock">
    Sooner is better—momentum matters for intros.

    **Score impact**: 1.1x boost for slots within 3 days
  </Accordion>
</AccordionGroup>

Final score = Base (1.0) × All factors

Learn more: [Smart Ranking Explained](/concepts/smart-ranking)

## Best Practices

<Check>
  **Always use the first slot**. It's ranked highest for a reason. Only offer alternatives if user explicitly requests them.
</Check>

<Check>
  **Cache results for 5 minutes**. Availability doesn't change that fast. Reduces API calls.
</Check>

<Check>
  **Show timezone to user**. Even though Syncline handles conversion, transparency builds trust.
</Check>

## Next Steps

<CardGroup cols={2}>
  <Card title="Schedule Meeting" href="/api-reference/schedule-meeting">
    Book one of the available slots
  </Card>

  <Card title="Smart Ranking" href="/concepts/smart-ranking">
    Deep dive into the ranking algorithm
  </Card>
</CardGroup>
