> ## Documentation Index
> Fetch the complete documentation index at: https://docs.orbit.devotel.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Email attachments

> Attach files to outbound emails — limits, MIME allowlist, and SDK samples in every language.

# Email attachments

Email sends through `POST /api/v1/messages/email` accept up to **20 attachments per email**, totalling **25 MB across all attachments** (RFC 5322 ceiling), with a per-file cap of **10 MB**. Each attachment is referenced either by `url` (preferred — an Orbit-issued GCS-signed URL, e.g. the one returned by the [Files API](/api-reference/files)) or as inline base64 `content`.

URL-based attachments are fetched server-side, base64-encoded, and forwarded to the upstream provider. Orbit applies an SSRF allowlist so an attacker who controls a `url` field can't pivot Orbit's outbound network into your private network. The allowlist accepts **only** HTTPS URLs on Orbit's storage host (`storage.googleapis.com`, or any `*.storage.googleapis.com` subdomain) — files hosted on your own CDN, S3 bucket, or any other host are rejected with `422 VALIDATION_ERROR` (`URL must point to our files service`). To send a file you host yourself, upload it to the [Files API](/api-reference/files) first and reference the signed URL it returns (or inline it as base64 `content`).

## Limits

| Constraint                | Value                                   |
| ------------------------- | --------------------------------------- |
| Per-attachment size       | 10 MB                                   |
| Total per-email size      | 25 MB (across all attachments combined) |
| Max attachments per email | 20                                      |

## MIME allowlist

The `content_type` field must be one of:

```
application/pdf
application/msword
application/vnd.openxmlformats-officedocument.wordprocessingml.document  (.docx)
application/vnd.ms-excel
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet         (.xlsx)
application/vnd.ms-powerpoint
application/vnd.openxmlformats-officedocument.presentationml.presentation (.pptx)
text/csv
text/plain
application/zip
image/jpeg
image/png
image/gif
image/webp
```

Anything else is rejected with `422 VALIDATION_ERROR`. We block executables and unknown types so a customer can't accidentally hand their domain reputation to a malware payload.

`image/svg+xml` is intentionally NOT on the list — SVG files are XML and can embed `<script>` elements, event handlers, or `<foreignObject>` HTML. If we accepted SVG attachments, a forwarded SVG could turn into stored XSS the moment a downstream client rendered it inline.

## Filename rules

* Required, max 255 chars.
* May not contain `/`, `\\`, or `..` — guards against path traversal in downstream tools that re-emit the filename.
* The filename surfaces verbatim in the recipient's mail client.

## URL vs content

| Approach  | When to use                                                                                                                                                                                                                                                                                                         |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `url`     | Files uploaded to Orbit via the [Files API](/api-reference/files) — reference the `storage.googleapis.com` signed URL it returns. Preferred — keeps the request small and lets the platform stream the bytes server-side. External hosts (your own CDN/S3) are **not** accepted; see the SSRF allowlist note above. |
| `content` | Small files generated inline (signed PDFs, dynamic CSVs) where the round-trip to upload first isn't worth it. Always base64-encoded.                                                                                                                                                                                |

Each attachment object MUST include either `url` or `content` — not both, not neither.

## Send with curl

### URL-based attachment

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/messages/email \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "to": "user@example.com",
    "from": "billing@yourdomain.com",
    "subject": "Your invoice for May 2026",
    "html": "<p>Your invoice is attached.</p>",
    "attachments": [
      {
        "filename": "invoice-may-2026.pdf",
        "content_type": "application/pdf",
        "url": "https://storage.googleapis.com/orbit-tenant-media/invoices/may-2026.pdf?X-Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Signature=...",
        "size": 348112
      }
    ]
  }'
```

### Inline base64 attachment

```bash theme={null}
curl -X POST https://api.orbit.devotel.io/api/v1/messages/email \
  -H "X-API-Key: dv_live_sk_..." \
  -H "Content-Type: application/json" \
  -d '{
    "to": "user@example.com",
    "from": "billing@yourdomain.com",
    "subject": "Receipt",
    "html": "<p>Receipt attached.</p>",
    "attachments": [
      {
        "filename": "receipt.pdf",
        "content_type": "application/pdf",
        "content": "JVBERi0xLjQKJeLjz9MK..."
      }
    ]
  }'
```

## Send with the Node.js SDK

```typescript theme={null}
import { Devotel } from "@devotel/orbit-sdk";
import { readFileSync } from "node:fs";

const orbit = new Devotel({ apiKey: process.env.ORBIT_API_KEY! });

await orbit.messages.send({
  channel: "email",
  to: "user@example.com",
  from: "billing@yourdomain.com",
  subject: "Your invoice for May 2026",
  html: "<p>Your invoice is attached.</p>",
  attachments: [
    {
      filename: "invoice-may-2026.pdf",
      content_type: "application/pdf",
      content: readFileSync("./invoice-may-2026.pdf").toString("base64"),
    },
  ],
});
```

## Other languages

The Node.js SDK (`@devotel/orbit-sdk`) and the browser SDK (`@devotel/orbit-web-sdk`) are GA today. First-party Python / Go / Java / PHP / Ruby / .NET SDKs are built and in beta, pending publication to their package registries — until then, call the REST endpoint above with any HTTP client.

## Validation errors

| Code               | HTTP | Cause                                                                                                                                                                                                                                                                                                                                                                                        |
| ------------------ | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VALIDATION_ERROR` | 422  | Filename contains `/`, `\\`, or `..`; MIME type not on the allowlist; total size exceeds 25 MB; more than 20 attachments; neither `url` nor `content` supplied; a `url`-style attachment could not be fetched (timeout, non-2xx response, or host outside the SSRF allowlist — re-host the file or use the `/v1/files` upload endpoint); or the fetched bytes exceed the per-file 10 MB cap. |

## See also

* [Channels → Email](/channels/email) — domain setup, tracking, suppression lists.
* [Files API](/api-reference/files) — pre-upload large attachments and reference them by URL.
* [Messaging API](/api-reference/endpoints/messaging) — full `POST /api/v1/messages/email` schema.
