Jobs: reports, email and links
Return outsourced job answers to their owner
/a/Job_returnToOutsourcer
Copy an outsourced job’s saved answers and notes back to the original account and mark the copy returned.
When to use this endpoint
Use once the recipient account has finished its outsourced copy and is ready to return work to the creator. This is a one-way handoff of selected saved fields, not a merge-conflict resolver or a full job clone.
Authentication and permissions
Requires authenticated tenant context through signed API-key or CRM session. Resolves outsourcing metadata from the local job and uses that stored original tenant/job pair for cross-account access.
No extra admin/ownership check or outsource-return-mode setting guard is implemented in this handler. Dispatcher user/account/IP restrictions and session CSRF apply.
The dispatcher also applies user/account and IP restrictions. CRM sessions carrying a CSRF token must supply X-FMCSRF or _csrf; missing or incorrect tokens can be rejected before the handler runs.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | Integer string | Yes | Positive physical outsourced-copy job ID in the authenticated tenant. No caller-supplied destination tenant/job ID is required. |
Example request
$result = FM_api('Job_returnToOutsourcer', [
'id' => '1042',
]);
Example response
{"ok":1}
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":"no ID provided"} | Missing/nonpositive id. |
{"error":"not an outsourced job"} | Job_isOutsourced returned false, including ordinary missing/deleted jobs. |
{"error":"job does not appear to be an outsourced one"} | The local row has no outsourcing metadata (for example an original rather than a copy). |
{"error":"job already returned to outsourcer"} | The returned property already exists, even if its value is blank/false. |
{"error":"Unable to save job delivery changes"} | A checked database write, audit or transaction failed; local transactional writes roll back. Cross-tenant files/commits are not distributed-atomic. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Resolves the original only from the active copy’s persisted meta.outsourcing.link_from and id. Caller-supplied preloaded job shortcuts are not used; missing/deleted original jobs are rejected.
- Replaces original form_values, subform_values and notes wholesale with the copy’s stored JSON/text. Only statusOnRoute and statusInProgress are copied from local metadata into original metadata; other metadata and costs/resources/accommodation are not copied.
- Recursively inspects decoded main/table answers and nested JSON strings for safe image src filenames (JPEG, PNG, GIF, WebP or HEIC), deduplicates them and copies missing/newer bytes before changing the original.
- File metadata uses tenant-relative images/ paths. Source queries route through the copy’s tenant, destination queries through the original tenant; file storage uses the full destination tenant path and checks success.
- After the return transaction commits, a completed copy (is_complete=1) triggers original-job completion if ONCOMPLETE is configured, using the local acting user ID. An empty response queues the existing global retry record; uncertainty/failure is reported as a saved-success warning, not a reason to repeat the return.
- Does not set original completion fields directly. The local returned marker is committed only after checked original/image persistence, before the external completion follow-up. Noncomplete copies can also be returned.
- A failed original lookup, image read/storage or original database write leaves the copy unreturned. A failure to save the local marker after a cross-tenant original commit reports original_updated=1 and asks for inspection of both jobs.
- No list filtering, paging or optimistic last_edited conflict check applies. Local calls serialize on the copy and destination writes lock the original. Different-tenant database commits and file storage are not a distributed transaction.
Side effects
- Attempts tenant image reads/writes, replaces original answers/notes/selected metadata and advances original last_edited/edited_uuid=Job_returnToOutsourcer.
- May call on-complete or enqueue a global retry record. Writes local meta.outsourcing.returned={cdate:Unix seconds,uid:acting user ID}, advances local edit/sync metadata and invalidates job caches. No explicit return-history entry is written.
See it in Fieldmotion
CRM1
On an outsourced copy’s job screen, Return job to Outsourcer is shown when outsource-return-mode is nonzero and not returned. The confirmation requires typing I am sure, then pageJob.js posts Job_returnToOutsourcer. That confirmation/setting gate is UI-only.
CRM2
No direct caller of Job_returnToOutsourcer was found. Do not infer that CRM2 exposes the CRM1 return-mode button.
Related endpoints
Job_isOutsourcedidentifies original versus outsourced-copy links.Job_setFieldImageandJob_setTableCellImagehave separate immediate mirror behavior.Job_getAsPdfrefuses report generation from an outsourced copy.