User accounts and profiles
Upload a user photo using a data URI
/a/User_setImage
Convert an uploaded user photo to JPEG and replace the stored avatar.
When to use this endpoint
Use either a real multipart Filedata upload or the data-URI branch supported by the API helper. Both share the same ownership, conversion and persistence checks.
User_updateImage is the multipart-only route used by CRM2. Both routes now set the profile image marker after confirmed storage.
Authentication and permissions
Authenticate with a signed API request or logged-in CRM session. Session uploads require the normal dispatcher CSRF token when configured; neither photo route is exempt.
Requires login and an owned tenant user. Ordinary self-profile edits are allowed; editing another user requires client administrator or explicit Users Edit permission. Protected membership fields require that permission even for self. Configured TeamAccess record scope and relationship checks remain in force.
The dispatcher applies configured TeamAccess user-record scope before the handler. An explicit target outside that scope returns an error; omitted targets use the current user for the scope check. Active/deleted filtering and additional target checks are described below.
Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
id | Integer string | Yes | Existing positive user ID owned by the authenticated tenant. Missing, malformed, zero/negative or foreign-parent IDs are rejected before file storage. |
Filedata | Multipart file or data URI | Yes | File part named Filedata. PNG, JPEG, GIF or WebP are accepted up to 12 MiB and 40 million input pixels. Without multipart files, a strict base64 data:image/... URI is accepted instead. |
filename | String | No | Accepted for compatibility with old data-URI callers but ignored. The stored JPEG name comes from the user ID. |
Example request
$result = FM_api('User_setImage', [
'id'=>'1042',
'Filedata'=>'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=',
'filename'=>'avatar.png',
]);
Example response
{
"ok": 1,
"time": 1793700000
}
time is current Unix seconds for cache busting; there is no returned file ID or URL.
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"} | id is omitted, null, or converts to an integer below 1. |
{"error": "no file uploaded"} | Multipart files are present but request Filedata is absent/falsy. |
{"error": "failed to create image. please check file and upload again"} | No converted JPEG is produced. |
{"error": "user does not exist"} | No target user belongs to the authenticated account. |
{"error": "only client admins or users with Users edit permission can edit other users"} | Caller targets another user without explicit edit authority. |
{"error": "Unable to save user changes. Check the saved state before retrying."} | A checked write/transaction failed; inspect saved state before retrying, especially for separate global identity storage. |
{"error":"Unable to save user photo. Check the saved state before retrying."} | Storage did not acknowledge success or profile bookkeeping failed. |
{"error":"file too large. max file size is 12582912 bytes"} | Multipart source exceeds 12 MiB. |
Legacy application errors may be returned with HTTP 200. Check the decoded response for error, not only the HTTP status.
Behaviour and edge cases
- Both routes use one checked photo helper and the existing User self/editor policy. The owned positive target must exist before conversion or storage.
- Validates multipart upload errors or strict data-URI/base64 input, file format, byte size and pixel count. ImageMagick converts the first frame, applies orientation and fits within 800×800 while preserving aspect ratio; this is not a square crop or a no-upscale guarantee.
- Uses private randomly named temporary storage and escaped conversion arguments. Temporary files are removed on conversion success, validation/storage failure and exceptions after temporary allocation.
- Stores the JPEG at
user-photos/<id>.jpgwith photo type 6. The file helper must acknowledge success with a positive file ID before has_image is set. A storage/profile-write failure returns an explicit error, never ok. - Both routes update has_image and user edit markers after confirmed file storage. A replacement file row can have a new ID. Photo storage and profile bookkeeping are not one distributed transaction: inspect saved state after an uncertain failure.
- Session requests use normal dispatcher CSRF checks. CRM2’s Dropzone already submits _csrf; integrations must send it when using an authenticated browser session.
Side effects
- Replaces photo data/metadata through the file helper, updates the tenant image marker/edit timestamps, clears files/users caches and logs image updated on success. No global identity photo is copied and no message is sent.
See it in Fieldmotion
CRM1
Open User → Main and upload a photo in CRM1. Filter User_setImage; the file uploader sends multipart Filedata and id. The corrected route accepts that request shape.
CRM2
No direct caller was found. CRM2’s User avatar widget uses User_updateImage with multipart Filedata instead.
Related endpoints
User_updateImage— Use the separate CRM2 multipart photo path.User_setName— Update the user’s display name.