---
title: "Set a contact photo"
description: "Uploads the photo shown for a contact, replacing any there was. The body is the image itself, not JSON, with its type in `Content-Type`: `image/png`, `image/jpeg`, `image/webp` or `image/gif`, up to 5 MB. It is fitted into a 512 pixel square and stored as WebP, and an animated image keeps its first frame."
url: "https://openemail.uk/docs/api/contacts/photo-set"
area: "API"
category: "Mailbox"
---

# Set a contact photo

Uploads the photo shown for a contact, replacing any there was. The body is the image itself, not JSON, with its type in `Content-Type`: `image/png`, `image/jpeg`, `image/webp` or `image/gif`, up to 5 MB. It is fitted into a 512 pixel square and stored as WebP, and an animated image keeps its first frame.

`PUT /contacts/{email}/photo`

## PUT /contacts/{email}/photo

Uploads the photo shown for a contact, replacing any there was. The body is the image itself, not JSON, with its type in `Content-Type`: `image/png`, `image/jpeg`, `image/webp` or `image/gif`, up to 5 MB. It is fitted into a 512 pixel square and stored as WebP, and an animated image keeps its first frame.

## Example

Needs `contacts:write`. The address has to be a saved contact, so save it with `PUT /contacts/{email}` first.

**curl**

```
curl -X PUT "$OE/contacts/grace%40example.com/photo" -H "$AUTH" -H 'content-type: image/jpeg' --data-binary @grace.jpg
```

**Response**

```
{
  "object": "contact",
  "email": "grace@example.com",
  "name": "Grace Hopper",
  "source": "manual",
  "notes": null,
  "photoUrl": "https://cdn-prod.openemail.uk/contacts/photos/5b0e2c7a-91d4-4f3e-8a26-c4f1d9e07b3a.webp",
  "lastSeenAt": null,
  "createdAt": "2026-09-18T09:00:00.000Z",
  "updatedAt": "2026-09-23T10:20:00.000Z",
  "audiences": [
    { "id": "aud_9f2c4b7e1a0d63d84c5f2e7b", "name": "All contacts", "builtin": "default" }
  ]
}
```

> An address that is not a saved contact is a `404` `contact_not_found`. A body that is not an image of an accepted type, is larger than 5 MB or cannot be read is a `422` `invalid_image`.

> A saturated image service answers `503` `image_busy`, which is safe to retry. Every upload gets a new `photoUrl`, so a cached old photo never shows under the new one.
