Jobs: creation
Create a Job
/a/Job_new
Create one customer or asset Job with its form, schedule, assignment and optional resource data.
When to use this endpoint
Choose the customer or asset, an available Job Form and the schedule before creating the Job. Each call creates one Job; CRM dialogs that select several forms or coincident users issue multiple calls.
Use this route for a new Job, including a secondary Job linked through secondary_to. Use Job_save for an existing Job and Job_copy for an explicit copy. Supplying an id here does not turn this handler into an update.
Authentication and permissions
Authenticate through a signed API key or a logged-in CRM session. The dispatcher establishes the active tenant and applies session CSRF checks where configured; API keys act as their owner. The handler calls Core_requireLogin().
The creator’s role-specific CRM Job Forms selection caps form_id. An unavailable role/form selection fails before the main Job insert. Assignee form preferences narrow the UI picker; the explicit handler check uses the creator role.
When Department Hierarchy and Team Access policy applies, scoped writers also need Jobs Add. Field roles cannot create Jobs. Assignment checks can require a visible Contract, a Customer/Site belonging to that Contract and department scope, and an eligible local assignee. Server-generated team_assignment metadata replaces caller-supplied provenance. Without those scoped guards the handler has no separate general Jobs Add check.
CRM2 session users with the explicit customer-by-user-contract role restriction must choose an eligible customer under Programme Contracts. Client administrators, signed API requests and app requests are excluded from that particular CRM2 restriction; TeamAccess checks remain independent.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
customer_id | Integer-compatible ID | Yes | Positive customer or asset ID, selected by customer_type. Integer-converted; zero/negative values fail. Use an existing eligible record. |
customer_type | Integer-compatible | No | 0 Customer (default), 1 Asset. Other values fail. An asset Job stores that asset ID; its location/owner supplies location context to downstream work. |
form_id | Integer-compatible ID | Yes | Positive Job Form ID allowed for the creator’s role. Use Forms_getDataTable with the creation picker controls to choose an available form. Zero/omission fails; this is not a form definition payload. |
meeting_time_e / meeting_time | Unix seconds / local datetime text | One schedule required | Prefer meeting_time_e as Unix seconds. Above 99999 it supplies the local meeting_time through the account timezone (UTC fallback). If meeting_time_e is zero/absent, derive it from meeting_time. Local schedules lexically below 2010 are rejected; this legacy route does not perform the stricter scheduling-setter validation. Send a real YYYY-MM-DD HH:MM:SS value or a valid epoch. |
due_date | SQL-compatible date/datetime text | No | Omitted/blank defaults to the resolved meeting_time. CRM commonly sends YYYY-MM-DD. Stored directly after SQL escaping; this handler does not normalize it as a relative offset. |
hours | Numeric-compatible hours | No | Float-converted, default 1. Used to calculate the initial date_completed/date_completed_e estimate with work-day-start, work-day-end and week-days-off settings. It is not evidence that an incomplete Job was completed. Negative durations are not rejected here; the end-time helper clamps the added portion to zero. |
user_id / user_type | Integer-compatible | No | user_id defaults 0 (unassigned). user_type defaults 0 (local user); 1 selects an outsourced account and invokes the outsourcing follow-up. Use an eligible linked account; scoped TeamAccess can reject outsourcing. This handler does not call the dedicated assignee validator outside those policy checks. |
department_id | Integer-compatible ID | No | Defaults 0 and is stored on the Job. The TeamAccess relationship check derives its boundaries from the Contract/customer/assignee, rather than treating this value as permission to access another department. |
contract_id / contract_name | Integer-compatible ID / text | No | contract_id defaults 0. A nonblank contract_name with no nonzero ID looks up an exact name or creates a minimal Contract before later Job validation. A nonzero contract_id takes precedence. Prefer an existing ID; contract-name creation is not rolled back by this handler if later validation fails. |
job_ref | Scalar text | No | Omitted/blank uses Jobs_getJobRefNext and job-ref-method. job-ref-only-numeric=1 strips nondigits from generated references and rejects nondigits in a supplied reference. Trimmed and truncated to 47 Unicode characters (byte fallback for invalid UTF-8). No uniqueness or replay key is enforced. |
job_ref_customer | Scalar text | No | Omitted/blank becomes empty. Trimmed and limited to 50 Unicode characters (byte fallback for invalid UTF-8). Stored both in the dedicated column and meta; arrays/objects are rejected. |
form_num / form_type / type_id | Integer-compatible | No | A nonzero form_num is used as supplied; otherwise one more than the largest active form_num for this form is selected. There is no sequence lock here. form_type defaults 0. type_id optionally stores a Job Type ID and affects notification preferences. |
is_complete / priority | Integer-compatible | No | Both default 0. is_complete is inserted directly (normal statuses 0 Incomplete, 1 Complete, 2 Processed); this is not a call to the status-transition APIs. A Contract checklist can reject Complete/Processed creation without required answers. CRM dialogs commonly send priority 3; no priority enum validation occurs here. |
meta | Native object/array or JSON text | No | Omitted, blank and native null normalize to an empty array. Invalid JSON or JSON null/scalars fail. The handler overwrites job_ref_customer, recurrence_no=1, original schedule and completion-date estimate metadata. Dispatcher creation guards remove caller-supplied contract_programme/contract_checklist; the server can attach a real Contract checklist. Use JSON text when signing requests. |
form_values / fields | Native object/array or JSON text | No | form_values is serialized into the initial row. Nonempty fields additionally invokes Job_setFields after insertion and merges field-name values, with its cost/asset follow-ups. Invalid or incomplete later field writes are not propagated consistently by this parent; read the Job back before assuming all values saved. |
table / subform_values | Native object/array or JSON text | No | Nonempty table invokes Job_setTables after insertion. subform_values is copied to table only when fields is absent/falsy; this compatibility fallback can replace an explicitly supplied table. Use table for explicit subtable writes. Child persistence, cost and asset follow-ups have their own rules. |
stock_used | JSON array text | No | Only strings longer than two bytes invoke Job_setStock after insertion; [] therefore skips it. Uses the existing stock-source/allocation workflow, which can change quantities, costs and related table data. The parent does not check its response before continuing. |
schedule_of_rates / schedule_of_rates2 | JSON array text | No | Defaults to []. The legacy parser chooses schedule_of_rates2 only when its encoded string is longer and decodes to an array. Accepts legacy positional rows or objects with positive sor_id/sid and numeric amt. Invalid rows are skipped. Missing component costs are filled from the selected rate; total cost is rounded to three decimals. The original first payload and enriched second payload are stored separately. |
misc_used / misc_cost | JSON array text / numeric-compatible | No | When misc_used is present, it determines misc_cost: sum cost_total where present, otherwise cost × qty (amt fallback). Invalid/non-array input calculates zero. Otherwise misc_cost float-converts, default 0. misc_used itself defaults to []. |
accommodation | Native object or JSON object text | No | Optional normalized version-1 object: name, address, paired latitude/longitude, phone, room_number, booking_reference, check_in/check_out and notes. Blank/null or an object with no details becomes SQL NULL. Coordinates must be paired and within ±90/±180. Dates require exact YYYY-MM-DD HH:MM:SS; checkout cannot precede checkin. Notes are normalized with IDs, actor and timestamps; empty content is omitted. This route has no blanket feature gate for this field. |
notes | Text, commonly JSON note-array text | No | Stored as supplied after escaping. CRM submits a JSON array of note objects; this handler does not normalize or create one from plain text. Omission leaves the database default. |
attendees / template_id | JSON object text / integer-compatible | No | attendees defaults empty. A decoded nonempty attendee object invokes Job_attendeesEmailSend, which expects an emails array. template_id=0/omission uses enabled account notification template preferences, including form-based choices; form/type notification rules can suppress delivery. No attendee result is included in this response. |
recurrence / recurrence_id | JSON object text / integer-compatible ID | No | recurrence.loaded truthy creates or updates a recurrence before inserting the Job; an existing positive recurrence_id can also be supplied through legacy meta. Conflicting positive column/meta IDs fail. Normalized membership is stored in the recurrence_id column, with legacy metadata removed from storage; Job_get can re-expose compatibility metadata. Recurrence failure stops the Job insert but a later insert failure is not a recurrence rollback guarantee. |
constraints / asset_used / internal_notes | JSON text | No | Decoded separately into named meta fields. Unlike meta, invalid JSON here becomes null without an explicit validation error. Omission leaves an existing incoming metadata value untouched. |
secondary_to / copied_id | Integer-compatible ID | No | secondary_to defaults 0 and links a secondary Job to its main Job; creation writes history to both. copied_id presence changes the creation log to a copy description and takes precedence over the secondary-job history branch. It does not copy data by itself. |
no_notifications | Wire string 0 or nonzero text | No | Omission/default string 0 enables scheduling; other values skip only the final notification-scheduling block. It does not suppress attendee mail, outsourcing mail, creation events or configured SMS listeners. |
isCRM2 | Legacy client marker | No | Presence, even value 0, selects the CRM2 sendNotification scheduling branch; absence invokes the legacy notifications service create/create2 actions. Also forwarded into new recurrence creation. This marker is not an authorization bypass or proof of CRM2 session identity. |
Example request
$job = FM_api('Job_new', [
'customer_id'=>'1042',
'customer_type'=>'0',
'form_id'=>'8',
'meeting_time'=>'2026-11-12 09:00:00',
'due_date'=>'2026-11-12',
'hours'=>'1',
'user_id'=>'0',
'user_type'=>'0',
'priority'=>'3',
'meta'=>json_encode((object)[]),
'form_values'=>json_encode((object)['Summary'=>'Inspect equipment']),
]);
Example response
{
"ok": 1,
"id": 1042,
"warning": ""
}
Returns one numeric Job ID and a warning string, empty on the ordinary success path. It does not return the Job, child-write results, notification IDs or recurrence details. There is no filtering, pagination or count envelope.
A notification scheduling failure can still return ok=1 and the saved ID with warning="Job #1042 was saved, but notification scheduling needs support review. Do not recreate the job." A recurrence update can also add a warning. Inspect the saved Job and follow-ups rather than repeating creation.
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": "invalid parameter: customer_id"} | customer_id integer-converts below one. |
{"error": "missing or invalid parameter: form_id"} | The form ID is zero/absent, after earlier role/customer policy checks. |
{"error": "invalid parameter (should be no less than 2010): meeting_time"} | The resolved local schedule is empty or lexically earlier than 2010. |
{"error": "Job Ref must be Numeric"} | A nonblank supplied reference contains nondigits while numeric-only references are enabled. |
{"error": "job_ref_customer too long. max length 50"} | The trimmed customer reference exceeds 50 characters. |
{"error": "invalid parameter: meta"} | Metadata cannot normalize to an array/object. |
{"error": "Conflicting recurrence membership"} | Positive top-level and legacy-meta recurrence IDs disagree. |
{"error": "Invalid asset selected"} | No asset exists for customer_type=1 and the supplied customer_id. |
{"error": "Invalid Customer Type."} | customer_type is neither 0 nor 1. |
{"error": "Your role does not permit creating Jobs."} | A restricted TeamAccess field role attempts creation. |
{"error": "Unable to create job"} | The main insert does not acknowledge a positive numeric ID. Earlier Contract/recurrence work may already exist. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Stores tenant-local schedule, assignment, form, costs and metadata, plus a historical customer/asset address snapshot at the scheduled time and parent-cache columns. Customer and asset existence checks in the ordinary handler do not explicitly exclude deleted rows; policy-specific scope checks are stricter. Select active records from supported pickers.
- The generated reference follows account job-ref-method (latest reference, largest active reference, or blank) and job-ref-only-numeric. The selected form number is calculated from active Jobs only. Neither selection is locked by this handler against concurrent creation.
- date_completed/date_completed_e are initialized from planned schedule and working duration even for an Incomplete Job. due_date defaults to the start schedule, not that calculated finish. Do not interpret these initial values as actual completion evidence.
- The supplied status is inserted directly. When Contract Programmes are enabled and a Contract is selected, the server attaches the Contract checklist snapshot. Complete/Processed creation can require all frozen checklist answers for the selected form; use form_values to supply those answers.
- There is no whole-operation transaction in Job_new itself. TeamAccess-enabled dispatcher execution can wrap tenant writes, but external notifications and other services are not part of a distributed transaction. Contract-name lookup/creation and recurrence changes precede the main insert. A successful insert is checked before most later follow-ups.
- Nonempty fields/table/stock payloads invoke separate handlers after insertion. Their errors are not consistently checked or surfaced by this parent, so ok=1 alone does not establish that all form/stock relationships were saved. The subform_values fallback is conditional on absent/falsy fields; prefer explicit table for a new integration.
- There is no bulk-create mode. CRM creates additional Jobs for multiple selected forms or users with repeated calls and can share one reference across them. secondary_to creates the explicit main/secondary relationship; copied_id only changes the audit description.
- The canonical office clients call this route directly. No direct Job_new caller was found in the searched current mobile intermediary or app source; app creation/synchronization uses the mobile service and is not established by this office contract.
Side effects
- Creates the Job and invalidates its tenant cache; can create/link a Contract, refresh Contract customer membership, cache parent/customer address details, and recount customer open/closed Jobs.
- Can create/update a recurrence, append the Job to its cached membership and move its processing position. Optional form/table/stock calls can update asset associations, costs, stock allocations, quantities and their own audits or notices.
- Fires Job_new events and compatible configured SMS creation listeners. Nonempty attendee data can send attendee emails. Outsourced assignments can create/send a remote Job and email its account contact. no_notifications does not suppress those independent effects.
- Schedules notifications through the branch selected by isCRM2 presence. An uncertain scheduling acknowledgement records a review hold and warning. An accepted scheduling response is not proof of message delivery.
- Removes earlier job-typed log entries for the newly created ID, then writes creation and initial-status history; secondary creation adds main-Job history too. No status-transition workflow is invoked by inserting is_complete directly.
See it in Fieldmotion
CRM1
In CRM1, open Jobs and create a new Job in the editor, select a customer/asset and form, then Save. Inspect /a/Job_new in developer tools (client/js/jobEdit.js). Form/table fields can also create a secondary Job through Choose Job Form → Create Job; those calls pass secondary_to and the source schedule.
CRM2
In CRM2, open Jobs and create a Job through the Job editor. Saving a new entry selects /a/Job_new; existing and copied entries select Job_save and Job_copy. The main editor sends isCRM2=1 and JSON metadata/resource payloads (client/js/lib/jobEdit.js). The form-field Create Job action also calls Job_new with secondary_to; its payload does not include the same client marker.
Related endpoints
Forms_getDataTable— Select a form using jobCreate/jobFormUserID picker controls.Jobs_getJobRefNext— Preview the account’s generated reference.Job_setFields— Understand named-field writes and cost follow-ups.Job_setTables— Understand the optional subtable write.Job_setStock— Inspect stock-source and allocation effects.JobRecurrence_create— Configure a recurrence separately.