Jobs: reports, email and links
Unlink a quote or invoice from a job
/a/Job_unlink
Remove one job-to-quote or job-to-invoice relation without undoing costs or deleting documents.
When to use this endpoint
Use the linked-jobs list’s unlink action for quotes, direct invoices or ancillary invoices. Use Job_taskUnlink for task associations instead.
Authentication and permissions
Signed API-key authentication or a logged-in CRM session is required. The handler uses the authenticated tenant and has no additional role/page check.
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 |
|---|---|---|---|
from_id | Integer string | Yes | Positive physical job ID; no job existence/active check. |
to_id | Integer string | Yes | Positive quote or invoice ID for this relation; no target existence requirement before removal. |
type_num | Integer string | Yes | 2=quote, 3=direct invoice, 4=ancillary invoice. Integer conversion must fall within 2–4. |
Example request
$result = FM_api('Job_unlink', [
'from_id' => '1042',
'to_id' => '501',
'type_num' => '3',
]);
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":"missing or invalid parameter: from_id"} | Missing/nonpositive from_id; to_id has analogous guard. |
{"error":"missing or invalid parameter: type_num"} | Integer value outside 2–4. |
{"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
- Deletes exact matching from_id/to_id/type_num relation rows. Missing relations still return ok; job and target rows are not verified first.
- For a removed type-3/type-4 relation, recomputes the target invoice’s distinct linked-job count in jobs_linked or legacy jobs_linked_ancilliary. Normalizes malformed invoice metadata before updating its count and edit markers.
- Repeated unlink of an absent pair is a no-op: no count changes, history or deletion audits. Legacy invoice-pointer fields are not cleared.
- Type 2 refreshes the target quote’s has_jobs from its remaining distinct linked-job relations and advances its edit time; it does not touch an invoice with the same numeric ID.
- Copied quote costs, invoice rows, invoice/job sent markers and business objects are retained. No filtering, pagination, finance-role guard or active-object check applies.
- Locks the job and target when present, then all matching links. Exact link deletion, every tenant deletion audit, history and cached target counts commit together; a persistence/audit failure rolls back.
Side effects
- Physically removes association rows, audits each relation ID through the tenant deletion log, and writes job unlink history only when relations were removed.
- Type-3/type-4 branches adjust invoice metadata counts without advancing invoice edit timestamps/markers. No cost reversal, mail or configured edit event is performed.
See it in Fieldmotion
CRM1
Open an invoice’s linked-jobs list and unlink a job. pageInvoices.js calls Job_unlink with type_num, from_id and to_id through its save helper.
CRM2
Open the invoice module’s linked jobs and choose Unlink. moduleInvoices.js posts the relation type and IDs then removes the displayed row; filter Job_unlink.
Related endpoints
Job_invoiceLinkcreates a direct invoice relation.Job_quoteLinkcan append costs that unlink does not reverse.Job_taskUnlinkremoves and audits a task relation.