Skip to main content
GET
/
api
/
calendar
/
v1
/
public
/
availability
Get Availability
curl --request GET \
  --url https://api.example.com/api/calendar/v1/public/availability \
  --header 'x-kordless-key: <x-kordless-key>'
{
  "400": {},
  "401": {},
  "404": {},
  "429": {},
  "slots": [
    {
      "startsAt": "<string>",
      "endsAt": "<string>",
      "capacity": 123,
      "availableCapacity": 123,
      "serviceId": "<string>",
      "hostId": "<string>",
      "teamId": "<string>",
      "timezone": "<string>",
      "host": {},
      "team": {}
    }
  ]
}

Endpoint

GET https://api.kordless.ai/api/calendar/v1/public/availability
Returns available time slots for a specific service within a date range. Use this to display bookable times to customers before creating a booking.

Authentication

x-kordless-key
string
required
Your API key

Query Parameters

org_slug
string
required
Your organization slugExample: acme-salon
service_slug
string
required
The service slug to check availability forExample: haircut
from
string
required
Start of date range (ISO 8601 format, UTC)Example: 2025-12-01T00:00:00Z
to
string
required
End of date range (ISO 8601 format, UTC)Example: 2025-12-07T23:59:59Z
partySize
integer
Number of people/seats needed (default: 1)Example: 2
timezone
string
Client timezone for slot calculations (optional)Example: America/New_York

Response

slots
array
Array of available time slots

Examples

curl "https://api.kordless.ai/api/calendar/v1/public/availability?org_slug=acme-salon&service_slug=haircut&from=2025-12-01T00:00:00Z&to=2025-12-07T23:59:59Z&partySize=1" \
  -H "x-kordless-key: your_api_key"

Response Example

{
  "slots": [
    {
      "startsAt": "2025-12-01T09:00:00Z",
      "endsAt": "2025-12-01T10:00:00Z",
      "capacity": 1,
      "availableCapacity": 1,
      "serviceId": "haircut",
      "hostId": "host_sarah",
      "teamId": null,
      "timezone": "America/New_York",
      "host": {
        "id": "host_sarah",
        "displayName": "Sarah Johnson"
      },
      "team": null
    },
    {
      "startsAt": "2025-12-01T10:00:00Z",
      "endsAt": "2025-12-01T11:00:00Z",
      "capacity": 1,
      "availableCapacity": 1,
      "serviceId": "haircut",
      "hostId": "host_sarah",
      "teamId": null,
      "timezone": "America/New_York",
      "host": {
        "id": "host_sarah",
        "displayName": "Sarah Johnson"
      },
      "team": null
    },
    {
      "startsAt": "2025-12-01T14:00:00Z",
      "endsAt": "2025-12-01T15:00:00Z",
      "capacity": 2,
      "availableCapacity": 1,
      "serviceId": "haircut",
      "hostId": null,
      "teamId": "team_stylists",
      "timezone": "America/New_York",
      "host": null,
      "team": {
        "id": "team_stylists",
        "displayName": "Stylists Team"
      }
    }
  ]
}

Building a Time Slot Picker

async function TimeSlotPicker({ orgSlug, serviceSlug, onSelect }) {
  const [selectedDate, setSelectedDate] = useState(new Date());
  const [slots, setSlots] = useState([]);
  const [loading, setLoading] = useState(false);

  useEffect(() => {
    async function loadSlots() {
      setLoading(true);

      const dayStart = new Date(selectedDate);
      dayStart.setHours(0, 0, 0, 0);

      const dayEnd = new Date(selectedDate);
      dayEnd.setHours(23, 59, 59, 999);

      const { slots } = await getAvailability({
        orgSlug,
        serviceSlug,
        from: dayStart.toISOString(),
        to: dayEnd.toISOString()
      });

      setSlots(slots);
      setLoading(false);
    }

    loadSlots();
  }, [selectedDate, orgSlug, serviceSlug]);

  return (
    <div className="slot-picker">
      <DatePicker
        value={selectedDate}
        onChange={setSelectedDate}
      />

      {loading && <Spinner />}

      {!loading && slots.length === 0 && (
        <p>No availability on this date. Try another day.</p>
      )}

      <div className="slots-grid">
        {slots.map(slot => (
          <button
            key={slot.startsAt}
            onClick={() => onSelect(slot)}
            className="slot-button"
          >
            {formatTime(slot.startsAt)}
            {slot.host && <span className="host">{slot.host.displayName}</span>}
          </button>
        ))}
      </div>
    </div>
  );
}

