When to use this endpoint

Bulk-create or update availability events from positional spreadsheet rows. The CRM2 import widget parses the file and sends JSON row data; the endpoint does not upload or parse a spreadsheet file itself.

Authentication and permissions

Authenticate with signed API-key fields or a logged-in CRM session. Uses the authenticated tenant’s data. Requires pageAvailability_add for inserted rows and pageAvailability_edit for updated rows; the whole batch’s required actions are checked before any row writes. Client administrators and full-access roles are allowed. Other roleless users are denied; signed API keys use the key owner’s role. These are page/action permissions, not an employee-ownership or HR-profile approver-list restriction.

Management calls reject accounts with features2.disableHREnhancements=1, returning HR enhancements are disabled. The helper reads the account’s current global feature value before falling back to session values. Browser permission checks are not server-side authorization.

Parameters

ParameterTypeRequiredDescription
vIntegerYesInteger-converted value at least 2. Lower/missing values fail before authentication.
mapJSON array textYesEach entry is an object with to. Its array index selects the corresponding row cell; the decorative from header is not used to find cells. Mapping must include obj_type, event_type, start_e and end_e, even for updates. Allowed destinations: id, obj_type, obj_id, event_type, availability, status, name, start_e, end_e, all_day, paid, requested_by, notes. Unknown/blank destinations are ignored; later duplicate destinations overwrite earlier ones.
rowsJSON array textYesArray of positional row arrays. Empty [] is accepted with Add permission and still produces an import-log entry. For a mapped ID >0, update an existing non-deleted tenant row; missing/nonpositive mapped IDs create. Row validation failures are accumulated while later rows continue.
filenameStringNoNonblank original file label is stored in import-log metadata. It does not locate/open a server file.

Example request

$result = FM_api('Availability_import', [
	'v' => '2',
	'map' => json_encode([
		['from' => 'Target', 'to' => 'obj_type'],
		['from' => 'User ID', 'to' => 'obj_id'],
		['from' => 'Type', 'to' => 'event_type'],
		['from' => 'Start', 'to' => 'start_e'],
		['from' => 'End', 'to' => 'end_e'],
		['from' => 'Status', 'to' => 'status'],
	]),
	'rows' => json_encode([
		['user', '71', 'Annual Leave', '1793005200', '1793034000', 'Approved'],
	]),
	'filename' => 'availability.csv',
]);

Example response

{"ok":1,"inserts":1,"updates":0}

Returns inserts/updates counters, optionally errors. No inserted ID list or import token is returned. Row failures look like {"ok":1,"inserts":0,"updates":0,"errors":[[0,[{"msg":"invalid event_type","val":"Bad row"}]]]}; the first value is the input row’s zero-based index for ordinary row arrays. Check errors even when ok=1.

Counters increment only after confirmed successful primary writes. Failed rows add an error and do not run row recalculation; later rows continue. Import-history or post-save recalculation failures add saved=1 and a warning. Accepted rows remain committed when others fail; do not blindly replay a partially accepted import.

Errors and unusual responses

ResponseCondition
{"error":"not logged in"}No authenticated tenant context is available. Invalid signed credentials can be rejected by the dispatcher before the handler runs.
{"error":"permission denied"}The authenticated actor lacks the required page/action permission.
{"error":"missing or invalid parameter: v"}Integer-converted version below 2, before login.
{"error":"missing or invalid parameter: map"}Missing map before login, or map does not decode to a PHP array after login.
{"error":"missing or invalid parameter: rows"}Missing rows before login, or rows does not decode to an array after login.
{"error":"HR enhancements are disabled"}The account has disabled HR enhancements.
{"error":"The import map is missing the \"FIELD\" field."}First missing destination among obj_type, event_type, start_e, end_e. FIELD is replaced with that actual name. Event/payload validation errors are instead returned inside errors for individual rows.
{"ok":1,"inserts":0,"updates":0,"errors":[[0,[{"msg":"Unable to save availability event","val":1}]]]}The first inserted row’s primary write failed. Failed writes are excluded from the counters.

Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.

Behaviour and edge cases

  • Cell values are trimmed strings. Target/event keys are lower-cased, spaces/hyphens replaced with underscores, and non-alphanumeric/underscore characters removed. For example Annual Leave becomes annual_leave.
  • Only exactly ten decimal digits are directly recognized as Unix seconds. Empty/"0" becomes zero; other time text uses PHP strtotime in the server’s current timezone, so use explicit timezone-bearing ISO text or ten-digit epochs. All-day normalization subsequently uses the account timezone.
  • all_day/paid are true only for 1, yes, y, true, on (case-insensitive). availability is true only for 1, available, yes, y, true; on is not in that allowlist. Status names pending/approved/rejected/cancelled/canceled map to 0/1/2/3; unrecognized status text uses intval and can become pending rather than an error.
  • An existing event supplies omitted destination defaults. A mapped blank cell is a supplied value and may clear text or fail validation rather than inherit. Existing metadata is preserved, with edited_by_import_id added; new rows receive created_by_import_id. Metadata is not an import destination.
  • The import saves content directly, not through mapped create/update. It does not recompute meta.leave_days or send legacy/configured transition notifications. Approved annual-leave balance recalculation is still called for old/new rows. No transaction spans the batch, primary writes, balances, or import history.
  • The primary write is checked before dependent work. After saving, failed recalculation or notification work retains the normal success fields and adds saved=1 and warning. Do not repeat the write to repair a follow-up; re-read the event and investigate balances/notification history. The event, history, balances and external queues are not one atomic transaction.

Side effects

  • Attempts per-row inserts/updates, sets last_edited, and on insert cdate/created_by; newly approved rows get current actor/time as approval attribution. Invalidates availability_events cache for attempted row writes.
  • When an old or new event is approved annual leave, the helper recalculates affected users’ stored annual-leave usage for touched years, using current company/department/role/user membership. Each account-local date touched counts as one whole day except dates in week-days-off (0 Sunday through 6 Saturday). Partial dates count as full days, year boundaries are split, and calendar arithmetic preserves DST dates. Overlapping leave events contribute separately; balances may become negative. Supported recalculation years are the server’s current year minus five through plus ten.
  • Adds a tenant imports_log row with import_type=46, token-derived cdate, creation/edit counts, actor ID and optional filename metadata. Individual events receive created_by_import_id or edited_by_import_id. This is import history, not a Core_log row audit or tenant deletion audit.

See it in Fieldmotion

CRM1

No direct CRM1 caller for Availability_import was found in the source search. The implemented management screen is CRM2’s Availability page.

CRM2

In Availability, choose Import. The widget uses map type 46, sends v=2, and checks the four required mapping destinations before calling Availability_import. Follow the row-error report rather than relying only on the UI’s Import Successful message.

Related endpoints