# Send Batch

`POST https://api.inboxjot.com/v1/emails/batch`

Sends up to 100 emails in one request. The body is an array of Send Email bodies. Inboxjot checks every email first, and queues none of them when one fails.

A batch cannot schedule emails or carry attachments. Send those one at a time. Send an optional `Idempotency-Key` header to retry safely. See Idempotency.

## Body

An array of 1 to 100 email objects, up to 10 MB in total. Each email takes the fields below, the same as Send Email without `scheduled_at` and `attachments`. When one email fails, the error message starts with its number, such as Email 2.

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `from` | string \| null | No | Sender on a verified domain, such as `Acme <hello@acme.dev>`, 3 to 320 characters. Required unless the template sets one. |
| `to` | string \| string[] | Yes | One address or a list. An address can carry a name, such as `Jane <jane@example.com>`, up to 640 characters each. At most 50 recipients across `to`, `cc` and `bcc`. Repeated addresses are sent once. |
| `cc` | string \| string[] | No | Carbon copy recipients. One address or a list. An address can carry a name, such as `Jane <jane@example.com>`, up to 640 characters each. |
| `bcc` | string \| string[] | No | Blind carbon copy recipients. One address or a list. An address can carry a name, such as `Jane <jane@example.com>`, up to 640 characters each. |
| `reply_to` | string \| string[] | No | Addresses that replies go to. One address or a list. An address can carry a name, such as `Jane <jane@example.com>`, up to 640 characters each. Defaults to the template’s reply-to. |
| `subject` | string \| null | No | Email subject, 1 to 998 characters, without line breaks. Required unless the template sets one. |
| `html` | string \| null | No | HTML body. Send `html`, `text` or both, unless the template has content. |
| `text` | string \| null | No | Plain text body. |
| `template` | object \| null | No | Send with a template. Its from, reply-to, subject and content fill the fields you leave out. The template content is used only when you send neither `html` nor `text`. |
| `template.id` | string | Yes | The template id, such as `tpl_06d2mfk8r1az4hb7cv5ke`. |
| `template.variables` | object | No | Values for the template variables, such as `{ "first_name": "Ada" }`. They fill the subject, preheader and content. |
| `headers` | object | No | Custom headers, up to 50, each with a string value and no line breaks. Allowed names are `In-Reply-To`, `References`, `List-Unsubscribe`, `List-Unsubscribe-Post` and most `X-` headers. A few `X-` names are reserved. |
| `tags` | object[] | No | Up to 10 tags, stored with the email and passed to webhooks. Use each name once. `email_id`, `workspace_id` and `campaign_id` are reserved. |
| `tags[].name` | string | Yes | 1 to 256 letters, numbers, `_` or `-`. |
| `tags[].value` | string | Yes | 1 to 256 letters, numbers, `_` or `-`. |

## Response

| Name | Type | Required | Description |
| --- | --- | --- | --- |
| `data` | object[] | No | One entry per email, in the order you sent them. |
| `data[].id` | string | No | The email id. |

## Request

cURL:

```bash
curl -X POST https://api.inboxjot.com/v1/emails/batch \
  -H "Authorization: Bearer $INBOXJOT_API_KEY" \
  -H "Content-Type: application/json" \
  -d '[
    {
      "from": "Acme <hello@acme.dev>",
      "to": "riti@example.com",
      "subject": "Your receipt from Acme",
      "html": "<p>Thanks for your order.</p>"
    },
    {
      "from": "Acme <hello@acme.dev>",
      "to": "marcus.lee@example.com",
      "subject": "Your receipt from Acme",
      "html": "<p>Thanks for your order.</p>"
    }
  ]'
```

Node.js:

```javascript
import { Inboxjot } from 'inboxjot';

const inboxjot = new Inboxjot();

const { data } = await inboxjot.emails.batch([
  {
    from: 'Acme <hello@acme.dev>',
    to: 'riti@example.com',
    subject: 'Your receipt from Acme',
    html: '<p>Thanks for your order.</p>',
  },
  {
    from: 'Acme <hello@acme.dev>',
    to: 'marcus.lee@example.com',
    subject: 'Your receipt from Acme',
    html: '<p>Thanks for your order.</p>',
  },
]);
```

Rust:

```rust
use inboxjot::{BatchEmail, Inboxjot};

let client = Inboxjot::from_env()?;

let receipt = |to: &str| BatchEmail {
    from: Some("Acme <hello@acme.dev>".into()),
    to: vec![to.into()],
    subject: Some("Your receipt from Acme".into()),
    html: Some("<p>Thanks for your order.</p>".into()),
    ..Default::default()
};

let result = client
    .emails()
    .batch(&[
        receipt("riti@example.com"),
        receipt("marcus.lee@example.com"),
    ])
    .await?;
```

