# Iterable (/docs/v/0.6.1/adapters/iterable)



The Iterable adapter calls [Iterable's target email API](https://api.iterable.com/api/docs) over `fetch` — no extra dependency. Instead of sending raw content, it triggers an existing campaign template for exactly one recipient and hands your message to the template as `dataFields`.

<ProviderBadge adapter="iterable" />

## Configure [#configure]

Create a server-side API key in Iterable and copy the campaign ID of the template you want to trigger.

```ts title="lib/email.ts"
import { createEmailClient } from "@opencoredev/email-sdk";
import { iterable } from "@opencoredev/email-sdk/iterable";

export const email = createEmailClient({
  adapters: [
    iterable({
      apiKey: process.env.ITERABLE_API_KEY!,
      campaignId: Number(process.env.ITERABLE_CAMPAIGN_ID!),
    }),
  ],
});
```

<TypeTable
  type="{
  apiKey: {
    description: &#x22;Iterable API key.&#x22;,
    type: &#x22;string&#x22;,
    required: true,
  },
  campaignId: {
    description:
      &#x22;Campaign whose template renders the email. Non-numeric values throw an EmailValidationError when the adapter is created.&#x22;,
    type: &#x22;number&#x22;,
    required: true,
  },
  allowRepeatMarketingSends: {
    description: &#x22;Forwarded to Iterable to allow re-sending a marketing campaign to the same user.&#x22;,
    type: &#x22;boolean&#x22;,
  },
  dataFields: {
    description:
      &#x22;Extra template fields — a static object or a function of the message. Merged in before the normalized fields, so subject/html/text/from win on conflicts.&#x22;,
    type: &#x22;Record<string, unknown> | (message) => Record<string, unknown>&#x22;,
  },
  sendAt: {
    description: &#x22;Schedule the send — UTC timestamp in Iterable's expected format.&#x22;,
    type: &#x22;string&#x22;,
  },
  baseUrl: {
    description: &#x22;Override the API origin, e.g. for a proxy.&#x22;,
    type: &#x22;string&#x22;,
    default: '&#x22;https://api.iterable.com&#x22;',
  },
  fetch: {
    description: &#x22;Custom fetch implementation for tests or special runtimes.&#x22;,
    type: &#x22;typeof fetch&#x22;,
  },
}"
/>

## Send [#send]

The campaign template renders the email. The adapter passes `subject`, `html`, `text`, and `from` into `dataFields` so the template can use them, and forwards `metadata` as Iterable webhook metadata.

```ts
const result = await email.send({
  from: "Acme <hello@acme.com>",
  to: "user@example.com",
  subject: "Your trial ends in 3 days",
  html: "<p>Upgrade to keep your workspace.</p>",
  metadata: { userId: "user_123" },
});

console.log(result.raw); // Iterable returns no message id — inspect the raw response
```

<Callout type="warn" title="One recipient, template-only fields">
  Iterable target sends accept exactly one `to` recipient and no `cc`, `bcc`, `replyTo`, `headers`,
  `attachments`, or `tags` — any of those throw an `EmailValidationError` before a request is made.
  Check [field support](/docs/v/0.6.1/adapters/field-support) before routing fallbacks through Iterable.
</Callout>

## Verify from the CLI [#verify-from-the-cli]

```bash
ITERABLE_API_KEY="..." ITERABLE_CAMPAIGN_ID="12345" npx email-sdk doctor --adapter iterable
```

```bash
ITERABLE_API_KEY="..." npx email-sdk send \
  --adapter iterable \
  --campaign-id 12345 \
  --from "Acme <hello@acme.com>" \
  --to user@example.com \
  --subject "Iterable smoke test" \
  --text "It works" \
  --dry-run
```

`--campaign-id` and `--send-at` override `ITERABLE_CAMPAIGN_ID` and `ITERABLE_SEND_AT`. Drop `--dry-run` to trigger one real campaign send and confirm the API key and campaign are live.
