Voxbi Cockpit APIs
All endpoints

Save the employee's overtime decision preference (user)

Creates or replaces the employee's overtime decision preference. There is at most one employee level row, so this is an upsert: 201 the first time, 200 on every later call. It writes the same row HR edits under "Decision Preferences", so a change made here is visible to HR immediately.

This is a full replace, not a patch. Fields left out are reset: policy_reason becomes null and is_active becomes true.

Which fields are required depends on auto_decision_policy:

auto_decision_policy preferred_decision cap rest_policy
skip_if_manual_decision required required required
force_auto_decision required required required
force_zero_overtime ignored, stored as null ignored, stored as null ignored, stored as null

The cap may be sent as max_seconds or as max_hours; max_seconds wins when both are present. max_hours is multiplied by 3600 and rounded to the nearest second. One of the two is required unless the policy is force_zero_overtime.

Self service only. The request is rejected with 403 when the authenticated user has no access to the tenant or the employee record is not their own, and with 404 when the employee cannot be found.

HTTP: bearerAuth

User bearer token. Default authentication for customer-facing endpoints. Obtain a token by calling POST /login with your credentials, then send it on every subsequent request as Authorization: Bearer <token>. The token inherits the permissions and PBX scope of the authenticated user.

HTTP Authorization Scheme
bearer
Bearer format
Bearer <token>
pbx_id path · string · uuid *
UUID of the PBX tenant.
employee_id path · string · uuid *
UUID of the employee. Must be the authenticated user's own employee record.
accept header · string
example: application/json

Request body · required

Request schema
auto_decision_policy*string
How the automatic overtime decision behaves.
enum: skip_if_manual_decision force_auto_decision force_zero_overtime
example: force_auto_decision
policy_reasonstring | null
Optional free text note. Reset to null when omitted.
length: 0–1000
example: Prefers time off over payout
preferred_decisionstring | null
What to do with the overtime up to the cap. Required unless `auto_decision_policy` is `force_zero_overtime`, in which case it is ignored and stored as null.
enum: pay_up_to increment_up_to null
example: increment_up_to
max_hoursnumber | null · float
The cap in hours. Converted to seconds on save. Send this or `max_seconds`, not necessarily both.
≥ 1≤ 32
example: 8
max_secondsinteger | null · int32
The cap in seconds. Takes precedence over `max_hours` when both are sent.
≥ 3600≤ 115200
example: 28800
rest_policystring | null
What happens to the overtime above the cap. Required unless `auto_decision_policy` is `force_zero_overtime`, in which case it is ignored and stored as null.
enum: add_to_balance drop null
example: add_to_balance
is_activeboolean
Whether the preference is applied. Defaults to true when omitted.
default: true
example: 1

Responses

