# List bookings API with GET /api/v1/bookings

> The Mujib list bookings API returns appointments by branch-local day and status, page by page, in local time and UTC. Examples in curl, JavaScript, Python.

The list bookings API is `GET /api/v1/bookings`: it returns your business's appointments in time order, each with its local time in its branch and its UTC instant, selected by day and by status. It needs a key with the `bookings.read` scope.

## What does the bookings list return?

It returns one page of bookings in `data`, from the earliest appointment to the latest, with a `nextCursor` for the next page, or `null` on the last page.

- Without `from` and `to`, you get every booking from the earliest, past and upcoming.
- Appointments booked by the agent or your team in Mujib are included. Historical rows imported from an old register are not part of the API.
- **Read bookings** is offered when you create a key only if your business uses appointments.

## Which parameters does it take?

All parameters are optional, and any other parameter returns a 400:

| Parameter | Type | Meaning |
| --- | --- | --- |
| `from` | date `YYYY-MM-DD` | First day, included |
| `to` | date `YYYY-MM-DD` | Last day, included |
| `status` | string | One status only (see the next table) |
| `limit` | integer | Bookings per page: 50 by default, up to 100 |
| `cursor` | string | The previous page's `nextCursor`. See [cursor pagination](https://docs.mujib.ai/en/developers/pagination/) |

- `from` and `to` are days in each branch's calendar and time zone, not UTC. An appointment at ten in the morning in your branch falls on its day as you see it in the dashboard.
- A date must be a real day: `2026-02-31` gets a 400, and so does a `from` after `to`.
- For a single day's bookings, set `from` and `to` to the same day.

`status` values, and what the dashboard calendar calls them:

| Value | In the dashboard |
| --- | --- |
| `requested` | **Pending** |
| `confirmed` | **Confirmed** |
| `cancelled` | **Cancelled** |
| `completed` | **Attended** |
| `no_show` | **No-show** |

## How do I send the request?

This example asks for a month of confirmed bookings:

```bash
curl "https://api.mujib.ai/api/v1/bookings?from=2026-11-01&to=2026-11-30&status=confirmed&limit=100" \
  -H "Authorization: Bearer $MUJIB_API_KEY"
```

```js
const url = new URL('https://api.mujib.ai/api/v1/bookings');
url.searchParams.set('from', '2026-11-01');
url.searchParams.set('to', '2026-11-30');
url.searchParams.set('status', 'confirmed');
url.searchParams.set('limit', '100');

const res = await fetch(url, {
  headers: { Authorization: `Bearer ${process.env.MUJIB_API_KEY}` },
});
const body = await res.json();
if (!res.ok) throw new Error(`${res.status} ${body.error.code}: ${body.error.message}`);
for (const b of body.data) console.log(b.localTime, b.branch?.name, b.customer.name);
```

```python
import os
import requests

res = requests.get(
    "https://api.mujib.ai/api/v1/bookings",
    params={"from": "2026-11-01", "to": "2026-11-30", "status": "confirmed", "limit": 100},
    headers={"Authorization": f"Bearer {os.environ['MUJIB_API_KEY']}"},
    timeout=30,
)
body = res.json()
if not res.ok:
    raise RuntimeError(f"{res.status_code} {body['error']['code']}: {body['error']['message']}")
for b in body["data"]:
    print(b["localTime"], (b["branch"] or {}).get("name"), b["customer"]["name"])
```