Understanding Availability Hierarchy

Kordless uses a hierarchy for availability:
  1. Individual (Host) - Specific staff member’s availability
  2. Team - Team-level availability
  3. Business - Default business hours
The API automatically returns the most specific availability:
  • If hosts have set their own hours, those are returned
  • If no individual hours, team availability is returned
  • If no team availability, business hours are returned
// Slots will include host/team information
const { slots } = await getAvailability({...});

// Filter by host if needed
const sarahSlots = slots.filter(slot => slot.host?.displayName === 'Sarah Johnson');

// Or filter to team-only availability
const teamSlots = slots.filter(slot => slot.team && !slot.host);

Common Use Cases

Get Today’s Availability

const today = new Date();
today.setHours(0, 0, 0, 0);

const tomorrow = new Date(today);
tomorrow.setDate(tomorrow.getDate() + 1);

const { slots } = await getAvailability({
  orgSlug: 'acme-salon',
  serviceSlug: 'haircut',
  from: today.toISOString(),
  to: tomorrow.toISOString()
});

Get a Week of Availability

const startOfWeek = new Date();
const endOfWeek = new Date();
endOfWeek.setDate(endOfWeek.getDate() + 7);

const { slots } = await getAvailability({
  orgSlug: 'acme-salon',
  serviceSlug: 'haircut',
  from: startOfWeek.toISOString(),
  to: endOfWeek.toISOString()
});

// Group by day for display
const slotsByDay = slots.reduce((acc, slot) => {
  const day = slot.startsAt.split('T')[0];
  if (!acc[day]) acc[day] = [];
  acc[day].push(slot);
  return acc;
}, {});

Check Group Availability

// For a party of 4
const { slots } = await getAvailability({
  orgSlug: 'restaurant',
  serviceSlug: 'dinner-reservation',
  from: '2025-12-03T00:00:00Z',
  to: '2025-12-03T23:59:59Z',
  partySize: 4
});

// Only slots with enough capacity are returned

Best Practices

Query only the dates you need:
// ✅ Good - one week at a time
const { slots } = await getAvailability({
  from: '2025-12-01T00:00:00Z',
  to: '2025-12-07T23:59:59Z',
  ...
});

// ❌ Bad - entire month (slow)
const { slots } = await getAvailability({
  from: '2025-12-01T00:00:00Z',
  to: '2025-12-31T23:59:59Z',
  ...
});
Cache availability for short periods:
const CACHE_TTL = 60 * 1000; // 1 minute
const cache = new Map();

async function getCachedAvailability(params) {
  const key = JSON.stringify(params);
  const cached = cache.get(key);

  if (cached && Date.now() - cached.time < CACHE_TTL) {
    return cached.data;
  }

  const data = await getAvailability(params);
  cache.set(key, { data, time: Date.now() });
  return data;
}
Always handle no availability gracefully:
const { slots } = await getAvailability({...});

if (slots.length === 0) {
  showMessage({
    type: 'info',
    title: 'No availability',
    message: 'No times available for this period. Try a different date.'
  });
  return;
}
Convert UTC to the user’s timezone:
const { slots } = await getAvailability({...});
const userTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone;

const displaySlots = slots.map(slot => ({
  ...slot,
  displayTime: new Date(slot.startsAt).toLocaleTimeString('en-US', {
    timeZone: userTimezone,
    hour: 'numeric',
    minute: '2-digit'
  })
}));

Errors

400
Bad Request
Missing required parameters
{
  "detail": "org_slug is required"
}
401
Unauthorized
Invalid or missing API key
{
  "detail": "Missing x-kordless-key header"
}
404
Not Found
Organization or service not found
{
  "detail": "Service not found"
}
429
Too Many Requests
Rate limit exceeded
Learn more about Error Handling

Create Booking

Book an available slot

Get Organization

Get services to check availability for

Get Service

Get service details

Bookings Overview

Learn about bookings