Upload a branding asset

POST /v1/branding/assets · permission branding:write

Uploads an image for the hosted login page: a logo, a favicon or a background image. The response id is the value you put in the theme fields logo, favicon or background_image (see Update branding); the image appears on the login page after you publish.

Every image is decoded in an isolated process and re-encoded as PNG, at most 1024 × 1024 pixels, with all metadata removed. A favicon also gets 32 px and 180 px versions. The id is the SHA-256 of the processed PNG, so uploading the same image again for the same field returns the existing asset with 200 OK.

Accepted formats are PNG, JPEG and WebP, and ICO for favicons only. SVG is not accepted. Font upload is not available.

An environment can hold at most 200 assets and 50 MB in total.

Request

The body is multipart/form-data, at most 1 MiB for the file.

Part Type Required Description
kind string yes logo, favicon or background_image.
file file yes The image. At most 1 MiB and 8192 pixels on a side.

With an Idempotency-Key, a retry must send exactly the same bytes, including the multipart boundary.

Response

201 Created for a new asset, 200 OK when the same image was already uploaded for the same field.

{
  "id": "3f0c…e91a",
  "kind": "favicon",
  "url": "https://acme.eu1.keenauth.app/assets/3f0c…e91a.png",
  "width": 256,
  "height": 256,
  "byte_size": 48213,
  "source_format": "ico",
  "favicon_32_url": "https://acme.eu1.keenauth.app/assets/9b1d…04c2.png",
  "favicon_180_url": "https://acme.eu1.keenauth.app/assets/c7e2…5a10.png",
  "created_at": "2026-10-19T20:30:00Z"
}

byte_size is the size of all stored PNGs of the asset and counts toward the quota.

Errors

HTTP code When
400 malformed_request The multipart body cannot be read, or has parts other than one kind and one file.
401 unauthenticated The credential is missing, invalid or revoked.
403 insufficient_permissions The credential lacks the required permission.
403 quota_exceeded The environment already has 200 assets or 50 MB.
413 payload_too_large The file is larger than 1 MiB.
415 unsupported_media_type The body is not multipart/form-data, or the file is not an accepted format (unsupported_media_type, svg_not_allowed, ico_favicon_only in errors[]).
422 validation_failed kind or file is missing or invalid (/kind font_upload_disabled for fonts), or the image cannot be used: invalid_image, image_too_large, image_dimensions_too_large, image_output_too_large, image_processing_limit.
422 idempotency_key_reuse The same Idempotency-Key was used with a different body.
429 rate_limited The rate limit was exceeded; retry after the Retry-After period.
503 service_unavailable Asset storage is temporarily unavailable; retry after the Retry-After period.

Example

curl -X POST https://api.eu.keenauth.com/v1/branding/assets \
  -H "Authorization: Bearer $KEENAUTH_SECRET_KEY" \
  -F kind=logo \
  -F file=@logo.png

With the CLI:

keenauth branding assets upload --kind logo --file logo.png