Upload a media

POST /api/v1/media Uploads an image into the media library of the organization. The body is multipart/form-data, not JSON — this is the only family in the reference that receives a file rather than a document.

Authorization

Role in the organizationThis endpoint
OWNERAllowed
ADMINAllowed
MEMBERRefused — 403
A key carries the role its creator holds in this organization — never a role on AgentsMail itself. See API keys.

Request body

FieldTypeRequiredDescription
filefileYesThe image. Accepted types: image/png, image/jpeg, image/webp, image/gif
folderstringNoA sub-folder of the media library. Lowercase letters, digits and dashes only
compressionstringNonone (default), light, balanced or strong — see Notes

Example

bash
curl -X POST -H "x-api-key: $AGENTMAIL_API_KEY" \
  -F "file=@./logo.png" \
  -F "folder=newsletter" \
  -F "compression=balanced" \
  "https://www.agentsmail.io/api/v1/media"

Response

  • 201 Created — the media.
  • 400 Bad Request — the file field is missing or empty, compression is not one of the four accepted values, folder is not lowercase letters, digits and dashes, the file's type is unsupported, the file is too large, or the organization's plan has reached its media storage limit.
  • 401 Unauthorized — missing or invalid key, or its owner left the organization.
  • 403 Forbidden — the caller is a legitimate member, but their role is too low.

Response fields

FieldTypeDescription
successbooleanIndicates if the operation was successful
data.pathstringThe media's identifier — pass it to Delete a media to remove this file
data.urlstringPublic URL — an email client is never authenticated
data.namestringFile name, after compression if it changed the extension
data.sizenumberFile size in bytes, after compression
data.typestringMIME type, after compression
data.createdAtstringISO creation date
data.folderstringSub-folder, absent at the root of the media library

Example response

json
{
  "success": true,
  "data": {
    "path": "organizations/7c2e…/media/newsletter/logo-1703123456789.jpg",
    "url": "https://…/logo-1703123456789.jpg",
    "name": "logo-1703123456789.jpg",
    "size": 48213,
    "type": "image/jpeg",
    "createdAt": "2026-08-29T10:00:00.000Z",
    "folder": "newsletter"
  }
}

Notes

  • The limit that stops uploads is the organization's plan, not a rate limit. Every key is already capped at 120 requests a minute like the rest of the API; on top of that, a plan has a maximum number of media files, and reaching it answers 400, not 403 — it is a state you can resolve by deleting a media, not a permission you are missing.
  • Compression runs on the server, with the same four levels the media screen offers (light, balanced, strong, and the default none). It targets the same definition and quality as the screen, but the engine that re-encodes the file is not the same one — expect the same order of magnitude in the resulting size, not a byte-identical file.
  • An animated GIF is never compressed, whatever compression is set to — it comes back untouched.
  • Compression never makes a file bigger: if re-encoding does not shrink it, the original file is stored instead, silently.
  • An opaque PNG comes back as JPEG when compressed — that gives the biggest size reduction, since JPEG has no transparency to preserve. A PNG with real transparency keeps its format and is still quantized to shrink it (palette-based re-encoding), just usually not as far as JPEG would.