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