Jobs: forms and resources
Upload a main-form job image
/a/Job_setFieldImage
Store image bytes and replace one named main-form answer with an encoded image reference.
When to use this endpoint
Use after reading the attached form’s field names. Supply image bytes; use Job_setField with an empty value to clear only the answer reference.
For an outsourced copy, this route also attempts to copy bytes and the answer reference back to the original job.
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 | Positive physical job ID. |
name | String | Yes | Trimmed, truthy form-field name. Must exist in the copy’s form, or in the original job’s form for the outsourced-copy fallback. The field’s type is not checked. |
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_setFieldImage', [
'id'=>'1042', 'name'=>'Site photo',
'jpeg'=>base64_encode($imageBytes),
]);
Example response
{"ok":1,"val":"{\"src\":\"73x1791714600123.jpg\"}"}
val is a JSON-encoded string, not a nested object. Decode it separately to read src. The generated filename combines the authenticated user ID and millisecond time.
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"} | Decoded/uploaded bytes are empty; this check precedes job/name validation and login in the handler. |
{"error":"missing or invalid parameter: id"} | Job ID is nonpositive. |
{"error":"missing or invalid parameter: name"} | Trimmed name is blank or PHP-falsy. |
{"error":"job does not exist"} | No active job is read. |
{"error":"no field by that name exists in the job's form"} | Neither the attached form nor the outsourced-original fallback contains the name; storage has not been attempted. |
{"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. |
{"ok":1,"val":"{\"src\":\"73x1791714600123.jpg\"}","warning":"Job saved; follow-up incomplete. Inspect saved state before retrying.","warnings":["Unable to mirror outsourced image"],"saved":1} | The primary save succeeded, but a recalculation, asset rebuild, notification or outsourced mirror failed. warnings identifies the failed follow-up; any normal response fields remain present. A benign no-change result from a follow-up is not a failure. Read saved state before retrying. |
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.
- Stores a JSON string containing src under the named answer. Invalid stored main-form JSON is normalized to an empty map. No resize, field-type check or duplicate-upload guard is applied.
- Field membership is checked before file creation. A rejected field name creates no image. There are no paging/filter controls.
Side effects
- Creates an account images file and file metadata with type_enum=8 linked to the job; does not append to the normal job attachment collection.
- Checked active-job answer/timestamp/edit-marker UPDATE with meetings-cache invalidation. Records generic history plus old/new field history when meta.id is positive.
- For an outsourced copy, copies the raw bytes to the original account and merges the reference into its active job. The helper does not restrict this to copies: editing an original with an outsourced counterpart can mirror the other way too. Mirror failure returns a saved-state warning and is not transactional with the local write. Deleted or missing counterparts are not changed and receive no copied image. No cost or asset recalculation is called.
See it in Fieldmotion
CRM1
Open a job Form image field, choose a file and inspect Job_setFieldImage. pageJobForm.js converts the selected image through a canvas to a JPEG data URI before sending it.
CRM2
Upload into a job Form image field. pageJobForm.js uses multipart Filedata through php/api.php with extra=/Job_setFieldImage and id/name, rather than sending a jpeg string.
Related endpoints
Job_setFieldsaves one main-form answer, including clearing an image reference.Job_setSubFieldImageis the CRM2 subform upload contract using formID.Job_setTableCellImageis the table upload contract with optional resizing and outsourced copy-back.