To read every page to the end, use the function in [cursor pagination](https://docs.mujib.ai/en/developers/pagination/).

## What does the response look like?

A 200. The data is made up, for a branch in Amman:

```json
{
  "data": [
    {
      "id": "3f0c9a52-7d1e-4b8a-9c41-2e6f8a1b0d77",
      "status": "confirmed",
      "source": "agent",
      "service": "consultation",
      "startsAt": "2026-11-12T07:30:00Z",
      "localTime": "2026-11-12T10:30:00",
      "timezone": "Asia/Amman",
      "durationMinutes": 30,
      "branch": { "id": "e7f6a5b4-3c2d-4e1f-9a8b-7c6d5e4f3a21", "name": "الفرع الرئيسي" },
      "specialist": "ليلى",
      "customer": { "contactId": "5d8f2a61-0c4e-4b7a-9f13-6e2b8c0d4a57", "name": "سارة", "phone": "962790000000" },
      "createdAt": "2026-11-04T18:22:09.117Z"
    },
    {
      "id": "c2a7e915-6f3b-4d80-b4e1-7a9d5c0f2b36",
      "status": "requested",
      "source": "staff",
      "service": "followup",
      "startsAt": "2026-11-12T09:00:00Z",
      "localTime": "2026-11-12T12:00:00",
      "timezone": "Asia/Amman",
      "durationMinutes": null,
      "branch": { "id": "e7f6a5b4-3c2d-4e1f-9a8b-7c6d5e4f3a21", "name": "الفرع الرئيسي" },
      "specialist": null,
      "customer": { "contactId": null, "name": "أحمد", "phone": "0790000000" },
      "createdAt": "2026-11-05T08:01:44.360Z"
    }
  ],
  "nextCursor": "MjAyNi0xMS0xMlQxMjowMDowMHxjMmE3ZTkxNS02ZjNiLTRkODAtYjRlMS03YTlkNWMwZjJiMzY"
}
```

Every booking always carries these fields; a field with no value comes as `null`:

| Field | Type | Meaning |
| --- | --- | --- |
| `id` | string | The booking's stable id, for [get a booking](https://docs.mujib.ai/en/developers/api-booking/) |
| `status` | string | The booking's status (see the table above) |
| `source` | string | Who made it: `agent`, the agent in a conversation, or `staff`, your team in the dashboard |
| `service` | string | The visit type's id as your business defined it, such as `consultation`, not its display name. See [visit types](https://docs.mujib.ai/en/bookings/visit-types/) |
| `startsAt` | UTC instant | The start in UTC, as `YYYY-MM-DDTHH:MM:SSZ` |
| `localTime` | string | The start on the branch's clock, with no offset, as `YYYY-MM-DDTHH:MM:SS`. This is the time the customer was given |
| `timezone` | string | The time zone of `localTime`, such as `Asia/Amman`: the branch's zone, else your business's |
| `durationMinutes` | integer or `null` | Length in minutes, or `null` when your business's default length applies |
| `branch` | object or `null` | The branch, with `id` and `name`, or `null` when the booking has none |
| `specialist` | string or `null` | The specialist's name, or `null` when none is assigned |
| `customer.contactId` | string or `null` | The customer's id for [get a contact](https://docs.mujib.ai/en/developers/api-contact/), or `null` for a booking without a registered customer |
| `customer.name` | string or `null` | The name written on the booking, else the customer's name |
| `customer.phone` | string or `null` | The number written on the booking, else the customer's number |
| `createdAt` | UTC instant | When the booking was made |

## What errors can it return?

| HTTP | `code` | Why |
| --- | --- | --- |
| 400 | `VALIDATION_ERROR` | An unknown parameter, a date not in `YYYY-MM-DD` or not a real day, `from` after `to`, an unknown `status`, a `limit` out of range, or a cursor that cannot be read |
| 401 | `UNAUTHORIZED` | No key, or a key that is not valid |
| 403 | `FORBIDDEN` | The key lacks `bookings.read` |
| 429 | `RATE_LIMITED` | You went over a rate limit |
| 500 | `INTERNAL` | An unexpected server error |

The details are in [API errors and rate limits](https://docs.mujib.ai/en/developers/errors-limits/).

## Frequently asked questions

### Are from and to in UTC?

No. They are calendar days in each branch's own time zone, and both days are included. startsAt in the response is a UTC instant.

### Do old bookings I imported from a previous register show up?

No. Historical rows imported from an old register are not part of the API. Appointments booked in Mujib are.

### What does a null durationMinutes mean?

The booking carries no length of its own, so your business's default length applies.

### Can the list tell me which bookings changed recently?

There is no parameter for that. Read the window of days you care about again, or use webhooks to hear about each new or cancelled booking as it happens.

---

Source: https://docs.mujib.ai/en/developers/api-bookings/ · Last updated: 2026-11-04
