Questions · Updated 2026-09-28

How do I send an email with an attachment?

Send an email with an attachment by adding attachments[] to POST /emails, each with filename, content (base64) or path (a URL fetched at send time), and content_type. GET /emails/{id}/attachments lists what was sent.

Send an email with an attachment by adding an attachments array to the body of POST /emails. Each entry has a filename, either content (the bytes as base64) or path (a URL that is fetched when the message is sent), and a content_type such as application/pdf. The same array goes on each item of POST /emails/batch, and POST /emails/preflight accepts it and checks it without sending anything.

The fields

  • filename is the name the recipient downloads. Only the last path segment is kept. Without it, the name is taken from the end of path, and failing that the file is called attachment.
  • content is base64. Content that does not decode is refused with invalid_attachment and the sentence "Attachment "…" content is not valid base64."
  • path is a URL. The bytes are fetched at send time, so the URL has to be reachable then.
  • content_type is a type and a subtype. Parameters are refused: the sentence is "Attachment content type must be a type and a subtype, like application/pdf."
  • content_id marks an inline image. Reference it from the HTML as cid: followed by that id.
  • contentType and contentId are accepted as camelCase spellings of the last two.

An email carries at most 100 attachments and 40 MB of base64 in total; over either, the refusal is invalid_attachment. An entry with neither content nor path gets the catalogue message "Each attachment needs content (base64) or path." and the fix "Provide attachment.content or attachment.path in POST /emails." retryable is false on every one of these, so correct the body rather than resending it.

Before a domain is verified, the onboarding sender does not carry attachments. The POST /emails summary says that path allows "no cc/bcc/attachments/tracking/extra headers", and the refusal is onboarding_shape_refused: "The onboarding sender does not accept cc, bcc, attachments, tracking, or extra headers." Its fix is "Omit those fields, or verify a domain with POST /domains and send from that domain."

curl -sS -X GET https://api.agentisend.com/emails/9c8f8f0e-3d1a-4d3f-9a1e-2b7c1a0f5e42/attachments \
  -H "Authorization: Bearer $AGENTISEND_API_KEY"
200
{
  "data": [],
  "has_more": true,
  "next_cursor": "string",
  "object": "string"
}
Response

Reading back what was sent

GET /emails/{id}/attachments lists them. Its summary is "What this email carried. Ids are stable positions; bytes_available says whether the payload is still retrievable." Each row has id, filename, content_type, size_bytes, content_id, path and bytes_available. GET /emails/{id}/attachments/{aid} returns one file; the summary is "The bytes of one attachment, exactly as they were sent. Served as an inert download." GET /emails/{id}/mime is the whole message as it went out, attachments encoded inside it, which is what to read when a recipient says a file arrived corrupted.

A receiving server that refuses a message for its size or its attachment type produces a bounce in the policy class. Why did my email bounce? says how to read that class, and the fix there is to shrink the file or drop it.

Next