Skip to content

Create a user asset upload ​

POST/api/v2/users/me/assets/

Start an upload of an avatar or cover image. This is the first of three steps. Send the file's metadata, not the file. The API creates the asset with is_uploaded: false and returns a signed storage credential in upload_data. You then upload the file straight to storage and confirm the upload.

A successful call returns 200, not 201, and the body is an envelope rather than the bare asset:

  • upload_data holds the storage url and the form fields to send with the file.
  • asset_id is the new asset's id. Use it as {pk} when you confirm, read, or delete.
  • asset_url is the asset's relative path.
  • asset is the asset object.

Body Parameters ​

entity_type:requiredstring
  • USER_AVATAR - USER_AVATAR
  • USER_COVER - USER_COVER

One of USER_AVATAR, USER_COVER.

name:requiredstring

The file name.

size:requiredinteger

The file size in bytes. Must be at least 1.

type:optionalstring

The file's MIME type, for example image/png. Defaults to image/jpeg. A type the endpoint doesn't accept is a 400.

Response shaping ​

fields:optionalstring

Comma-separated list of fields to return. Unrequested keys are omitted, not returned as null. id always comes back. Pass all for every requestable field. An unknown name is a 400. See Sparse fields.

Requestable here: asset_url, attributes, content_type, created_at, created_by_id, entity_type, id, is_uploaded, name, size, updated_at, updated_by_id, user_id.

Scopes ​

assets:write

Errors ​

StatusCodeCause
400invalid_requestThe request body or a query parameter failed validation.
401unauthorizedMissing or invalid credentials.
402payment_requiredThe feature this endpoint belongs to isn't enabled on your plan or is switched off.
403forbiddenYour role or token scope doesn't allow this.
404not_foundNo such resource, or it's outside your tenant.
406not_acceptableThe Accept header asks for a representation the API can't produce.
409conflictA business rule blocks the write — see the notes above.
413payload_too_largeThe request body is over the size limit.
415unsupported_media_typeThe Content-Type isn't one this endpoint accepts.
429rate_limitedThrottled. Honor the Retry-After header before retrying.
Create a user asset upload
bash
curl -X POST \
  "https://api.plane.so/api/v2/users/me/assets/" \
  -H "X-Api-Key: $PLANE_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "entity_type": "USER_AVATAR",
  "name": "avatar.png",
  "size": 48213,
  "type": "image/png"
}'
Response200
json
{
  "upload_data": {
    "url": "https://plane-uploads.s3.amazonaws.com/",
    "fields": {
      "Content-Type": "image/png",
      "key": "7b3e9d1f5a2c4e8b9d0f6a3c1e7b5d24-avatar.png",
      "x-amz-algorithm": "AWS4-HMAC-SHA256",
      "x-amz-credential": "AKIAIOSFODNN7EXAMPLE/20260114/us-east-1/s3/aws4_request",
      "x-amz-date": "20260114T092241Z",
      "policy": "eyJleHBpcmF0aW9uIjoiMjAyNi0wMS0xNFQxMDoyMjo0MVoifQ==",
      "x-amz-signature": "6f1a9c2e4b7d0835a1e9f2c6b4d8a0e3f5c7b9d1a2e4f6081c3b5d7e9f0a2c4b"
    }
  },
  "asset_id": "6a1f8c3e-9b2d-4f70-a5e4-1c7d3b9f0e28",
  "asset_url": "/api/assets/v2/static/6a1f8c3e-9b2d-4f70-a5e4-1c7d3b9f0e28/",
  "asset": {
    "id": "6a1f8c3e-9b2d-4f70-a5e4-1c7d3b9f0e28",
    "name": "avatar.png",
    "content_type": "image/png",
    "size": 48213.0,
    "attributes": {
      "name": "avatar.png",
      "type": "image/png",
      "size": 48213
    },
    "asset_url": "/api/assets/v2/static/6a1f8c3e-9b2d-4f70-a5e4-1c7d3b9f0e28/",
    "is_uploaded": false,
    "entity_type": "USER_AVATAR",
    "user_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
    "created_at": "2026-01-14T09:22:41.478363Z",
    "updated_at": "2026-01-14T09:22:41.478363Z",
    "created_by_id": "16c61a3a-512a-48ac-b0be-b6b46fe6f430",
    "updated_by_id": null
  }
}