Jobs: forms and resources
Upload a job subform image
/a/Job_setSubFieldImage
Store an image at a zero-based table cell using the job Form upload contract.
When to use this endpoint
Use for CRM2’s subform image upload, where formID identifies the saved table and x/y select its row and field positions.
Do not confuse this with Job_setTableCellImage: parameter names, resizing, returned reference fields and outsourced behavior differ.
Authentication and permissions
Authenticate with signed API-key fields or a logged-in CRM session. The handler performs no additional page or role-permission check.
Job and form IDs refer to the authenticated account. Cross-account writes occur only through the stored outsourcing relationship on the routes that explicitly mirror changes.
The dispatcher also applies account/user and IP restrictions. A CRM session carrying a CSRF token must supply it through X-FMCSRF or _csrf; missing/incorrect tokens can be rejected before this handler runs.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | Integer string | Yes | Required by presence, then converted to integer; positivity is not validated. The lookup requires an active job. |
formID | Integer string | Yes | Required by presence; converted to integer. The destination table form is not checked for existence or attachment to the job. |
x | Integer string | No | Zero-based row index; defaults to 0. No nonnegative or upper-bound validation. |
y | Integer string | No | Zero-based field index; defaults to 0. No nonnegative or upper-bound validation. |
jpeg | Base64 string | Conditional | Raw base64 image bytes or the exact data:image/jpeg;base64, prefix followed by base64. Decoding is non-strict. If present it overrides Filedata, even when blank. |
Filedata | Multipart file | Conditional | Alternative upload part; the handler reads its temporary-file bytes. Supply one nonempty image source. |
Example request
$result = FM_api('Job_setSubFieldImage', [
'id'=>'1042', 'formID'=>'302', 'x'=>'0', 'y'=>'1',
'jpeg'=>base64_encode($imageBytes),
]);
Example response
{"ok":1,"val":"{\"src\":\"appointment-1042-1791714600.1234.jpg\"}"}
val is a JSON string containing src; the exact generated filename uses the legacy appointment- prefix, job ID and microtime.
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 jpeg parameter supplied"} | No nonempty decoded/uploaded image bytes. |
{"error":"missing or invalid parameter: id"} | id is absent. |
{"error":"missing or invalid parameter: formID"} | formID is absent. |
{"error":"job does not exist"} | No active job is read. |
{"error":"Unable to save job"} | The primary database save failed. Successful-save history and subsequent follow-ups are not run. Image storage may already have created a file. |
{"error":"Unable to store job image"} | Image storage reported failure or did not return a positive file-metadata ID; no answer reference is saved. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Image bytes are not validated as JPEG. A .jpg filename does not prove the content type; a base64 value takes precedence over an uploaded file.
- Each upload creates a new image reference; replacing a cell/answer does not delete its previous image file. Storage errors or a missing file-metadata ID return Unable to store job image before the answer is saved.
- Creates the selected table if absent. Missing rows through x become arrays and preceding cells through y-1 become empty strings; the addressed cell is replaced with the encoded src object.
- No server-side image resize, field-type/schema check, coordinate bound or outsourced copy-back is performed. Use valid nonnegative positions from the current form.
Side effects
- Creates a type-8 image file linked to the job. Writes subform_values, timestamps and edit marker for the active job and invalidates the meetings cache; the database result is checked before history is recorded.
- Logs generic image-change history and a sub-field entry whose old value is the previous table (after gap filling) and whose subtype uses the job ID, not the form-field ID. Does not recalculate costs or asset links.
See it in Fieldmotion
CRM1
No direct canonical or legacy caller was found in CRM1. Its job Tables image editor uses Job_setTableCellImage instead.
CRM2
Upload an image in a subform on the job Form. pageJobForm.js posts multipart Filedata to php/api.php with extra=/Job_setSubFieldImage, id, formID, x and y.
Related endpoints
Job_setTableCellImageis the table upload contract with optional resizing and outsourced copy-back.Job_setFieldImageuploads image bytes into a named main-form answer.Job_setTableCellspatches selected row/field positions while retaining unspecified cells.