Attachment upload API

Get an upload slot with POST /v1/attachments, upload the file, then put the attachment ID in attachments on new mail and replies.

View as MarkdownLast updated

Attaching takes three steps: create an upload slot, upload the file, and send the email with the ID. Uploading needs the Send (messages:send) scope too. This request doesn't take an Idempotency-Key; every call creates a new attachment ID.

Create an upload slot

http
POST /v1/attachments
FieldRequiredDescription
agent_idYesThe token's agent ID
filenameYesThe file name recipients see. 1–255 characters. The ending must match the type. With no dot, the matching ending is added.
mime_typeYesapplication/pdf · image/png · image/jpeg · text/plain
bytesYesFile size in bytes
sha256YesThe file's SHA-256, 64 lowercase hex characters
bash
curl -sS https://api.atmark.ai/v1/attachments \
  -H "Authorization: Bearer $ATMARK_AGENT_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "agent_id": "7c21…f5a1",
    "filename": "report.pdf",
    "mime_type": "application/pdf",
    "bytes": 48213,
    "sha256": "9f86…0a08"
  }'
Example 201 response
{
  "attachment_id": "3b0c…9e21",
  "filename": "report.pdf",
  "expires_at": "2026-09-30T09:00:00.000Z",
  "upload": {
    "method": "PUT",
    "url": "https://…",
    "headers": { "…": "…" },
    "expires_at": "2026-09-28T09:15:00.000Z"
  }
}
  • filename is the name that was stored. If an ending was added, you get the name with it.
  • expires_at (the attachment) is 2 days out. Send before then.
  • upload.expires_at (the upload address) is 15 minutes out.

Upload the file

Send the file's bytes to upload.url with upload.method. Include every header in upload.headers, unchanged. The upload is refused if the size or SHA-256 differs from what you declared, or if you upload to the same address twice. Don't put the agent token on this request.

Node.js example
const res = await fetch(upload.url, { method: upload.method, headers: upload.headers, body: fileBytes });
if (!res.ok) throw new Error(`upload failed: ${res.status}`);

The malware scan starts as soon as the upload finishes. It usually takes a few seconds. There's no separate notice; sending tells you the result.

Attach to an email

Add attachments to the body of a new email or a reply.

FieldRequiredDescription
attachmentsNoAn array of attachment IDs. Up to 10, each ID once. Attached in order.
json
{ "agent_id": "7c21…f5a1", "from": "scout@atmark.ai", "to": ["partner@example.com"], "subject": "Report", "text": "See the attachment.", "attachments": ["3b0c…9e21"] }
  • If the scan isn't done, you get 409 attachment_scan_pending. No message is created, so send again shortly with the same Idempotency-Key.
  • One attachment can go on several emails (up to 50 per attachment).
  • Forwards (/forward) and scheduled mail don't take attachments. Including it returns 400 attachments_not_supported.

Errors

When creating an upload slot:

StatuserrorMeaning
400invalid_requestA field is missing or malformed.
400attachment_type_not_allowedThat mime_type isn't accepted.
400attachment_filename_extension_mismatchThe file name's ending doesn't match mime_type.
400attachment_filename_invalidThe file name contains a form that isn't allowed.
400attachment_too_largeOne file is bigger than the per-email attachment limit. text/plain is limited to 1 MB.
403agent_not_active · organization_not_sendingThis agent or organization can't send right now.
429attachment_daily_limitThe agent hit its daily limit (100 files or 200 MB per 24 hours).
429attachment_org_daily_limitThe organization hit its daily limit (1 GB per 24 hours).

When attaching to an email:

StatuserrorMeaning
400attachment_maliciousMalware was found. That file can't be sent.
400attachment_scan_failedThe scan couldn't finish. Upload the file again.
400attachment_integrity_mismatchThe uploaded file's size or SHA-256 differs from what you declared.
400attachment_content_mismatchThe content isn't the declared type.
400attachment_bytes_exceededThe attachments add up to more than the per-email limit.
400attachment_type_not_allowedThis agent's policy doesn't allow that type.
404attachment_not_foundIt doesn't exist or belongs to another agent.
409attachment_scan_pendingStill scanning. Send the same request again shortly.
409attachment_not_uploadedThe file hasn't been uploaded yet.
409attachment_expiredIt's been more than 2 days since the upload. Upload it again.
409attachment_link_limitThis attachment is already on 50 emails. Upload it again.
503attachment_check_timeoutChecking took too long. Send again with the same Idempotency-Key.

When you get an attachment error, the email doesn't go out.

Feedback on this page? Write to support@atmark.ai.