Jobs: scheduling and lifecycle
Copy jobs across selected forms and users
/a/Job_copy
Create one new job for every selected user/form combination, optionally carrying answers, stock and recurrence data.
When to use this endpoint
Use the copy dialog workflow to duplicate an existing active job. This is a creation operation, not an in-place edit or an idempotent template lookup.
Authentication and permissions
Authenticate with signed API-key fields or a logged-in CRM session. Legacy edit-restriction bit 128 is enforced; client administrators retain the existing admin exception.
The primary job selection is tenant-scoped. There is no automatic schedule-conflict or assignee-ownership check. Cross-tenant follow-ups use persisted outsourcing context.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | Integer string | Yes | Active source job ID. |
form_ids | Array or JSON array | Yes | Nonempty target job-form IDs; each must exist, have type=0 and not be archived. One new job is attempted per user/form pair. Values are integer-coerced. |
user_ids | Array or JSON array | No | Defaults to []. Empty means the source user ID; -1 also means source user; 0 unassigned. Positive IDs must be active tenant users. No list deduplication; copies use user_type=0. |
do_not_copy_form_values | Integer flag | No | Nonzero clears main-form answers; default 0 retains them. Changing form does not map answers into the new structure. |
do_not_copy_subform_values | Integer flag | No | Nonzero clears table answers; default 0 retains them. Stock is a separate flag. |
do_not_copy_job_ref | Integer flag | No | Nonzero requests the configured next reference; default 0 retains the source reference. job-ref-method and job-ref-only-numeric still apply through creation. |
do_not_copy_complete_status | Integer flag | No | Nonzero creates Incomplete; default 0 carries is_complete. It does not run completion actions merely because the copied status is Complete. |
do_not_copy_stock_used | Integer flag | No | Exactly 1 clears stock; otherwise retains selected stock fields and resets every source to -1_0 (no source). Omission now follows the same no-source default. |
do_not_copy_recurrence_data | Presence flag | No | Any supplied value removes recurrence_triggered, recurrence_no and recurrence_previous_id before creation. It does NOT suppress copying recurrence_id or the recurrence itself. |
ret_meeting_time | Integer flag | No | Nonzero adds the source meeting_time and meeting_time_e to the response. |
accommodation | JSON object, object or null | No | Overrides the source accommodation; omission carries it. Uses the existing accommodation normalizer, not an arbitrary metadata field. Normalized keys include name/address/phone/room_number/booking_reference, paired latitude/longitude, check_in/check_out and notes. Dates use YYYY-MM-DD HH:mm:ss; check_out must not precede check_in; coordinates must be paired and in range. Unknown keys are dropped; blank details normalize to null. |
Example request
$result = FM_api('Job_copy', [
'id' => '1042',
'form_ids' => json_encode(['81']),
'user_ids' => json_encode(['0']),
'do_not_copy_complete_status' => '1',
'do_not_copy_stock_used' => '1',
]);
Example response
{"ok":1,"job_ids":1088,"created_ids":[1087,1088],"rid":null}
job_ids remains the last scalar ID for existing callers. created_ids lists EVERY saved copy in creation order; rid remains only the last copied recurrence ID or null. A nested saved warning is retained with saved=1/warning/warnings.
A later creation failure returns error plus the saved created_ids prefix and saved=1 when nonempty. It does not undo earlier copies; reconcile that prefix before retrying.
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: id"} | Missing/nonpositive source ID. |
{"error":"missing or invalid parameter: form_ids"} | Missing/undecodable target list. |
{"error":"missing or invalid parameter: user_ids"} | Undecodable user list. |
{"error":"job does not exist"} | Missing/deleted source. |
{"error":"error when copying job. please copy this and send it to FieldMotion to be looked into: …"} | Job_new failed; the message embeds its response. Earlier copies or copied recurrence rows can already exist. |
{"error":"Invalid target job form"} | A target ID is missing, archived or not a job form; no copies are created. |
{"error":"Invalid job assignee"} | A selected source/target user does not resolve to an active tenant user. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Prevalidates a nonempty target-form list and all tenant-user IDs before creation. Target forms must be active job forms; user 0 is unassigned and -1 resolves the source user's numeric ID. Outsource assignments are not preserved as outsource type by copying.
- Creates every user/form pair without deduplicating either input list. Changing form does not map source answers into the new question structure.
- Copies the source schedule, customer/asset, contract, notes, attendees, priority, Job Type, department and selected answer/cost inputs. Job_new assigns a fresh creator/time and work-calendar completion estimate; this is not a historical clone.
- Resets subStatus to 0, removes viewing/on-route/in-progress metadata and strips source outsourcing relationships, so editing a copy cannot update the source job's remote counterpart. Accommodation is retained unless overridden; note entries use content, optional id/createdby and noteTime/editedTime in YYYY-MM-DD HH:mm:ss.
- Each pair can get its own recurrence copy. do_not_copy_recurrence_data removes selected trigger metadata, NOT recurrence_id; the recurrence-days setting supplies lead time. No batch size cap or idempotency key.
- Retained stock rows always start with source=-1_0 (no source), including when the stock flag is omitted. This avoids consuming the source job's inventory allocation again.
- Job_new is invoked through the merging PHP request helper. Accepted outer creation controls can also affect it; avoid unrelated editor fields.
Side effects
- Runs Job_new per pair: job/contract links, history, customer counts, answer/table saves, optional stock handling, asset tracking, configured events and notification scheduling. Existing copied stock allocations are not replayed.
- Copies recurrence rows and rewrites their job-ID caches. Does not clone source quote/invoice relation rows.
See it in Fieldmotion
CRM1
Open Jobs, choose Copy, select users/forms and the reference/answer checkboxes; filter Job_copy. jobCopy.js JSON-encodes both lists and warns that a recurrence is also copied.
CRM2
Choose Copy in the Jobs module. moduleJobs.js sends user_ids/form_ids and separate completion/reference/answer flags; its table-and-stock switch sends both subform and stock flags. Recurring-job copying can then issue Job_setMeetingTime.
Related endpoints
Job_setMeetingTimeruns dependent scheduling updates.Job_setUserIdAndTypehandles typed outsourcing handover.Job_deletedeletes one job through checked local cleanup.