---
title: "List the workspace's services"
description: "Returns the workspace's services ordered by position then creation time."
api_method: GET
api_path: "/v1/services"
canonical_url: https://wiblo.app/docs/developers/api/services/list-services
last_updated: 2026-07-28T17:31:44+02:00
md_url: https://wiblo.app/docs/developers/api/services/list-services.md
---

# List the workspace's services

`GET /v1/services`

Returns the workspace's services ordered by position then creation time. Retired services are excluded unless `include_retired=true` (the services admin surface passes it; booking pickers must not). Available to any active member. Per-user rate-limited at 60/60s.

## Query parameters

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `include_retired` | `boolean` | No | Include retired services in the list. The services admin surface passes `true`; booking pickers must not, so retired services never reach a picker. Defaults to `false`. |

## Headers

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `X-Wiblo-Workspace` | `string` | Yes | Workspace id the call is scoped to. The actor must hold an active membership in it; a foreign or unknown id collapses onto 404 WORKSPACE_NOT_FOUND. |

## Request

**curl**

```bash
curl "https://api.wiblo.app/v1/services?include_retired=true" \
  -H "Authorization: Bearer $WIBLO_TOKEN" \
  -H "X-Wiblo-Workspace: $WIBLO_WORKSPACE_ID"
```

**TypeScript**

```ts
import { createSdk, listServices } from "@workspace/sdk"

const sdk = createSdk({ baseUrl: "https://api.wiblo.app" })

const { data, error } = await listServices({
  client: sdk,
  query: { include_retired: true },
  headers: { "X-Wiblo-Workspace": "..." },
})
```

## Example response

```json
{
  "services": [
    {
      "id": "3f8a2b9c-51d4-4e0b-9c6a-7d2e8f1a4b5c",
      "workspace_id": "9c1b7e24-6a3f-4d58-b2e9-0f4a8c6d1e37",
      "title": "Initial consultation",
      "slug": "initial-consultation",
      "description": "A 30-minute intro call to scope your project and answer questions.",
      "category": "Consultations",
      "hidden": false,
      "position": 1,
      "length": 30,
      "time_zone": null,
      "slot_interval": 30,
      "offset_start": 0,
      "price": 7500,
      "currency": "usd",
      "scheduling_type": "round_robin",
      "assign_all_team_members": false,
      "is_rr_weights_enabled": false,
      "reschedule_with_same_rr_host": true,
      "minimum_booking_notice": 120,
      "before_event_buffer": 10,
      "after_event_buffer": 10,
      "booking_limits": {
        "day": 4,
        "week": 20
      },
      "duration_limits": null,
      "max_active_bookings_per_booker": 3,
      "period_type": "rolling",
      "period_days": 60,
      "period_start_date": null,
      "period_end_date": null,
      "locations": [
        {
          "type": "video",
          "link": "https://meet.example.com/acme"
        }
      ],
      "recurring_event": null,
      "requires_confirmation": false,
      "requires_booker_email_verification": false,
      "disable_guests": false,
      "hide_calendar_notes": false,
      "hide_calendar_event_details": false,
      "hide_organizer_email": false,
      "lock_time_zone": false,
      "disable_cancelling": false,
      "disable_rescheduling": false,
      "seats_per_time_slot": null,
      "seats_show_attendees": false,
      "seats_show_availability_count": true,
      "color": {
        "light": "#0ea5e9",
        "dark": "#38bdf8"
      },
      "booking_fields": null,
      "metadata": null,
      "retired_at": null,
      "created_at": "2026-06-12T09:30:00.000Z",
      "updated_at": "2026-07-18T14:05:00.000Z",
      "tags": [
        {
          "id": "d4c8b1a2-3e5f-4a6b-8c9d-0e1f2a3b4c5d",
          "name": "Intro",
          "color": "#38bdf8"
        }
      ]
    },
    {
      "id": "b5e1c7d3-9a2f-4c8e-b0d6-3f7a1c5e9b2d",
      "workspace_id": "9c1b7e24-6a3f-4d58-b2e9-0f4a8c6d1e37",
      "title": "Legacy 60-minute session",
      "slug": "legacy-60-minute-session",
      "description": null,
      "category": "Consultations",
      "hidden": false,
      "position": 2,
      "length": 60,
      "time_zone": null,
      "slot_interval": null,
      "offset_start": 0,
      "price": 9000,
      "currency": "usd",
      "scheduling_type": null,
      "assign_all_team_members": false,
      "is_rr_weights_enabled": false,
      "reschedule_with_same_rr_host": false,
      "minimum_booking_notice": 120,
      "before_event_buffer": 0,
      "after_event_buffer": 0,
      "booking_limits": null,
      "duration_limits": null,
      "max_active_bookings_per_booker": null,
      "period_type": "unlimited",
      "period_days": null,
      "period_start_date": null,
      "period_end_date": null,
      "locations": [
        {
          "type": "in_person",
          "address": "12 Savile Row, London"
        }
      ],
      "recurring_event": null,
      "requires_confirmation": false,
      "requires_booker_email_verification": false,
      "disable_guests": false,
      "hide_calendar_notes": false,
      "hide_calendar_event_details": false,
      "hide_organizer_email": false,
      "lock_time_zone": false,
      "disable_cancelling": false,
      "disable_rescheduling": false,
      "seats_per_time_slot": null,
      "seats_show_attendees": false,
      "seats_show_availability_count": true,
      "color": null,
      "booking_fields": null,
      "metadata": null,
      "retired_at": "2026-05-30T11:00:00.000Z",
      "created_at": "2025-11-03T10:00:00.000Z",
      "updated_at": "2026-05-30T11:00:00.000Z",
      "tags": []
    }
  ]
}
```

