Availability API
Every Availability action (13) and data model (7): endpoints, scopes, input, preconditions, events and examples.
Opening hours, special days, time off and who offers which service.
Module code availability · 13 actions · 7 data models · 12 events. A key or token never does more here than the person behind it can; row-level security applies exactly as in the app. Scopes: availability (everything), availability:read (read only) or availability:<capability>.
Resources
| Resource | Table | List and read | POST | PATCH | DELETE |
|---|---|---|---|---|---|
leave_allowances | leave_allowance | /api/v1/leave_allowances | |||
leave_periods | leave_period | /api/v1/leave_periods | |||
leave_types | leave_type | /api/v1/leave_types | |||
service_available_days | service_available_day | /api/v1/service_available_days | |||
service_providers | service_provider | /api/v1/service_providers | |||
working_hour_overrides | working_hour_override | /api/v1/working_hour_overrides | |||
working_hours | working_hour | /api/v1/working_hours |
Lists take limit (up to 200), cursor, order (e.g. -created_at), fields and q; every other query parameter is a column filter: ?status=paid, ?total[gte]=100, ?id[in]=a,b, ?x=null.
Actions
| Action | Needs | What it does |
|---|---|---|
availability.adjust_allowance | availability:manage | Adjust a balance |
availability.approve_leave | availability:write | Approve time off |
availability.cancel_leave | availability:write | Withdraw time off |
availability.carry_over | availability:manage | Close a leave year |
availability.clear_override | availability:manage | Remove a special day |
availability.clear_person_hours | availability:manage | Use the workspace hours |
availability.reject_leave | availability:write | Decline time off |
availability.request_leave | availability:write | Request time off |
availability.save_leave_type | availability:manage | Save a kind of time off |
availability.set_hours | availability:manage | Set opening hours |
availability.set_item_days | availability:manage | Set which days something runs |
availability.set_item_providers | availability:manage | Set who can do it |
availability.set_override | availability:manage | Set a special day |
availability.adjust_allowance
Adjust a balance — Adds or removes days on one person’s balance for one year — a joiner’s pro-rata, a correction.
Needs availability:manage · changes data
https://app.getera.io/api/v1/leave_allowances/actions/adjust_allowance- Canonical address:
POST /api/v1/actions/availability.adjust_allowance
Input
| Field | Type | Required | Notes |
|---|---|---|---|
userId | uuid | yes | Team member · id of app_user |
leaveType | string | yes | Kind · max 40 |
year | number | yes | Year · min 2000 · max 2100 |
adjustment | number | yes | Adjustment (days) · min -366 · max 366 |
note | string | Why · max 1000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
unknown_leave_type | Only a counted kind has a balance. |
not_a_member | They must be a member of this workspace. |
Events it publishes: availability.allowance_adjusted
What it writes: leave_allowance (upsert) — Sets the adjustment for that year
curl -X POST https://app.getera.io/api/v1/leave_allowances/actions/adjust_allowance \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"userId":"APP_USER_ID","leaveType":"Kind","year":2000,"adjustment":1}'{
"ok": true,
"result": "…",
"events": [
"availability.allowance_adjusted"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.leaveAllowances.adjustAllowance({
userId: "APP_USER_ID",
leaveType: "Kind",
year: 2000,
adjustment: 1
})era leave_allowances adjust_allowance \
--user-id "APP_USER_ID" \
--leave-type "Kind" \
--year 2000 \
--adjustment 1{
"name": "era__availability__adjust_allowance",
"arguments": {
"userId": "APP_USER_ID",
"leaveType": "Kind",
"year": 2000,
"adjustment": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 unknown_leave_type, not_a_member · 429 slow down
availability.approve_leave
Approve time off — Makes time off real, naming any appointments it would break.
Needs availability:write · changes data
https://app.getera.io/api/v1/leave_periods/{id}/actions/approve_leave- Canonical address:
POST /api/v1/actions/availability.approve_leave
Input
| Field | Type | Required | Notes |
|---|---|---|---|
leaveId | uuid | yes | Filled from the path {id} · Request · id of leave_period |
force | boolean | Approve anyway | |
note | string | Note · max 1000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
already_approved | It must not already be approved. |
not_found | The request must exist. |
not_pending | Only a request still waiting can be decided. |
cannot_approve_own | Your own time off is decided by somebody else, unless you hold Configure. |
not_an_approver | Their manager, or somebody who can approve, decides. |
clashes_with_appointments | Appointments inside the time off are named. |
Events it publishes: availability.leave_approved
What it writes: leave_period (update) — Marks it approved
curl -X POST https://app.getera.io/api/v1/leave_periods/LEAVE_PERIOD_ID/actions/approve_leave \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"force":true}'{
"ok": true,
"result": "…",
"events": [
"availability.leave_approved"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.leavePeriods.approveLeave("LEAVE_PERIOD_ID", {
force: true
})era leave_periods approve_leave LEAVE_PERIOD_ID \
--force true{
"name": "era__availability__approve_leave",
"arguments": {
"leaveId": "LEAVE_PERIOD_ID",
"force": true
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 already_approved, not_found, not_pending, cannot_approve_own, not_an_approver, clashes_with_appointments · 429 slow down
availability.cancel_leave
Withdraw time off — Withdraws a request, or gives back approved time off that has not started.
Needs availability:write · removes or cannot be undone
https://app.getera.io/api/v1/leave_periods/{id}/actions/cancel_leave- Canonical address:
POST /api/v1/actions/availability.cancel_leave
Input
| Field | Type | Required | Notes |
|---|---|---|---|
leaveId | uuid | yes | Filled from the path {id} · Request · id of leave_period |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The request must exist. |
not_your_leave | Only the person, or somebody with Configure, can withdraw it. |
cannot_cancel | Declined, withdrawn or already-started time off cannot be withdrawn. |
Events it publishes: availability.leave_cancelled
What it writes: leave_period (update) — Marks it withdrawn
curl -X POST https://app.getera.io/api/v1/leave_periods/LEAVE_PERIOD_ID/actions/cancel_leave \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"availability.leave_cancelled"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.leavePeriods.cancelLeave("LEAVE_PERIOD_ID")era leave_periods cancel_leave LEAVE_PERIOD_ID{
"name": "era__availability__cancel_leave",
"arguments": {
"leaveId": "LEAVE_PERIOD_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, not_your_leave, cannot_cancel · 429 slow down
availability.carry_over
Close a leave year — Moves what is left of each balance into the next year, up to each kind’s carry-over limit.
Needs availability:manage · changes data
https://app.getera.io/api/v1/leave_allowances/actions/carry_over- Canonical address:
POST /api/v1/actions/availability.carry_over
Input
| Field | Type | Required | Notes |
|---|---|---|---|
year | number | yes | Year · min 2000 · max 2100 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
year_not_over | Only a year that has ended can be closed. |
Events it publishes: availability.leave_carried_over
What it writes: leave_allowance (upsert) — Writes next year’s carried-over days
curl -X POST https://app.getera.io/api/v1/leave_allowances/actions/carry_over \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"year":2000}'{
"ok": true,
"result": "…",
"events": [
"availability.leave_carried_over"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.leaveAllowances.carryOver({
year: 2000
})era leave_allowances carry_over \
--year 2000{
"name": "era__availability__carry_over",
"arguments": {
"year": 2000
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 year_not_over · 429 slow down
availability.clear_override
Remove a special day — Puts one date back on the weekly hours.
Needs availability:manage · removes or cannot be undone
https://app.getera.io/api/v1/working_hour_overrides/{id}/actions/clear_override- Canonical address:
POST /api/v1/actions/availability.clear_override
Input
| Field | Type | Required | Notes |
|---|---|---|---|
overrideId | uuid | yes | Filled from the path {id} · Special day · id of working_hour_override |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The special day must still exist. |
Events it publishes: availability.override_cleared
What it writes: working_hour_override (delete) — Removes that date’s exception
curl -X POST https://app.getera.io/api/v1/working_hour_overrides/WORKING_HOUR_OVERRIDE_ID/actions/clear_override \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{}'{
"ok": true,
"result": "…",
"events": [
"availability.override_cleared"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.workingHourOverrides.clearOverride("WORKING_HOUR_OVERRIDE_ID")era working_hour_overrides clear_override WORKING_HOUR_OVERRIDE_ID{
"name": "era__availability__clear_override",
"arguments": {
"overrideId": "WORKING_HOUR_OVERRIDE_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found · 429 slow down
availability.clear_person_hours
Use the workspace hours — Removes one person’s own working week, so the workspace’s opening hours apply to them again.
Needs availability:manage · removes or cannot be undone
https://app.getera.io/api/v1/working_hours/actions/clear_person_hours- Canonical address:
POST /api/v1/actions/availability.clear_person_hours
Input
| Field | Type | Required | Notes |
|---|---|---|---|
userId | uuid | yes | Team member |
Events it publishes: availability.hours_changed
What it writes: working_hour (delete) — Removes that person’s weekday rows
curl -X POST https://app.getera.io/api/v1/working_hours/actions/clear_person_hours \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"userId":"USER_ID"}'{
"ok": true,
"result": "…",
"events": [
"availability.hours_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.workingHours.clearPersonHours({
userId: "USER_ID"
})era working_hours clear_person_hours \
--user-id "USER_ID"{
"name": "era__availability__clear_person_hours",
"arguments": {
"userId": "USER_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 429 slow down
availability.reject_leave
Decline time off — Declines a request, with a reason the requester is told.
Needs availability:write · changes data
https://app.getera.io/api/v1/leave_periods/{id}/actions/reject_leave- Canonical address:
POST /api/v1/actions/availability.reject_leave
Input
| Field | Type | Required | Notes |
|---|---|---|---|
leaveId | uuid | yes | Filled from the path {id} · Request · id of leave_period |
note | string | Reason · max 1000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The request must exist. |
not_pending | Only a request still waiting can be decided. |
cannot_approve_own | Your own time off is decided by somebody else, unless you hold Configure. |
not_an_approver | Their manager, or somebody who can approve, decides. |
Events it publishes: availability.leave_rejected
What it writes: leave_period (update) — Marks it declined
curl -X POST https://app.getera.io/api/v1/leave_periods/LEAVE_PERIOD_ID/actions/reject_leave \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"note":"Reason"}'{
"ok": true,
"result": "…",
"events": [
"availability.leave_rejected"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.leavePeriods.rejectLeave("LEAVE_PERIOD_ID", {
note: "Reason"
})era leave_periods reject_leave LEAVE_PERIOD_ID \
--note "Reason"{
"name": "era__availability__reject_leave",
"arguments": {
"leaveId": "LEAVE_PERIOD_ID",
"note": "Reason"
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, not_pending, cannot_approve_own, not_an_approver · 429 slow down
availability.request_leave
Request time off — Records time off, counts its working days against the balance, and blocks nothing until it is approved.
Needs availability:write · changes data
https://app.getera.io/api/v1/leave_periods/actions/request_leave- Canonical address:
POST /api/v1/actions/availability.request_leave
Input
| Field | Type | Required | Notes |
|---|---|---|---|
userId | uuid | Team member · Leave empty for time off that closes the whole organisation. | |
title | string | yes | Title · max 120 |
leaveType | string | Kind · max 40 | |
startDate | date | yes | From |
endDate | date | yes | To |
isAllDay | boolean | All day | |
startTime | string | From (time) · max 8 | |
endTime | string | To (time) · max 8 | |
description | string | Note · max 2000 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_range | The end must not be before the start, and one request covers at most a year. |
times_required | A part-day leave needs both times. |
not_your_leave | Somebody else’s time off, or the whole organisation’s, needs Configure. |
unknown_leave_type | The kind must be one this workspace uses. |
overlaps_existing | It must not overlap time off already requested or approved. |
insufficient_balance | A counted kind cannot go below zero unless its policy allows it. |
Events it publishes: availability.leave_requested
What it writes: leave_period (insert) — Records the request
curl -X POST https://app.getera.io/api/v1/leave_periods/actions/request_leave \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"title":"Title","startDate":"2026-11-02","endDate":"2026-11-02"}'{
"ok": true,
"result": "…",
"events": [
"availability.leave_requested"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.leavePeriods.requestLeave({
title: "Title",
startDate: "2026-11-02",
endDate: "2026-11-02"
})era leave_periods request_leave \
--title "Title" \
--start-date "2026-11-02" \
--end-date "2026-11-02"{
"name": "era__availability__request_leave",
"arguments": {
"title": "Title",
"startDate": "2026-11-02",
"endDate": "2026-11-02"
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:write · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_range, times_required, not_your_leave, unknown_leave_type, overlaps_existing, insufficient_balance · 429 slow down
availability.save_leave_type
Save a kind of time off — Adds or changes a kind of time off and its policy: accrual, carry-over and ceiling.
Needs availability:manage · changes data
https://app.getera.io/api/v1/leave_types/actions/save_leave_type- Canonical address:
POST /api/v1/actions/availability.save_leave_type
Input
| Field | Type | Required | Notes |
|---|---|---|---|
code | string | yes | Code · max 40 |
label | string | yes | Name · max 80 |
colour | string | Colour · max 20 | |
isPaid | boolean | Paid | |
accrualPeriod | string (enum) | Accrues · Allowed values: none, year, month | |
accrualDays | number | Days per period · max 366 | |
carryOverMax | number | Carry over at most · max 366 | |
maxBalance | number | Balance ceiling · max 999 | |
allowNegative | boolean | May go below zero | |
isActive | boolean | In use |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
invalid_code | A code is lower-case letters, digits and underscores. |
invalid_policy | A counted kind accrues something, and carry-over fits under the ceiling. |
Events it publishes: availability.leave_type_saved
What it writes: leave_type (upsert) — Writes the kind and its policy
curl -X POST https://app.getera.io/api/v1/leave_types/actions/save_leave_type \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"code":"Code","label":"Name"}'{
"ok": true,
"result": "…",
"events": [
"availability.leave_type_saved"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.leaveTypes.saveLeaveType({
code: "Code",
label: "Name"
})era leave_types save_leave_type \
--code "Code" \
--label "Name"{
"name": "era__availability__save_leave_type",
"arguments": {
"code": "Code",
"label": "Name"
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 invalid_code, invalid_policy · 429 slow down
availability.set_hours
Set opening hours — Changes one weekday — one or more opening blocks — naming any appointments it would put outside the hours.
Needs availability:manage · changes data
https://app.getera.io/api/v1/working_hours/actions/set_hours- Canonical address:
POST /api/v1/actions/availability.set_hours
Input
| Field | Type | Required | Notes |
|---|---|---|---|
userId | uuid | Team member · Leave empty to set the whole workspace’s hours. | |
weekday | number | yes | Weekday · min 0 · max 6 |
isWorkingDay | boolean | Open that day | |
startsAt | string | Opens · max 8 | |
endsAt | string | Closes · max 8 | |
blocks | object / array | Opening blocks · Several blocks in one day, e.g. [{"startsAt":"09:00","endsAt":"12:30"},{"startsAt":"13:30","endsAt":"18:00"}]. Replaces Opens/Closes. | |
force | boolean | Change anyway |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
hours_required | An open day needs both times. |
invalid_range | Closing must be after opening. |
overlapping_blocks | Opening blocks in one day may not overlap. |
orphans_appointments | Appointments left outside the new hours are named. |
Events it publishes: availability.hours_changed
What it writes: working_hour (upsert) — Replaces that weekday’s hours
curl -X POST https://app.getera.io/api/v1/working_hours/actions/set_hours \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"weekday":1}'{
"ok": true,
"result": "…",
"events": [
"availability.hours_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.workingHours.setHours({
weekday: 1
})era working_hours set_hours \
--weekday 1{
"name": "era__availability__set_hours",
"arguments": {
"weekday": 1
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 hours_required, invalid_range, overlapping_blocks, orphans_appointments · 429 slow down
availability.set_item_days
Set which days something runs — Restricts a catalogue entry to certain weekdays. Empty means every day.
Needs availability:manage · changes data
https://app.getera.io/api/v1/service_available_days/actions/set_item_days- Canonical address:
POST /api/v1/actions/availability.set_item_days
Input
| Field | Type | Required | Notes |
|---|---|---|---|
itemId | uuid | yes | Item · id of item |
days | string | Weekdays · Comma-separated, 0 = Sunday. Leave empty for every day. · max 32 |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The catalogue entry must exist. |
invalid_weekday | A weekday must be between 0 and 6. |
Events it publishes: availability.item_days_changed
What it writes: service_available_day (upsert) — Replaces the weekday restriction
curl -X POST https://app.getera.io/api/v1/service_available_days/actions/set_item_days \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"itemId":"ITEM_ID"}'{
"ok": true,
"result": "…",
"events": [
"availability.item_days_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.serviceAvailableDays.setItemDays({
itemId: "ITEM_ID"
})era service_available_days set_item_days \
--item-id "ITEM_ID"{
"name": "era__availability__set_item_days",
"arguments": {
"itemId": "ITEM_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, invalid_weekday · 429 slow down
availability.set_item_providers
Set who can do it — Replaces the team members who can perform one catalogue entry.
Needs availability:manage · changes data
https://app.getera.io/api/v1/service_providers/actions/set_item_providers- Canonical address:
POST /api/v1/actions/availability.set_item_providers
Input
| Field | Type | Required | Notes |
|---|---|---|---|
itemId | uuid | yes | Item · id of item |
userIds | string | Team members · Comma-separated user ids. Leave empty to assign nobody. · max 2000 | |
force | boolean | Assign anyway |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
not_found | The catalogue entry must exist. |
not_a_member | Everyone assigned must be an active member here. |
clashes_with_appointments | Bookings that still name someone being removed are listed. |
Events it publishes: availability.item_providers_changed
What it writes: service_provider (upsert) — Replaces the assignment list
curl -X POST https://app.getera.io/api/v1/service_providers/actions/set_item_providers \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"itemId":"ITEM_ID"}'{
"ok": true,
"result": "…",
"events": [
"availability.item_providers_changed"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.serviceProviders.setItemProviders({
itemId: "ITEM_ID"
})era service_providers set_item_providers \
--item-id "ITEM_ID"{
"name": "era__availability__set_item_providers",
"arguments": {
"itemId": "ITEM_ID"
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 not_found, not_a_member, clashes_with_appointments · 429 slow down
availability.set_override
Set a special day — Overrides the weekly hours for one date — a holiday, or a one-off opening.
Needs availability:manage · changes data
https://app.getera.io/api/v1/working_hour_overrides/actions/set_override- Canonical address:
POST /api/v1/actions/availability.set_override
Input
| Field | Type | Required | Notes |
|---|---|---|---|
overrideDate | date | yes | Date |
title | string | yes | Why · max 120 |
userId | uuid | Team member · Leave empty for the whole workspace. | |
isWorkingDay | boolean | Open that day | |
startsAt | string | Opens · max 8 | |
endsAt | string | Closes · max 8 | |
force | boolean | Set anyway |
Preconditions
The action is refused with 409 and one of these codes:
| Code | Refused when |
|---|---|
hours_required | An open day needs both times. |
invalid_range | Closing must be after opening. |
clashes_with_appointments | Appointments already booked that day are named. |
Events it publishes: availability.override_set
What it writes: working_hour_override (upsert) — Replaces that date’s exception
curl -X POST https://app.getera.io/api/v1/working_hour_overrides/actions/set_override \
-H "Authorization: Bearer $ERA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{"overrideDate":"2026-11-02","title":"Why"}'{
"ok": true,
"result": "…",
"events": [
"availability.override_set"
]
}import { createEra } from "@getera/sdk"
const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.workingHourOverrides.setOverride({
overrideDate: "2026-11-02",
title: "Why"
})era working_hour_overrides set_override \
--override-date "2026-11-02" \
--title "Why"{
"name": "era__availability__set_override",
"arguments": {
"overrideDate": "2026-11-02",
"title": "Why"
}
}Errors: 401 no or expired credential · 403 the credential does not hold availability:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 hours_required, invalid_range, clashes_with_appointments · 429 slow down
Data models
LeaveAllowance
Table leave_allowance · Resource leave_allowances
GET /api/v1/leave_allowancesGET /api/v1/leave_allowances/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
user_id | string (uuid) · uuid | app_user | ||
leave_type_code | string · text | |||
year | integer · int4 | |||
carried_over | string (decimal) · numeric | |||
adjustment | string (decimal) · numeric | |||
note | string · text | yes | ||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: workspace_id, user_id, leave_type_code, year
Checks:
CHECK ((carried_over >= (0)::numeric))CHECK (((year >= 2000) AND (year <= 2100)))
LeavePeriod
Table leave_period · Resource leave_periods
GET /api/v1/leave_periodsGET /api/v1/leave_periods/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
user_id | string (uuid) · uuid | yes | app_user | |
leave_type | string · text | |||
title | string · text | |||
description | string · text | yes | ||
start_date | string (date) · date | |||
end_date | string (date) · date | |||
is_all_day | boolean · bool | |||
start_time | string (time) · time | yes | ||
end_time | string (time) · time | yes | ||
status | string · text | requested, approved, rejected, cancelled | ||
created_at | string (date-time) · timestamptz | |||
days | string (decimal) · numeric | yes | ||
requested_by_user_id | string (uuid) · uuid | yes | app_user | |
decided_by_user_id | string (uuid) · uuid | yes | app_user | |
decided_at | string (date-time) · timestamptz | yes | ||
decision_note | string · text | yes |
Primary key: id
Checks:
CHECK ((end_date >= start_date))CHECK ((is_all_day OR ((start_time IS NOT NULL) AND (end_time IS NOT NULL) AND (end_time > start_time))))
LeaveType
Table leave_type · Resource leave_types
GET /api/v1/leave_typesGET /api/v1/leave_types/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
code | string · text | |||
label | string · text | |||
colour | string · text | |||
is_paid | boolean · bool | |||
accrual_period | string · text | none, year, month | ||
accrual_days | string (decimal) · numeric | |||
carry_over_max | string (decimal) · numeric | |||
max_balance | string (decimal) · numeric | yes | ||
allow_negative | boolean · bool | |||
is_active | boolean · bool | |||
sort_order | integer · int4 | |||
created_at | string (date-time) · timestamptz | |||
updated_at | string (date-time) · timestamptz |
Primary key: id
Unique: workspace_id, code
Checks:
CHECK ((accrual_days >= (0)::numeric))CHECK ((carry_over_max >= (0)::numeric))CHECK ((code ~ '^[a-z][a-z0-9_]{0,39}$'::text))CHECK ((length(TRIM(BOTH FROM label)) > 0))CHECK (((max_balance IS NULL) OR (max_balance > (0)::numeric)))
ServiceAvailableDay
Table service_available_day · Resource service_available_days
GET /api/v1/service_available_daysGET /api/v1/service_available_days/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
item_id | string (uuid) · uuid | item | ||
day_of_week | integer · int2 |
Primary key: id
Unique: item_id, day_of_week
Checks:
CHECK (((day_of_week >= 0) AND (day_of_week <= 6)))
ServiceProvider
Table service_provider · Resource service_providers
GET /api/v1/service_providersGET /api/v1/service_providers/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
item_id | string (uuid) · uuid | item | ||
user_id | string (uuid) · uuid | app_user |
Primary key: id
Unique: item_id, user_id
WorkingHourOverride
Table working_hour_override · Resource working_hour_overrides
GET /api/v1/working_hour_overridesGET /api/v1/working_hour_overrides/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
user_id | string (uuid) · uuid | yes | app_user | |
override_date | string (date) · date | |||
title | string · text | |||
is_working_day | boolean · bool | |||
starts_at | string (time) · time | yes | ||
ends_at | string (time) · time | yes | ||
created_at | string (date-time) · timestamptz |
Primary key: id
Checks:
CHECK (((is_working_day AND (starts_at IS NOT NULL) AND (ends_at IS NOT NULL) AND (ends_at > starts_at)) OR (NOT is_working_day)))CHECK ((override_date IS NOT NULL))
WorkingHour
Table working_hour · Resource working_hours
GET /api/v1/working_hoursGET /api/v1/working_hours/{id}
| Column | Type | Nullable | Allowed values | References |
|---|---|---|---|---|
id | string (uuid) · uuid | |||
organization_id | string (uuid) · uuid | organization | ||
workspace_id | string (uuid) · uuid | workspace | ||
user_id | string (uuid) · uuid | yes | app_user | |
weekday | integer · int2 | |||
is_working_day | boolean · bool | |||
starts_at | string (time) · time | yes | ||
ends_at | string (time) · time | yes |
Primary key: id
Unique: workspace_id, user_id, weekday, starts_at
Checks:
CHECK (((is_working_day AND (starts_at IS NOT NULL) AND (ends_at IS NOT NULL) AND (ends_at > starts_at)) OR (NOT is_working_day)))CHECK (((weekday >= 0) AND (weekday <= 6)))
Events
Workflows and webhooks react to these events.
| Event | What happens | Payload |
|---|---|---|
availability.allowance_adjusted | Somebody’s leave balance was adjusted by hand. | id, userId, leaveType, year, adjustment, recipientUserId |
availability.hours_changed | Opening hours were changed. | weekday |
availability.item_days_changed | The weekdays a catalogue entry runs on changed. | id, days |
availability.item_providers_changed | Who can perform a catalogue entry changed. | id, userIds |
availability.leave_approved | Time off was approved and now blocks bookings. | id, userId, clashes, recipientUserId |
availability.leave_cancelled | Time off was withdrawn. | id, userId, recipientUserId |
availability.leave_carried_over | A leave year was closed and unused days carried over. | year, balances, moved |
availability.leave_rejected | A time-off request was declined. | id, userId, note, recipientUserId |
availability.leave_requested | Someone requested time off. Addressed to their manager. | id, userId, leaveType, startDate, endDate, days, recipientUserId |
availability.leave_type_saved | A kind of time off or its policy changed. | id, code |
availability.override_cleared | A date went back to the weekly hours. | id, overrideDate |
availability.override_set | A single date was given its own hours. | id, overrideDate, isWorkingDay |