Skip to main content
Avatar upload is a two-step process: get a presigned URL, then upload the image directly to S3. The server automatically processes and promotes the avatar.

Step 1: Get Upload URL

// POST /users/me/avatar
const { uploadUrl, assetKey, cdnUrl } = await client.createAvatarUploadUrl({
  contentType: "image/jpeg", // or "image/png", "image/gif", "image/webp"
});

// uploadUrl = presigned S3 PUT URL (valid for 10 minutes)
// cdnUrl   = where the processed image will be served from

Step 2: Upload the Image

Upload the raw file bytes directly to the presigned URL:
const file = inputElement.files[0]; // from an <input type="file">

await fetch(uploadUrl, {
  method: "PUT",
  headers: { "Content-Type": file.type },
  body: file,
});

Step 3: Wait for Processing

The image is automatically processed by a Lambda function (converted to WebP, resized to multiple sizes). The avatar is auto-promoted once processing completes. Poll GET /users/me until avatarUrl appears:
async function waitForAvatar(maxAttempts = 10) {
  for (let i = 0; i < maxAttempts; i++) {
    const profile = await client.getCurrentUser();
    if (profile.avatarUrl) {
      return profile.avatarUrl;
    }
    await new Promise((r) => setTimeout(r, 1000)); // wait 1s between polls
  }
  throw new Error("Avatar processing timed out");
}

const avatarUrl = await waitForAvatar();

Constraints

ConstraintValue
Max file size5 MB
Allowed typesJPEG, PNG, GIF, WebP
Processing timeUsually 1-3 seconds
CDN variants44px, 48px, 60px, original, raw

Removing an Avatar

// PATCH /users/me
await client.patchUser({ removeAvatar: true });

Error Handling

try {
  const { uploadUrl } = await client.createAvatarUploadUrl({
    contentType: file.type,
  });

  const uploadRes = await fetch(uploadUrl, {
    method: "PUT",
    headers: { "Content-Type": file.type },
    body: file,
  });

  if (!uploadRes.ok) {
    throw new Error(`Upload failed: ${uploadRes.status}`);
  }

  const avatarUrl = await waitForAvatar();
  console.log("Avatar ready:", avatarUrl);
} catch (err) {
  if (err.status === 415) {
    console.error("Unsupported file type");
  } else {
    console.error("Avatar upload failed:", err);
  }
}