## Responses

**`200`** — The workspace's services. Returns `ServiceListResponse`.

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `services` | `array<ServiceResponse>` | Yes | The workspace's services, ordered by position then creation time. |

**`400`** — A query parameter failed validation, or the `X-Wiblo-Workspace` header was missing or not a UUID (`VALIDATION_FAILED`). Returns `ApiErrorEnvelope`.

**`401`** — No valid session cookie or `wbl_*` bearer was present. Returns `ApiErrorEnvelope`.

**`404`** — The actor has no active membership in the header's workspace (`WORKSPACE_NOT_FOUND`). Returns `ApiErrorEnvelope`.

**`429`** — Rate limit exceeded. The body's `error.code` is `RATE_LIMITED` and `error.details.retry_after` is the same number of seconds as the `Retry-After` header. Returns `ApiErrorEnvelope`.

### The ServiceResponse object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | Yes | Unique id of the service. |
| `workspace_id` | `string` | Yes | Workspace the service belongs to. |
| `title` | `string` | Yes | Display name of the service. |
| `slug` | `string` | Yes | URL-safe identifier, unique within the workspace. |
| `description` | `string \| null` | Yes | Long-form description shown on the booking page. |
| `category` | `string \| null` | Yes | Free-form grouping label for the admin catalogue. |
| `hidden` | `boolean` | Yes | Hidden services never appear on public booking surfaces. |
| `position` | `integer` | Yes | Manual sort position; lists order by it, then creation time. |
| `length` | `integer` | Yes | Appointment length in minutes. |
| `time_zone` | `string \| null` | Yes | IANA time zone the service is anchored to; `null` follows the visitor. |
| `slot_interval` | `integer \| null` | Yes | Minutes between offered start times; `null` falls back to the service length. |
| `offset_start` | `integer` | Yes | Minutes past each interval boundary that slots start at. |
| `price` | `integer` | Yes | Price of the service; `0` means free. |
| `currency` | `string` | Yes | Lowercase 3-letter ISO 4217 currency code. |
| `scheduling_type` | `"round_robin" \| "collective" \| null` | Yes | Team scheduling mode — `round_robin` rotates one host per booking, `collective` books every host together. `null` means single-host 1-on-1. |
| `assign_all_team_members` | `boolean` | Yes | Every current and future team member is auto-assigned as a host. |
| `is_rr_weights_enabled` | `boolean` | Yes | Round-robin rotation is biased by each host's `weight`. |
| `reschedule_with_same_rr_host` | `boolean` | Yes | Reschedules keep the originally assigned round-robin host. |
| `minimum_booking_notice` | `integer` | Yes | Minimum lead time in minutes between booking and slot start. |
| `before_event_buffer` | `integer` | Yes | Minutes blocked off before each booking. |
| `after_event_buffer` | `integer` | Yes | Minutes blocked off after each booking. |
| `booking_limits` | `object` | No | Booking count caps (`BookingLimits` shape); `null` means uncapped. |
| `duration_limits` | `object` | No | Booked-minute caps (`DurationLimits` shape); `null` means uncapped. |
| `max_active_bookings_per_booker` | `integer \| null` | Yes | Cap on live bookings one booker can hold; `null` means uncapped. |
| `period_type` | `"unlimited" \| "rolling" \| "range"` | Yes | Which booking window applies — `unlimited` (any future slot), `rolling` (the next `period_days` days), or `range` (between `period_start_date` and `period_end_date`). |
| `period_days` | `integer \| null` | Yes | Size of the rolling window in days, when `period_type` is `rolling`. |
| `period_start_date` | `string \| null` | Yes | UTC start of the bookable range, when `period_type` is `range`. |
| `period_end_date` | `string \| null` | Yes | UTC end of the bookable range, when `period_type` is `range`. |
| `locations` | `object` | No | Locations offered at booking time (array of `ServiceLocation`). |
| `recurring_event` | `object` | No | Recurrence configuration (`RecurringEvent` shape); `null` for one-off services. |
| `requires_confirmation` | `boolean` | Yes | New bookings start pending until a host confirms them. |
| `requires_booker_email_verification` | `boolean` | Yes | Bookers must verify their email before the booking is accepted. |
| `disable_guests` | `boolean` | Yes | Bookers cannot add guest attendees. |
| `hide_calendar_notes` | `boolean` | Yes | Booking notes are hidden on synced calendar events. |
| `hide_calendar_event_details` | `boolean` | Yes | Event details are hidden on synced calendar events. |
| `hide_organizer_email` | `boolean` | Yes | The organizer's email is hidden on booking surfaces and notifications. |
| `lock_time_zone` | `boolean` | Yes | The booking page is pinned to `time_zone` instead of the visitor's local zone. |
| `disable_cancelling` | `boolean` | Yes | Bookers cannot cancel bookings of this service. |
| `disable_rescheduling` | `boolean` | Yes | Bookers cannot reschedule bookings of this service. |
| `seats_per_time_slot` | `integer \| null` | Yes | Attendee seats a single slot can hold; `null` means seated mode is off. |
| `seats_show_attendees` | `boolean` | Yes | Existing attendees are shown to prospective bookers. |
| `seats_show_availability_count` | `boolean` | Yes | The remaining-seat count is shown on the booking page. |
| `color` | `object` | No | Accent colour (`ServiceColor` shape); `null` uses the workspace default. |
| `booking_fields` | `object` | No | Custom booking-form field definitions; `null` when the default form is used. |
| `metadata` | `object` | No | Extensible metadata bag; `null` when unset. |
| `retired_at` | `string \| null` | Yes | When the service was retired; `null` for live services. |
| `created_at` | `string` | Yes | Creation timestamp (RFC 3339). |
| `updated_at` | `string` | Yes | Last-update timestamp (RFC 3339). |
| `tags` | `array<TagSummary>` | Yes | Workspace tags pinned to this service; `[]` when untagged. |

### The TagSummary object

| Field | Type | Required | Description |
| --- | --- | --- | --- |
| `id` | `string` | Yes | Unique id of the tag. |
| `name` | `string` | Yes | Display name, unique per workspace (case-insensitive). |
| `color` | `string \| null` | Yes | `#rrggbb` chip colour; `null` uses the default chip style. |
