Availability and leave
Evaluate a user’s availability for one requested window
/a/Availability_effective
Evaluate a user’s availability for one requested window
When to use this endpoint
Check continuous availability before assigning a local user to a job. The result covers the complete requested window and identifies the first blocking event or shift gap when unavailable. Use timeline to inspect every unavailable subinterval.
Authentication and permissions
Authenticate with signed API-key fields or a logged-in CRM session. Uses the authenticated tenant’s data. This scheduler/job helper has no extra Availability-management page or role-permission check.
There is no per-user/department visibility or active-user permission filter in this CRM read. Existing target memberships come from the tenant users table; do not pass outsourced-user tokens as local IDs.
Unlike management routes, disabled HR enhancements do not return a feature error here. The helper applies the endpoint-specific empty/available fallback described below after validating request formats and the window.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
user_ids | Native array or JSON array text | No | Defaults to an empty array, which returns an empty result. A scalar string is JSON-decoded and must become an array. Pass actual positive tenant user IDs; element values are integer-converted, without a public existence/positivity validation error. |
start_e / end_e | Unix seconds | Yes | Integer-converted positive start and strictly later end. Epoch milliseconds are not converted automatically. |
from / to | Unix seconds | Aliases | Used only when start_e/end_e are null or omitted, respectively. A present blank/zero primary field takes precedence and fails validation; a null primary value allows its alias. |
Example request
$result = FM_api('Availability_effective', [
'user_ids' => json_encode([71]),
'start_e' => '1793005200',
'end_e' => '1793034000',
]);
Example response
{
"71": {
"user_id": 71,
"available": 0,
"event": {
"id": 1042,
"obj_type": "user",
"obj_id": 71,
"event_type": "annual_leave",
"availability": 0,
"status": 1,
"status_name": "approved",
"name": "Annual leave",
"start_e": 1793005200,
"end_e": 1793034000,
"all_day": 0,
"paid": 1,
"requested_by": 71,
"approved_by": 0,
"approved_at": 0,
"notes": "Family leave",
"meta": [],
"cdate": 1792584000,
"last_edited": 1792584000,
"created_by": 71,
"specificity": 4
},
"events": [
{
"id": 1042,
"obj_type": "user",
"obj_id": 71,
"event_type": "annual_leave",
"availability": 0,
"status": 1,
"status_name": "approved",
"name": "Annual leave",
"start_e": 1793005200,
"end_e": 1793034000,
"all_day": 0,
"paid": 1,
"requested_by": 71,
"approved_by": 0,
"approved_at": 0,
"notes": "Family leave",
"meta": [],
"cdate": 1792584000,
"last_edited": 1792584000,
"created_by": 71,
"specificity": 4
}
]
}
}
Returns a map keyed by integer-converted user ID. Each value contains integer user_id/available, a representative event (or null), and ranked real events. With any unavailable segment, event is its first blocking event; when fully available, event remains the highest-ranked overlapping real event, or null when shifts alone cover the window. Empty input returns []. Real event objects use the normalized shape and add specificity.
On segments without a real event, availability is checked against user shifts. A blocking outside-shift segment uses a synthetic work_shift event (id=0, source and meta.source="work_shift", name="Outside work shift", unavailable, approved). Its range identifies the gap, not necessarily the complete request. The events array still contains ranked real events only; synthetic events do not add specificity.
When HR is disabled, positive requested IDs return available=1, event=null and events=[] without checking stored availability events or work shifts. Zero/negative IDs are skipped in that branch.
Errors and unusual responses
| Response | Condition |
|---|---|
{"error":"not logged in"} | No authenticated tenant context is available. Invalid signed credentials can be rejected by the dispatcher before the handler runs. |
{"error":"missing or invalid parameter: user_ids"} | A non-array input does not JSON-decode to an array. |
{"error":"missing or invalid time window"} | Integer-converted start is below one or end is not later than start. These request checks run before the disabled-HR fallback. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Only approved (status=1), deleted=0 events overlapping the window are considered: start_e < requested end and end_e > requested start. Company target 0 affects every resolved user, then matching role and any department, then user-specific targets. Precedence is descending specificity (user 4, department 3, role 2, company 1), then cdate, then last_edited, then event ID, all descending. This is creation-time-first within a specificity level, not simply latest edit wins.
- Splits the complete half-open window at event and shift boundaries and applies the ranked precedence separately to every segment. available=1 requires every segment to be available. An available override covers only its own interval and cannot erase a lower-priority absence or outside-shift gap elsewhere. On failure, event identifies the first blocking real or synthetic event.
- On segments without a real event, user meta.work_hours is normalized to shifts. Missing or false-like configuration falls back to work-day-start/work-day-end (minutes, defaults 510/1020) and week-days-off. A nonempty malformed string, unsupported shape, or explicit version-2 configuration with no valid shifts instead yields no shifts. Account timezone is used, defaulting to UTC if unset/invalid; overnight and adjacent shifts are supported. Lunch metadata does not split shift coverage here.
- Unknown users are not rejected. With HR enabled, effective evaluation still loops the requested integer IDs and may use default work shifts even when no user row was found. Invalid IDs can therefore produce misleading fallback results; use a real user lookup first.
- No paging/window-size limit is imposed. The read does not mutate balances, events, history or notifications.
See it in Fieldmotion
CRM1
No direct CRM1 caller for Availability_effective was found in the source search. The implemented management screen is CRM2’s Availability page.
CRM2
Create/edit a Job and choose a local assignee or change its time. jobAvailabilityCheck and jobEdit call Availability_effective with a one-user JSON ID list and start/end epochs. The UI can block assignment or show a warning according to its override setting; this endpoint itself does not assign or save a Job.
Related endpoints
Availability_forScheduler: ranked real events without synthetic work-shift results.Availability_timeline: segmented unavailable intervals for display.Availability_list: stored event details and status.