Skip to content
Files, email and meetings

Uploading contact avatars

Upload an image and set it as a contact's profile photo in Lightfield.

Use the file upload lifecycle to upload an image, then set it as the contact’s photo through the contact’s $profilePhotoUrl field. The photo is the one the app shows on the contact, the same as choosing Upload photo on the record.

You’ll need:

  • A valid API key that resolves to a user. Workspace keys use their creator. Keys that cannot resolve to a user get purpose_requires_user when they create the upload.
  • The files:create scope to upload the image
  • Either contacts:create or contacts:update, depending on whether you are creating a new contact or updating an existing one
  • Edit access to the contact. Viewers and members who can’t edit the contact can’t change its photo in the app or through the API.
  • Optional files:read and contacts:read scopes if you want to check the upload or the final photo

There are two supported patterns:

Pattern When to use Contact request
Set on create You are creating the contact and its photo together POST /v1/contacts with fields.$profilePhotoUrl
Set on update The contact already exists, or you want to replace its photo POST /v1/contacts/{id} with fields.$profilePhotoUrl

In both patterns, the first three steps are the file upload lifecycle:

  1. POST /v1/files with purpose: "contact_avatar"
  2. PUT <uploadUrl>
  3. POST /v1/files/{id}/complete

After that, you set the file ID as the contact’s photo.

Create the upload session with purpose: "contact_avatar". The image must be a PNG, JPEG, WebP or GIF of at most 8 MB, the same limits as photos uploaded in the app.

Set sizeBytes to the file’s exact size in bytes. The upload URL is signed for that length, so a file of any other size is rejected by storage.

Terminal window
curl https://api.lightfield.app/v1/files \
-X POST \
-H "Authorization: Bearer $LIGHTFIELD_API_KEY" \
-H "Lightfield-Version: 2026-03-01" \
-H "Content-Type: application/json" \
-d '{
"purpose": "contact_avatar",
"filename": "ada-chen.png",
"mimeType": "image/png",
"sizeBytes": 48213
}'

Save id as FILE_ID and uploadUrl as UPLOAD_URL, then upload the bytes with the returned uploadHeaders:

Terminal window
curl -i -X PUT "$UPLOAD_URL" \
-H "content-type: image/png" \
--data-binary @ada-chen.png

Then complete the upload:

Terminal window
curl https://api.lightfield.app/v1/files/$FILE_ID/complete \
-X POST \
-H "Authorization: Bearer $LIGHTFIELD_API_KEY" \
-H "Lightfield-Version: 2026-03-01" \
-H "Content-Type: application/json" \
-d '{}'

Check that the response has status: "COMPLETED". See Uploading files for checksums, retries and upload errors.

Pass the completed file ID in fields.$profilePhotoUrl when you create the contact.

Terminal window
curl https://api.lightfield.app/v1/contacts \
-X POST \
-H "Authorization: Bearer $LIGHTFIELD_API_KEY" \
-H "Lightfield-Version: 2026-03-01" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-ada-chen-001" \
-d '{
"fields": {
"$name": { "firstName": "Ada", "lastName": "Chen" },
"$email": ["ada@northwind.example"],
"$profilePhotoUrl": "fil_abc123"
}
}'

Option B: set or replace the photo on an existing contact

Section titled “Option B: set or replace the photo on an existing contact”

Update the contact with the completed file ID. The new photo replaces any photo uploaded earlier, in the app or through the API.

Terminal window
curl https://api.lightfield.app/v1/contacts/$CONTACT_ID \
-X POST \
-H "Authorization: Bearer $LIGHTFIELD_API_KEY" \
-H "Lightfield-Version: 2026-03-01" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: ada-chen-photo-001" \
-d '{
"fields": {
"$profilePhotoUrl": "fil_abc123"
}
}'

Choose a new idempotency key for each intended write and reuse it only when retrying the same request. A retried update with the same key returns the original result without setting the photo again. See Idempotency.

Set $profilePhotoUrl to null. This works like Remove photo in the app and removes the photo the contact currently shows. If that’s an uploaded photo, a photo synced earlier (for example, from LinkedIn) can appear in its place; send null again to hide that one too.

Terminal window
curl https://api.lightfield.app/v1/contacts/$CONTACT_ID \
-X POST \
-H "Authorization: Bearer $LIGHTFIELD_API_KEY" \
-H "Lightfield-Version: 2026-03-01" \
-H "Content-Type: application/json" \
-d '{ "fields": { "$profilePhotoUrl": null } }'

$profilePhotoUrl sets an uploaded photo only from a fil_ ID. Apart from LinkedIn and Google profile-picture links, an image URL is not fetched: the request succeeds, but the photo does not change. To use an image hosted elsewhere, download it and upload it with the steps above.

Writing back the URL a read returned for the current photo is treated as no change, so you can send a retrieved record back unmodified.

Retrieve the contact and read fields.$profilePhotoUrl.value. It’s a temporary URL for the photo the app shows, not the file ID you sent, so fetch a fresh record instead of storing it.

Terminal window
curl https://api.lightfield.app/v1/contacts/$CONTACT_ID \
-H "Authorization: Bearer $LIGHTFIELD_API_KEY" \
-H "Lightfield-Version: 2026-03-01"

A photo that can’t be used fails the whole contact request with 422 unprocessable_content, and param is $profilePhotoUrl. None of the request’s other fields are written. Fix the photo and send the request again.

Message includes Cause and fix
no contact avatar upload found with ID The ID isn’t a contact_avatar upload opened by this key’s user, the upload expired before it was completed, or it was already used on a contact. Upload the image again as the same user. Retrying an idempotent create after its upload was used also returns this.
the file is not a PNG, JPEG, WebP or GIF image The uploaded bytes don’t match the declared image type. Upload a real PNG, JPEG, WebP or GIF with its matching mimeType.

At upload time, invalid_mime_type and file_too_large mean the image isn’t a supported type or is over 8 MB. A 404 on the contact means it doesn’t exist or isn’t in your workspace. See Errors for the standard error envelope.