An existing preference was replaced.
Response schema
message*string
example: Your overtime decision preference has been saved.
data*object
The employee level overtime decision preference: the standing rule that tells the nightly overtime job what to do with the overtime this employee produces. It is the same row HR edits on the employee record under "Decision Preferences", stored in `tempus_overtime_decision_preferences`. An employee has at most one of these. When it is absent the company level preference applies instead. When `auto_decision_policy` is `force_zero_overtime` the employee is not eligible for overtime at all, so `preferred_decision`, `max_seconds` and `rest_policy` are always null.
idstring · uuid
UUID of the preference row.
example: 9b1f0c62-4d0a-4e2f-9f43-6f4a5c8d1e70
scopestring
Always `employee` on this endpoint. Company level rows are managed by HR only.
enum: employee
example: employee
employee_idstring · uuid
UUID of the employee the preference belongs to.
example: a24c68c0-92c0-4ca0-b749-84c6ddd671ed
auto_decision_policystring
How the automatic overtime decision behaves. | value | meaning | |---|---| | `skip_if_manual_decision` | Leave the period alone when a manual decision already exists. | | `force_auto_decision` | Always apply the automatic decision, overriding a manual one. | | `force_zero_overtime` | The employee is not eligible for overtime. |
enum: skip_if_manual_decision force_auto_decision force_zero_overtime
example: force_auto_decision
auto_decision_policy_labelstring
Human readable label for `auto_decision_policy`, translated to the request locale.
example: Force auto-decision (override manual)
policy_reasonstring | null
Free text note explaining why this policy was chosen.
length: 0–1000
example: Prefers time off over payout
preferred_decisionstring | null
What to do with the overtime, up to the `max_seconds` cap. Null when `auto_decision_policy` is `force_zero_overtime`. | value | meaning | |---|---| | `pay_up_to` | Pay the overtime out, up to the cap. | | `increment_up_to` | Add the overtime to the compensatory balance, up to the cap. |
enum: pay_up_to increment_up_to null
example: pay_up_to
preferred_decision_labelstring | null
Human readable label for `preferred_decision`.
example: Pay up to X hours
max_secondsinteger | null · int32
The cap in seconds, from 3600 (1 hour) to 115200 (32 hours). Null when `auto_decision_policy` is `force_zero_overtime`.
≥ 3600≤ 115200
example: 28800
max_hoursnumber | null · float
The same cap expressed in hours, rounded to two decimals. Convenience mirror of `max_seconds`.
example: 8
rest_policystring | null
What happens to the overtime left over above the cap. Null when `auto_decision_policy` is `force_zero_overtime`. | value | meaning | |---|---| | `add_to_balance` | Carry the remainder into the compensatory balance. | | `drop` | Discard the remainder. |
enum: add_to_balance drop null
example: add_to_balance
rest_policy_labelstring | null
Human readable label for `rest_policy`.
example: Add to balance
is_activeboolean
Whether the preference is applied. A deactivated row is kept but ignored.
example: 1
created_atstring | null
When the preference was first saved, `Y-m-d H:i:s`.
example: 2026-08-06 09:14:22
updated_atstring | null
When the preference was last changed, `Y-m-d H:i:s`.
example: 2026-08-06 10:02:51
meta*object
Everything a client needs to render the preference form without hardcoding the enums: the selectable values with their translated labels, the bounds of the cap, and the values that apply when the employee has never saved a preference.
configured*boolean
Whether an employee level preference row exists. False means `data` is null and the company level rule applies.
example: 1
options*object
Selectable values keyed by field name. Each entry maps the stored value to its translated label.
auto_decision_policy*object
Free-form object
preferred_decision*object
Free-form object
rest_policy*object
Free-form object
limits*object
Accepted range for the cap, in both units.
min_seconds*integer · int32
example: 3600
max_seconds*integer · int32
example: 115200
min_hours*number · float
example: 1
max_hours*number · float
example: 32
defaults*object
The values the form should start from when `configured` is false.
auto_decision_policystring
example: skip_if_manual_decision
preferred_decisionstring
example: pay_up_to
rest_policystring
example: add_to_balance
max_secondsinteger · int32
example: 3600
max_hoursnumber · float
example: 1
is_activeboolean
example: 1
The employee had no preference before and one was created.
Response schema
message*string
example: Your overtime decision preference has been saved.
data*object
The employee level overtime decision preference: the standing rule that tells the nightly overtime job what to do with the overtime this employee produces. It is the same row HR edits on the employee record under "Decision Preferences", stored in `tempus_overtime_decision_preferences`. An employee has at most one of these. When it is absent the company level preference applies instead. When `auto_decision_policy` is `force_zero_overtime` the employee is not eligible for overtime at all, so `preferred_decision`, `max_seconds` and `rest_policy` are always null.
idstring · uuid
UUID of the preference row.
example: 9b1f0c62-4d0a-4e2f-9f43-6f4a5c8d1e70
scopestring
Always `employee` on this endpoint. Company level rows are managed by HR only.
enum: employee
example: employee
employee_idstring · uuid
UUID of the employee the preference belongs to.
example: a24c68c0-92c0-4ca0-b749-84c6ddd671ed
auto_decision_policystring
How the automatic overtime decision behaves. | value | meaning | |---|---| | `skip_if_manual_decision` | Leave the period alone when a manual decision already exists. | | `force_auto_decision` | Always apply the automatic decision, overriding a manual one. | | `force_zero_overtime` | The employee is not eligible for overtime. |
enum: skip_if_manual_decision force_auto_decision force_zero_overtime
example: force_auto_decision
auto_decision_policy_labelstring
Human readable label for `auto_decision_policy`, translated to the request locale.
example: Force auto-decision (override manual)
policy_reasonstring | null
Free text note explaining why this policy was chosen.
length: 0–1000
example: Prefers time off over payout
preferred_decisionstring | null
What to do with the overtime, up to the `max_seconds` cap. Null when `auto_decision_policy` is `force_zero_overtime`. | value | meaning | |---|---| | `pay_up_to` | Pay the overtime out, up to the cap. | | `increment_up_to` | Add the overtime to the compensatory balance, up to the cap. |
enum: pay_up_to increment_up_to null
example: pay_up_to
preferred_decision_labelstring | null
Human readable label for `preferred_decision`.
example: Pay up to X hours
max_secondsinteger | null · int32
The cap in seconds, from 3600 (1 hour) to 115200 (32 hours). Null when `auto_decision_policy` is `force_zero_overtime`.
≥ 3600≤ 115200
example: 28800
max_hoursnumber | null · float
The same cap expressed in hours, rounded to two decimals. Convenience mirror of `max_seconds`.
example: 8
rest_policystring | null
What happens to the overtime left over above the cap. Null when `auto_decision_policy` is `force_zero_overtime`. | value | meaning | |---|---| | `add_to_balance` | Carry the remainder into the compensatory balance. | | `drop` | Discard the remainder. |
enum: add_to_balance drop null
example: add_to_balance
rest_policy_labelstring | null
Human readable label for `rest_policy`.
example: Add to balance
is_activeboolean
Whether the preference is applied. A deactivated row is kept but ignored.
example: 1
created_atstring | null
When the preference was first saved, `Y-m-d H:i:s`.
example: 2026-08-06 09:14:22
updated_atstring | null
When the preference was last changed, `Y-m-d H:i:s`.
example: 2026-08-06 10:02:51
meta*object
Everything a client needs to render the preference form without hardcoding the enums: the selectable values with their translated labels, the bounds of the cap, and the values that apply when the employee has never saved a preference.
configured*boolean
Whether an employee level preference row exists. False means `data` is null and the company level rule applies.
example: 1
options*object
Selectable values keyed by field name. Each entry maps the stored value to its translated label.
auto_decision_policy*object
Free-form object
preferred_decision*object
Free-form object
rest_policy*object
Free-form object
limits*object
Accepted range for the cap, in both units.
min_seconds*integer · int32
example: 3600
max_seconds*integer · int32
example: 115200
min_hours*number · float
example: 1
max_hours*number · float
example: 32
defaults*object
The values the form should start from when `configured` is false.
auto_decision_policystring
example: skip_if_manual_decision
preferred_decisionstring
example: pay_up_to
rest_policystring
example: add_to_balance
max_secondsinteger · int32
example: 3600
max_hoursnumber · float
example: 1
is_activeboolean
example: 1
Authorization Token Missing. This error is returned when the authorization token is missing.
Response schema
errorstring
Error message
example: Authorization Token is missing
The authenticated user has no access to the requested tenant, or the requested employee record does not belong to the authenticated user.
Response schema
No response body.
Data Not Found. This error is returned when the requested data is not found.
Response schema
messagearray<string>
[]string
The body failed validation.
Response schema
message*string
example: The given data was invalid.
errors*object
Free-form object
put https://cockpit.voxbi.com/api/v1/{pbx_id}/user/{employee_id}/user-overtime-decision-preference
Base URL
Request sample
curl -X PUT 'https://cockpit.voxbi.com/api/v1/{pbx_id}/user/{employee_id}/user-overtime-decision-preference' \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  --data '{"auto_decision_policy":"force_auto_decision","policy_reason":"Happy to be paid out for the first 8 hours","preferred_decision":"pay_up_to","max_hours":8,"rest_policy":"add_to_balance","is_active":true}'
const response = await fetch('https://cockpit.voxbi.com/api/v1/{pbx_id}/user/{employee_id}/user-overtime-decision-preference', {
  method: 'PUT',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': `Bearer ${YOUR_TOKEN}`,
  },
  body: JSON.stringify({
    "auto_decision_policy": "force_auto_decision",
    "policy_reason": "Happy to be paid out for the first 8 hours",
    "preferred_decision": "pay_up_to",
    "max_hours": 8,
    "rest_policy": "add_to_balance",
    "is_active": true
}),
});

