# Events V1 — Developer Guide

> **Reference:** [Events (V1) Endpoint Documentation](https://papidocs.hs1api.com/publicapi/api-appointment-management/events-(v1))


Events represent non-patient schedule blocks on the office calendar — team meetings, training sessions, closures, and similar items. They are distinct from **AppointmentV1** records, which are tied to patients and clinical workflows.

All Events V1 endpoints require an `Organization-ID` header. Read operations require the `model:read` scope; write operations (POST, PUT, DELETE) require `model:write`.

> **⚠️ Rate Limited:** Events endpoints are rate limited. See the [rate limiting guide](https://papidocs.hs1api.com/publicapi/api-consumer-guide#rate-limiting) for details.


## GET — Bulk List Events

Returns a paginated list of events matching the provided filter criteria.

### Parameters

| Parameter | In | Required | Type | Description |
|  --- | --- | --- | --- | --- |
| `Organization-ID` | header | Yes | string | Your organization identifier |
| `filter` | query | No | string | Filter expression. See [Filter Keys](#filter-keys) below |
| `responseFields` | query | No | string | URL-encoded comma-delimited list of fields to return. Use `ALL` for all fields |
| `page` | query | No | string | Page number for offset-based pagination |
| `pageSize` | query | No | string | Number of records per page |
| `lastId` | query | No | string | The largest `eventId` from the previous page — used for cursor-based pagination |


### Filter Keys

| Filter Key | Supported Operators |
|  --- | --- |
| `id` | `->` |
| `title` | `==`, `!=`, `~=`, `<>=` |
| `start` | `>`, `>=`, `<`, `<=` |
| `operatory.id` | `==`, `!=`, `->` |
| `location.id` | `==`, `!=`, `->` |
| `provider.id` | `==`, `!=`, `->` |
| `lastModified` | `>`, `>=`, `<`, `<=` |
| `groupId` | `==` |


> **Upcoming Breaking Change:** `location.id` (as a list of IDs or `["ALL"]`) will become a **required** filter. Omitting it currently defaults to all locations. See [Location-Based API Billing](/publicapi/location-filtering).


> **Date-limiting filter:** If you do not provide a date-limiting filter (`lastModified`, `start`, or `createdDate`), the server may automatically apply `lastModified >=` your organization's creation date or one year ago — whichever is more recent.


> **Ordering:** When filtering by `start` or `lastModified`, results are returned in descending order (newest first). Use `lastId` with date filters for stable pagination.


### Example Requests

Fetch events modified after a date at a specific location:

```
GET /api/v1/events?filter=lastModified>=2025-01-01T00:00:00Z,location.id->[64000000000004]
```

Fetch events for a provider within a date range:

```
GET /api/v1/events?filter=start>=2025-06-01,start<=2025-06-30,provider.id==789
```

Paginate using `lastId`:

```
GET /api/v1/events?lastId=9000000903375&pageSize=100&filter=lastModified>=2025-01-01T00:00:00Z,location.id->[64000000000004]
```

## GET — Event by ID

Returns a single event by its ID.

### Parameters

| Parameter | In | Required | Type | Description |
|  --- | --- | --- | --- | --- |
| `eventId` | path | Yes | integer | The ID of the event |
| `Organization-ID` | header | Yes | string | Your organization identifier |
| `responseFields` | query | No | string | Comma-delimited list of fields to return, or `ALL` |


### Example Request

```
GET /api/v1/events/9000000903375
```

## GET — Events Async

`GET /api/v1/events/async` retrieves events asynchronously. It returns the first 10 matching results immediately in the HTTP response and streams all matching records to the Streaming API.

See the [Async Endpoints V1 — Developer Guide](/publicapi/endpoints/async-v1#get--v1events-async) for shared async mechanics, parameters, and pagination. Filter keys are the same as [bulk list](#filter-keys) above.

## GET — Events Total Count

Returns the total number of events matching a filter. Uses the same filter syntax as [GET /v1/events](#get--bulk-list-events).

### Parameters

| Parameter | In | Required | Type | Description |
|  --- | --- | --- | --- | --- |
| `Organization-ID` | header | Yes | string | Your organization identifier |
| `filter` | query | No | string | Filter criteria — same keys as bulk GET |


### Example Request

```
GET /api/v1/events/totalcount?filter=lastModified>=2025-01-01,location.id==123
```

### Response

| Field | Type | Description |
|  --- | --- | --- |
| `data.total` | integer | Number of events matching the filter |


## POST — Create Event

Creates a new event. Supports one-time and recurring events (`WEEKLY` or `MONTHLY` patterns).

### Parameters

| Parameter | In | Required | Type | Description |
|  --- | --- | --- | --- | --- |
| `Organization-ID` | header | Yes | string | Your organization identifier |


### Request Body — `EventV1`

**Required fields:**

| Field | Type | Description |
|  --- | --- | --- |
| `title` | string | Event title (max 60 characters) |
| `start` | string (date-time) | Start time in ISO 8601 format (e.g. `2025-01-15T10:00:00.000Z`) |
| `location` | object | Linked model `{ "id": "<locationId>" }` |


**Optional fields:**

| Field | Type | Default | Description |
|  --- | --- | --- | --- |
| `duration` | integer | `60` | Length in minutes. Valid range: `5`–`1440` |
| `description` | string | — | Free-text description |
| `allDay` | boolean | — | When `true`, start is normalized to location midnight and duration is set to 1439 minutes |
| `color` | string | `bbbbbb` | 6-character hexadecimal color code without `#` prefix (e.g. `FF5733`) |
| `operatory` | object | — | Linked model `{ "id": "<operatoryId>" }`. Must belong to the same location |
| `provider` | object | — | Linked model `{ "id": "<providerId>" }`. Must be active with `isNonPersonEntity: false` |


**Recurrence fields** (all required together when creating a recurring series):

| Field | Type | Notes |
|  --- | --- | --- |
| `recurrenceType` | string | `WEEKLY` or `MONTHLY` |
| `recurrenceStart` | string (date) | Start of recurrence range (`YYYY-MM-DD`) |
| `recurrenceEnd` | string (date) | End of recurrence range. Max 2 years after `recurrenceStart` |
| `recurrenceFrequency` | integer | Interval (1–52) |
| `recurrenceDays` | array | Required for `WEEKLY`. Values: `SUNDAY`–`SATURDAY` |
| `recurrenceWeek` | string | Required for `MONTHLY`. Values: `FIRST`, `SECOND`, `THIRD`, `FOURTH`, `LAST` |
| `recurrenceDay` | string | Required for `MONTHLY`. Day of week (`SUNDAY`–`SATURDAY`) |


### Example Request — One-time event

```
POST /api/v1/events
Content-Type: application/json

{
  "title": "Team Meeting",
  "start": "2025-01-15T10:00:00.000Z",
  "duration": 60,
  "location": { "id": "64000000000004" },
  "operatory": { "id": "9000000000338" },
  "color": "FF5733"
}
```

### Example Request — Weekly recurring event

```
POST /api/v1/events
Content-Type: application/json

{
  "title": "Weekly Standup",
  "start": "2025-01-15T10:00:00.000Z",
  "duration": 30,
  "location": { "id": "64000000000004" },
  "recurrenceType": "WEEKLY",
  "recurrenceStart": "2025-01-15",
  "recurrenceEnd": "2025-06-15",
  "recurrenceFrequency": 1,
  "recurrenceDays": ["MONDAY", "WEDNESDAY", "FRIDAY"]
}
```

Returns `201 Created` with the created `EventV1` in `data`.

## PUT — Update Event

Updates an existing event. The request body uses the **`EventV1`** model with **partial update** semantics: only properties present in the JSON body are changed; omitted properties are left unchanged.

### Parameters

| Parameter | In | Required | Type | Description |
|  --- | --- | --- | --- | --- |
| `eventId` | path | Yes | integer | The ID of the event to update |
| `Organization-ID` | header | Yes | string | Your organization identifier |
| `recurrenceTarget` | query | **Yes** | string | `SERIES` or `OCCURRENCE` (uppercase only) |
| `responseFields` | query | No | string | Comma-delimited list of fields to return, or `ALL` |


### `recurrenceTarget` Query Parameter

| Value | Behavior |
|  --- | --- |
| `OCCURRENCE` | Updates only the single event identified by `eventId` |
| `SERIES` | Updates the recurring series (required when modifying recurrence settings; propagates time changes across future occurrences) |


> **Note:** Although the `EventV1` schema lists `title`, `start`, and `location` as required for create, **none of these are required on PUT**. Send only the fields you want to change.


### Writable Body Properties

| Property | Notes |
|  --- | --- |
| `title` | string, max 60 characters |
| `description` | string; send `null` to clear |
| `start` | ISO 8601 date-time |
| `duration` | integer, 5–1440 minutes; recomputes `end` |
| `allDay` | boolean; when `true`, duration is forced to 1439 min and start is normalized to location midnight |
| `color` | 6-char hex, no `#` prefix |
| `operatory` | linked model `{ id }`; send `null` to clear |
| `provider` | linked model `{ id }`; send `null` to clear |
| `recurrenceType`, `recurrenceStart`, `recurrenceEnd`, `recurrenceFrequency` | require `recurrenceTarget=SERIES` |
| `recurrenceDays` | required for `WEEKLY` recurrence updates |
| `recurrenceWeek`, `recurrenceDay` | required for `MONTHLY` recurrence updates |


### Not Writable on PUT

| Property | Behavior |
|  --- | --- |
| `location` | Read-only after create — if sent, a warning is returned and the value is ignored |
| `end` | Read-only — computed from `start` + `duration`; sending `end` has no effect |
| `id`, `lastModified`, `groupId`, `type` | Read-only response fields |


### Example Request

```
PUT /api/v1/events/9000000903375?recurrenceTarget=OCCURRENCE
Content-Type: application/json

{
  "title": "Updated Team Meeting",
  "duration": 90
}
```

Returns `200 OK` with the updated `EventV1` in `data`.

## DELETE — Delete Event

Deletes an event by ID.

### Parameters

| Parameter | In | Required | Type | Description |
|  --- | --- | --- | --- | --- |
| `eventId` | path | Yes | integer | The ID of the event to delete |
| `Organization-ID` | header | Yes | string | Your organization identifier |
| `recurrenceTarget` | query | Conditional | string | **Required** when the event is part of a recurring series. `SERIES` or `OCCURRENCE` |


| `recurrenceTarget` | Behavior |
|  --- | --- |
| `OCCURRENCE` | Deletes only the single occurrence |
| `SERIES` | Deletes the target occurrence and all future occurrences in the series |


### Example Requests

Delete a one-time event:

```
DELETE /api/v1/events/9000000903375
```

Delete a single occurrence from a recurring series:

```
DELETE /api/v1/events/9000000903375?recurrenceTarget=OCCURRENCE
```

Returns `200 OK` with `{ "data": { "id": <eventId> } }`.

## Response Structure

A successful response returns the following top-level structure:

| Field | Type | Description |
|  --- | --- | --- |
| `data` | object or array | The event object (single) or array of events (bulk) |
| `warnings` | array | Non-fatal warnings (operation still succeeded) |
| `errors` | array | Field-level or structural errors |


For bulk responses, `data` is an array and the response also includes `meta.pagination` with `lastId` for cursor pagination.

### `EventV1` Object Fields

| Field | Access | Description |
|  --- | --- | --- |
| `id` | read-only | Unique event identifier |
| `type` | read-only | Always `"EventV1"` |
| `title` | read/write | Event title (max 60 characters) |
| `description` | read/write | Free-text description |
| `start` | read/write | Start time (ISO 8601) |
| `end` | read-only | Computed from `start` + `duration` |
| `duration` | read/write | Length in minutes (5–1440) |
| `allDay` | read/write | All-day event flag |
| `color` | read/write | 6-char hex color (defaults to `bbbbbb`) |
| `lastModified` | read-only | Last modification timestamp |
| `groupId` | read-only | Group identifier for recurring events (max 50 chars) |
| `location` | read/write on create; read-only on update | Linked model `{ id }` |
| `operatory` | read/write | Linked model `{ id }`; optional |
| `provider` | read/write | Linked model `{ id }`; optional |
| `recurrenceType` | read/write | `WEEKLY` or `MONTHLY`; `null` for non-recurring |
| `recurrenceStart` | read/write | Recurrence range start (`YYYY-MM-DD`) |
| `recurrenceEnd` | read/write | Recurrence range end (`YYYY-MM-DD`) |
| `recurrenceFrequency` | read/write | Recurrence interval (1–52) |
| `recurrenceDays` | read/write | Days of week for `WEEKLY` recurrence |
| `recurrenceWeek` | read/write | Week of month for `MONTHLY` recurrence |
| `recurrenceDay` | read/write | Day of week for `MONTHLY` recurrence |


## Paginating with `lastId`

`lastId` is the recommended approach for iterating through large result sets. It uses the event's `id` as a cursor rather than an offset.

### Step 1 — Initial Request

```
GET /api/v1/events?pageSize=100&filter=lastModified>=2025-01-01T00:00:00Z,location.id->[64000000000004]
```

### Step 2 — Subsequent Requests

Pass the `id` of the last record in `data` as `lastId`:

```
GET /api/v1/events?lastId=9000000903375&pageSize=100&filter=lastModified>=2025-01-01T00:00:00Z,location.id->[64000000000004]
```

### Step 3 — Repeat

Continue until `data` is empty.

> **⚠️ Not Recommended:** Do not paginate large data sets using the `page` parameter. Use `lastId` instead.


## Streaming API

Events are streamable via the [Streaming API](https://papidocs.hs1api.com/publicapi/api-consumer-guide#streaming). Create, update, and delete operations emit stream messages. The async bulk GET endpoint also delivers matching records to the Streaming API.

See the [Streaming API Webhooks Setup Guide](/publicapi/streamapi-webhooks-setup) for setup instructions.

### Routing Key Format

```
organizationId.locationId.EventV1.operationType
```

### Example Routing Keys

All event changes at a specific location:

```
66d22762060811049b50d085.64000000000004.EventV1.*
```

All `CREATE` events across all locations:

```
66d22762060811049b50d085.*.EventV1.CREATE
```

### Operations Emitted

| Operation | When |
|  --- | --- |
| `CREATE` | A new event (or recurring series) is created |
| `UPDATE` | An event or series occurrence is updated |
| `DELETE` | An event or series occurrence is deleted |


## Errors & Warnings Reference

details
summary
strong
HTTP Status Code Errors
— click to expand
These apply to all Events V1 endpoints.

#### `400` — Bad Request

The request body or query parameters are invalid or cannot be processed. See [Field-Level Validation Errors](#field-level-validation-errors) below.

#### `401` — Unauthorized

|  |  |
|  --- | --- |
| **Message** | Authentication is required and has failed or has not been provided. |
| **Why** | Missing, expired, or malformed Bearer token. |
| **Fix** | Provide a valid Bearer token in the `Authorization` header. |


#### `403` — Forbidden

|  |  |
|  --- | --- |
| **Message** | The request is understood, but it has been refused or access is not allowed. |
| **Why** | The token lacks the required scope. Read operations require `model:read`; write operations require `model:write`. |
| **Fix** | Request a token with the appropriate scope. |


#### `404` — Not Found

|  |  |
|  --- | --- |
| **Message** | The requested resource is either missing or does not exist. |
| **Why** | No event exists for the given `eventId` within your organization, or the ID is invalid. |
| **Fix** | Confirm the `eventId` is correct and belongs to your organization. |


#### `408` — Request Timeout

|  |  |
|  --- | --- |
| **Message** | The server timed out while processing the request. |
| **Why** | High server load or an overly broad filter. |
| **Fix** | Narrow filter criteria and retry. |


#### `429` — Too Many Requests

|  |  |
|  --- | --- |
| **Message** | Rate limit exceeded. |
| **Why** | Too many requests in a short period. |
| **Fix** | Implement exponential backoff. See the [rate limiting guide](https://papidocs.hs1api.com/publicapi/api-consumer-guide#rate-limiting). |


#### `500` — Internal Server Error

|  |  |
|  --- | --- |
| **Message** | An unexpected error occurred. |
| **Fix** | Retry. If the issue persists, contact support with the request details and timestamp. |


details
summary
strong
Field-Level Validation Errors
— click to collapse
### `title`

**Error:** `title must be longer than or equal to 1 characters` / `title must be a string`

|  |  |
|  --- | --- |
| **Why** | `title` was omitted, empty, or not a string. |
| **Fix** | Provide a non-empty string up to 60 characters. |


### `start`

**Error:** `'undefined' is not a valid date in the format 'YYYY-MM-DD[T]HH:mm:ss.SSS[Z]'.`

|  |  |
|  --- | --- |
| **Why** | `start` was omitted or not a valid ISO 8601 date-time. |
| **Fix** | Provide a valid date-time, e.g. `2025-01-15T10:00:00.000Z`. |


**Error:** `start and end should be at the same day`

|  |  |
|  --- | --- |
| **Why** | For non-allDay events, the computed end time falls on a different calendar day than start (in the location's timezone). |
| **Fix** | Adjust `start` or `duration` so the event fits within a single day. |


### `duration`

**Error:** `duration` must be an integer between `5` and `1440`.

|  |  |
|  --- | --- |
| **Why** | Duration is outside the allowed range. |
| **Fix** | Provide a value between `5` and `1440` inclusive. |


### `location`

**Error:** `Linked model for property 'location' should be plain object with id of LocationV1`

|  |  |
|  --- | --- |
| **Why** | `location` was omitted on create or not a valid linked model. |
| **Fix** | Provide `{ "id": "<locationId>" }` with a valid location ID. |


**Warning:** `Property 'location' is read-only. Assignment is ignored.`

|  |  |
|  --- | --- |
| **Why** | `location` was included in a PUT request body. |
| **Fix** | Omit `location` from update requests. Location cannot be changed after create. |


### `operatory`

**Error:** `Location of Event and Location of Operatory should be the same`

|  |  |
|  --- | --- |
| **Why** | The operatory belongs to a different location than the event. |
| **Fix** | Assign an operatory at the same location as the event. |


### `provider`

**Error:** `Provider should have 'isNonPersonEntity: false' to be assigned to the appointment`

|  |  |
|  --- | --- |
| **Why** | The provider is a non-person entity. |
| **Fix** | Assign a person provider. |


**Error:** `Provider should have 'active: true' to be assigned to the appointment`

|  |  |
|  --- | --- |
| **Why** | The provider is inactive. |
| **Fix** | Use an active provider or reactivate the provider first. |


### `color`

**Error:** `color must be a 6-character hexadecimal color code without # prefix`

|  |  |
|  --- | --- |
| **Why** | Invalid color format (e.g. includes `#`, wrong length, non-hex characters). |
| **Fix** | Use exactly 6 hex characters, e.g. `FF5733` or `aabbcc`. |


### `recurrenceTarget`

**Error:** `recurrenceTarget must have a value in [SERIES,OCCURRENCE]`

|  |  |
|  --- | --- |
| **Why** | `recurrenceTarget` was omitted on PUT, or an invalid value was provided (including lowercase). |
| **Fix** | Provide `recurrenceTarget=SERIES` or `recurrenceTarget=OCCURRENCE` in uppercase. |


**Error:** `recurrenceTarget should be SERIES if fields for modifying recurrence settings are provided`

|  |  |
|  --- | --- |
| **Why** | Recurrence fields were sent in the body with `recurrenceTarget=OCCURRENCE`. |
| **Fix** | Use `recurrenceTarget=SERIES` when modifying recurrence settings. |


**Error:** `recurrenceTarget cannot be SERIES because event doesn't have recurrence settings`

|  |  |
|  --- | --- |
| **Why** | `recurrenceTarget=SERIES` was used on DELETE for a non-recurring event. |
| **Fix** | Omit `recurrenceTarget` for one-time events, or use `OCCURRENCE`. |


### Recurrence fields

**Error:** `recurrenceStart cannot be after the recurrenceEnd`

|  |  |
|  --- | --- |
| **Fix** | Ensure `recurrenceStart` is on or before `recurrenceEnd`. |


**Error:** `range between recurrenceStart and recurrenceEnd cannot be bigger than 2 years`

|  |  |
|  --- | --- |
| **Fix** | Limit the recurrence range to 2 years or less. |


**Error:** `No events can be created for provided recurrence settings`

|  |  |
|  --- | --- |
| **Why** | The recurrence configuration produces zero event dates. |
| **Fix** | Adjust recurrence days, frequency, or date range so at least one occurrence is generated. |


**Error:** `recurrenceDays should not be empty` / `recurrenceFrequency must not be less than 1` / `recurrenceFrequency must not be greater than 52`

|  |  |
|  --- | --- |
| **Why** | Incomplete or out-of-range recurrence configuration. |
| **Fix** | Provide all required recurrence fields for the chosen `recurrenceType`. See [POST — Create Event](#post--create-event). |