Go:

```go
client := inboxjot.NewClient("")

result, err := client.Emails.Batch(ctx, []inboxjot.BatchEmailParams{
	{
		From:    "Acme <hello@acme.dev>",
		To:      []string{"riti@example.com"},
		Subject: "Your receipt from Acme",
		HTML:    "<p>Thanks for your order.</p>",
	},
	{
		From:    "Acme <hello@acme.dev>",
		To:      []string{"marcus.lee@example.com"},
		Subject: "Your receipt from Acme",
		HTML:    "<p>Thanks for your order.</p>",
	},
})
```

Python:

```python
from inboxjot import Inboxjot

inboxjot = Inboxjot()

result = inboxjot.emails.batch(
    [
        {
            "from": "Acme <hello@acme.dev>",
            "to": "riti@example.com",
            "subject": "Your receipt from Acme",
            "html": "<p>Thanks for your order.</p>",
        },
        {
            "from": "Acme <hello@acme.dev>",
            "to": "marcus.lee@example.com",
            "subject": "Your receipt from Acme",
            "html": "<p>Thanks for your order.</p>",
        },
    ]
)
```

## Responses

| Status | Description |
| --- | --- |
| 202 | Accepted. Inboxjot sends the emails in the background. |
| 400 | A field is missing or invalid, an attachment could not be read or downloaded, or the Idempotency-Key header is not valid. |
| 401 | The API key is missing, revoked or expired. |
| 403 | The from domain is not added or not verified, the key is limited to another domain, every recipient is on the suppression list, sending is paused for the workspace, or the request comes from outside the key’s IP allowlist. |
| 409 | A request with the same Idempotency-Key is still running. Retry after one second. |
| 413 | The body is larger than 10 MB. |
| 415 | The Content-Type header is not application/json. |
| 422 | The Idempotency-Key was already used with a different request. |
| 429 | The key made too many requests, or the workspace reached its daily sending limit. Wait for the seconds in the Retry-After header. |
| 500 | Something failed on our side. Retry later. |

202:

```json
{
  "data": [
    {
      "id": "eml_06d2mfk8r4tq9xv2bnc7s"
    },
    {
      "id": "eml_06d2mfk8r7bc5yn1qz8gh"
    }
  ]
}
```

400:

```json
{
  "name": "validation_error",
  "status": 400,
  "message": "The request body is not valid JSON",
  "errors": [
    {
      "message": "The request body is not valid JSON",
      "location": "body"
    }
  ],
  "request_id": "5f0c6a2e-8d1b-4c7a-9e3f-2b6d4a1c8e90"
}
```

401:

```json
{
  "name": "unauthorized",
  "status": 401,
  "message": "Unauthorized",
  "errors": [],
  "request_id": "5f0c6a2e-8d1b-4c7a-9e3f-2b6d4a1c8e90"
}
```

403:

```json
{
  "name": "forbidden",
  "status": 403,
  "message": "This key cannot be used from 203.0.113.7",
  "errors": [],
  "request_id": "5f0c6a2e-8d1b-4c7a-9e3f-2b6d4a1c8e90"
}
```

409:

```json
{
  "name": "concurrent_idempotent_requests",
  "status": 409,
  "message": "A request with this idempotency key is still in progress. Try again later.",
  "errors": [],
  "request_id": "5f0c6a2e-8d1b-4c7a-9e3f-2b6d4a1c8e90"
}
```

413:

```json
{
  "name": "payload_too_large",
  "status": 413,
  "message": "The request body is too large",
  "errors": [],
  "request_id": "5f0c6a2e-8d1b-4c7a-9e3f-2b6d4a1c8e90"
}
```

415:

```json
{
  "name": "unsupported_media_type",
  "status": 415,
  "message": "The Content-Type must be application/json",
  "errors": [],
  "request_id": "5f0c6a2e-8d1b-4c7a-9e3f-2b6d4a1c8e90"
}
```

422:

```json
{
  "name": "invalid_idempotent_request",
  "status": 422,
  "message": "This idempotency key was already used with a different request",
  "errors": [],
  "request_id": "5f0c6a2e-8d1b-4c7a-9e3f-2b6d4a1c8e90"
}
```

429:

```json
{
  "name": "rate_limit_exceeded",
  "status": 429,
  "message": "Too many attempts. Try again later.",
  "errors": [],
  "request_id": "5f0c6a2e-8d1b-4c7a-9e3f-2b6d4a1c8e90"
}
```

500:

```json
{
  "name": "unknown_error",
  "status": 500,
  "message": "Something went wrong. Please try again later.",
  "errors": [],
  "request_id": "5f0c6a2e-8d1b-4c7a-9e3f-2b6d4a1c8e90"
}
```