const data = await response.json();
console.log(data);
import requests

response = requests.put('https://cockpit.voxbi.com/api/v1/{pbx_id}/user/{employee_id}/user-overtime-decision-preference',
    headers={'Authorization': f'Bearer {YOUR_TOKEN}'},
    json={
    "auto_decision_policy": "force_auto_decision",
    "policy_reason": "Happy to be paid out for the first 8 hours",
    "preferred_decision": "pay_up_to",
    "max_hours": 8,
    "rest_policy": "add_to_balance",
    "is_active": true
}
)
response.raise_for_status()
data = response.json()
print(data)
<?php
$context = stream_context_create([
    'http' => [
        'method'  => 'PUT',
        'header'  => "Content-Type: application/json\r\nAuthorization: Bearer YOUR_TOKEN",
        'content' => '{
    \"auto_decision_policy\": \"force_auto_decision\",
    \"policy_reason\": \"Happy to be paid out for the first 8 hours\",
    \"preferred_decision\": \"pay_up_to\",
    \"max_hours\": 8,
    \"rest_policy\": \"add_to_balance\",
    \"is_active\": true
}',
    ],
]);

$response = file_get_contents('https://cockpit.voxbi.com/api/v1/{pbx_id}/user/{employee_id}/user-overtime-decision-preference', false, $context);
$data = json_decode($response, true);
print_r($data);
Sample request
{}
"auto_decision_policy": "force_auto_decision",
"policy_reason": "Happy to be paid out for the first 8 hours",
"preferred_decision": "pay_up_to",
"max_hours": 8,
"rest_policy": "add_to_balance",
"is_active": true
}
{}
"message": "Your overtime decision preference has been saved.",
"data": {},
"id": "9b1f0c62-4d0a-4e2f-9f43-6f4a5c8d1e70",
"scope": "employee",
"employee_id": "a24c68c0-92c0-4ca0-b749-84c6ddd671ed",
"auto_decision_policy": "force_auto_decision",
"auto_decision_policy_label": "Force auto-decision (override manual)",
"policy_reason": "Happy to be paid out for the first 8 hours",
"preferred_decision": "pay_up_to",
"preferred_decision_label": "Pay up to X hours",
"max_seconds": 28800,
"max_hours": 8,
"rest_policy": "add_to_balance",
"rest_policy_label": "Add to balance",
"is_active": true,
"created_at": "2026-08-06 09:14:22",
"updated_at": "2026-08-06 10:02:51"
},
"meta": {}
"configured": true,
"options": {},
"auto_decision_policy": {},
"skip_if_manual_decision": "Skip if manual decision exists",
"force_auto_decision": "Force auto-decision (override manual)",
"force_zero_overtime": "Force zero overtime (not eligible)"
},
"preferred_decision": {},
"pay_up_to": "Pay up to X hours (specify max time)",
"increment_up_to": "Increment balance up to X hours (specify max time)"
},
"rest_policy": {}
"add_to_balance": "Add to balance",
"drop": "Drop (ignore remaining hours)"
}
},
"limits": {},
"min_seconds": 3600,
"max_seconds": 115200,
"min_hours": 1,
"max_hours": 32
},
"defaults": {}
"auto_decision_policy": "skip_if_manual_decision",
"preferred_decision": "pay_up_to",
"rest_policy": "add_to_balance",
"max_seconds": 3600,
"max_hours": 1,
"is_active": true
}
}
}
{}
"message": "Your overtime decision preference has been saved.",
"data": {},
"id": "9b1f0c62-4d0a-4e2f-9f43-6f4a5c8d1e70",
"scope": "employee",
"employee_id": "a24c68c0-92c0-4ca0-b749-84c6ddd671ed",
"auto_decision_policy": "force_zero_overtime",
"auto_decision_policy_label": "Force zero overtime (not eligible)",
"policy_reason": "Management contract, overtime not applicable",
"preferred_decision": null,
"preferred_decision_label": null,
"max_seconds": null,
"max_hours": null,
"rest_policy": null,
"rest_policy_label": null,
"is_active": true,
"created_at": "2026-08-06 10:02:51",
"updated_at": "2026-08-06 10:02:51"
},
"meta": {}
"configured": true,
"options": {},
"auto_decision_policy": {},
"skip_if_manual_decision": "Skip if manual decision exists",
"force_auto_decision": "Force auto-decision (override manual)",
"force_zero_overtime": "Force zero overtime (not eligible)"
},
"preferred_decision": {},
"pay_up_to": "Pay up to X hours (specify max time)",
"increment_up_to": "Increment balance up to X hours (specify max time)"
},
"rest_policy": {}
"add_to_balance": "Add to balance",
"drop": "Drop (ignore remaining hours)"
}
},
"limits": {},
"min_seconds": 3600,
"max_seconds": 115200,
"min_hours": 1,
"max_hours": 32
},
"defaults": {}
"auto_decision_policy": "skip_if_manual_decision",
"preferred_decision": "pay_up_to",
"rest_policy": "add_to_balance",
"max_seconds": 3600,
"max_hours": 1,
"is_active": true
}
}
}
{}
"error": "Authorization Token is missing"
}
Cache-Control string
example: private, must-revalidate
Connection string
example: keep-alive
Content-Type string
example: application/json
Vary string
example: Origin
X-RateLimit-Limit integer
Max requests allowed in the current rate-limit window.
example: 60
X-RateLimit-Remaining integer
Requests remaining in the current rate-limit window.
example: 57
No example for this status.
{}
"message": []
"Data not found"
]
}
Cache-Control string
example: private, must-revalidate
Connection string
example: keep-alive
Content-Type string
example: application/json
Vary string
example: Origin
X-RateLimit-Limit integer
Max requests allowed in the current rate-limit window.
example: 60
X-RateLimit-Remaining integer
Requests remaining in the current rate-limit window.
example: 57
{}
"message": "The given data was invalid.",
"errors": {}
"max_hours": []
"A maximum number of hours is required for the selected preferred decision."
]
}
}