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

# Custom headers

> Learn how to add custom headers to your emails.

## What are custom headers?

Custom headers let you attach RFC 5322 compliant headers to your messages. This is useful for message classification,
tracking, and per-recipient identifiers.

<Info>
  Some headers are restricted to prevent abuse, and MailChannels may limit how many times the same header can be repeated. See [Restricted headers](#restricted-headers) for a non-exhaustive list.
</Info>

### Adding custom headers

Include custom headers in the top-level `headers` object to apply them to every recipient of the message:

<CodeGroup>
  ```bash cURL theme={null}
  #!/usr/bin/env bash
  set -u
  : "${MAILCHANNELS_API_KEY:?Set MAILCHANNELS_API_KEY before running}"
  : "${FROM_EMAIL:?Set FROM_EMAIL (must be on a Domain-Lockdown-authorized domain)}"
  : "${TO_EMAIL:?Set TO_EMAIL}"

  curl -X POST https://api.mailchannels.net/tx/v1/send \
    -H "Content-Type: application/json" \
    -H "X-Api-Key: $MAILCHANNELS_API_KEY" \
    -d @- <<JSON
  {
    "personalizations": [
      { "to": [{ "email": "$TO_EMAIL", "name": "Recipient" }] }
    ],
    "from": {
      "email": "$FROM_EMAIL",
      "name": "Your Name"
    },
    "subject": "Hello from MailChannels",
    "content": [
      {
        "type": "text/plain",
        "value": "Hello from MailChannels."
      }
    ],
    "headers": {
      "X-Campaign-Id": "welcome-2026"
    }
  }
  JSON
  ```

  ```javascript Node.js theme={null}
  import { MailChannels } from "mailchannels-sdk";

  const { MAILCHANNELS_API_KEY, FROM_EMAIL, TO_EMAIL } = process.env;
  if (!MAILCHANNELS_API_KEY) throw new Error("Set MAILCHANNELS_API_KEY before running");
  if (!FROM_EMAIL) throw new Error("Set FROM_EMAIL (must be on a Domain-Lockdown-authorized domain)");
  if (!TO_EMAIL) throw new Error("Set TO_EMAIL");

  const mailchannels = new MailChannels(MAILCHANNELS_API_KEY);

  const { data, error } = await mailchannels.emails.send({
    from: { email: FROM_EMAIL, name: "Your Name" },
    to: { email: TO_EMAIL, name: "Recipient" },
    subject: "Hello from MailChannels",
    text: "Hello from MailChannels.",
    headers: {
      "X-Campaign-Id": "welcome-2026",
    },
  });

  if (error) {
    console.error("Send failed:", error);
    process.exit(1);
  }
  console.log(data);
  ```

  ```python Python theme={null}
  import os
  import sys

  import mailchannels

  if not os.environ.get("MAILCHANNELS_API_KEY"):
      sys.exit("Set MAILCHANNELS_API_KEY before running")

  from_email = os.environ.get("FROM_EMAIL")
  to_email = os.environ.get("TO_EMAIL")
  if not from_email:
      sys.exit("Set FROM_EMAIL (must be on a Domain-Lockdown-authorized domain)")
  if not to_email:
      sys.exit("Set TO_EMAIL")

  response = mailchannels.Emails.send(
      {
          "from": {"email": from_email, "name": "Your Name"},
          "to": [{"email": to_email, "name": "Recipient"}],
          "subject": "Hello from MailChannels",
          "text": "Hello from MailChannels.",
          "headers": {
              "X-Campaign-Id": "welcome-2026",
          },
      }
  )

  print(response)
  ```

  ```php PHP theme={null}
  <?php

  require_once __DIR__ . '/vendor/autoload.php';

  use MailChannels\Client;

  $api_key    = getenv('MAILCHANNELS_API_KEY') or exit("Error: MAILCHANNELS_API_KEY is not set\n");
  $from_email = getenv('FROM_EMAIL') or exit("Error: FROM_EMAIL is not set\n");
  $to_email   = getenv('TO_EMAIL') or exit("Error: TO_EMAIL is not set\n");

  $client = new Client(apiKey: $api_key);

  $response = $client->emails->send([
      'from'    => ['email' => $from_email, 'name' => 'Your Name'],
      'to'      => ['email' => $to_email, 'name' => 'Recipient'],
      'subject' => 'Hello from MailChannels',
      'text'    => 'Hello from MailChannels.',
      'headers' => [
          'X-Campaign-Id' => 'welcome-2026',
      ],
  ]);

  print_r($response->toArray());
  ```
</CodeGroup>

<Warning>
  Using custom headers incorrectly could significantly harm your email deliverability.
</Warning>

### Custom headers within personalizations

Custom headers can also be added to individual personalizations, allowing for recipient-specific headers. This is useful for
personalized unsubscribe links, tracking, or identification headers that vary per recipient.

<CodeGroup>
  ```bash cURL theme={null}
  #!/usr/bin/env bash
  set -u
  : "${MAILCHANNELS_API_KEY:?Set MAILCHANNELS_API_KEY before running}"
  : "${FROM_EMAIL:?Set FROM_EMAIL (must be on a Domain-Lockdown-authorized domain)}"
  : "${TO_EMAIL_1:?Set TO_EMAIL_1}"
  : "${TO_EMAIL_2:?Set TO_EMAIL_2}"

  curl -X POST https://api.mailchannels.net/tx/v1/send \
    -H "Content-Type: application/json" \
    -H "X-Api-Key: $MAILCHANNELS_API_KEY" \
    -d @- <<JSON
  {
    "personalizations": [
      {
        "to": [{ "email": "$TO_EMAIL_1" }],
        "headers": {
          "X-Campaign-Id": "welcome-2026-cohort-a"
        }
      },
      {
        "to": [{ "email": "$TO_EMAIL_2" }],
        "headers": {
          "X-Campaign-Id": "welcome-2026-cohort-b"
        }
      }
    ],
    "from": {
      "email": "$FROM_EMAIL",
      "name": "Your Name"
    },
    "subject": "Hello from MailChannels",
    "content": [
      {
        "type": "text/plain",
        "value": "Hello from MailChannels."
      }
    ]
  }
  JSON
  ```

  ```javascript Node.js theme={null}
  import { MailChannels } from "mailchannels-sdk";

  const { MAILCHANNELS_API_KEY, FROM_EMAIL, TO_EMAIL_1, TO_EMAIL_2 } = process.env;
  if (!MAILCHANNELS_API_KEY) throw new Error("Set MAILCHANNELS_API_KEY before running");
  if (!FROM_EMAIL) throw new Error("Set FROM_EMAIL (must be on a Domain-Lockdown-authorized domain)");
  if (!TO_EMAIL_1) throw new Error("Set TO_EMAIL_1");
  if (!TO_EMAIL_2) throw new Error("Set TO_EMAIL_2");

  const mailchannels = new MailChannels(MAILCHANNELS_API_KEY);

  // Each personalization can carry its own headers, which override the
  // top-level `headers` for that recipient.
  const { data, error } = await mailchannels.emails.send({
    from: { email: FROM_EMAIL, name: "Your Name" },
    personalizations: [
      { to: [{ email: TO_EMAIL_1 }], headers: { "X-Campaign-Id": "welcome-2026-cohort-a" } },
      { to: [{ email: TO_EMAIL_2 }], headers: { "X-Campaign-Id": "welcome-2026-cohort-b" } },
    ],
    subject: "Hello from MailChannels",
    text: "Hello from MailChannels.",
  });

  if (error) {
    console.error("Send failed:", error);
    process.exit(1);
  }
  console.log(data);
  ```

  ```python Python theme={null}
  import os
  import sys

  import mailchannels

  if not os.environ.get("MAILCHANNELS_API_KEY"):
      sys.exit("Set MAILCHANNELS_API_KEY before running")

  from_email = os.environ.get("FROM_EMAIL")
  to_email_1 = os.environ.get("TO_EMAIL_1")
  to_email_2 = os.environ.get("TO_EMAIL_2")
  if not from_email:
      sys.exit("Set FROM_EMAIL (must be on a Domain-Lockdown-authorized domain)")
  if not to_email_1:
      sys.exit("Set TO_EMAIL_1")
  if not to_email_2:
      sys.exit("Set TO_EMAIL_2")

  # Each personalization can carry its own headers, which override the
  # top-level `headers` for that recipient.
  response = mailchannels.Emails.send(
      {
          "from": {"email": from_email, "name": "Your Name"},
          "personalizations": [
              {
                  "to": [{"email": to_email_1}],
                  "headers": {"X-Campaign-Id": "welcome-2026-cohort-a"},
              },
              {
                  "to": [{"email": to_email_2}],
                  "headers": {"X-Campaign-Id": "welcome-2026-cohort-b"},
              },
          ],
          "subject": "Hello from MailChannels",
          "text": "Hello from MailChannels.",
      }
  )

  print(response)
  ```

  ```php PHP theme={null}
  <?php

  require_once __DIR__ . '/vendor/autoload.php';

  use MailChannels\Client;

  $api_key    = getenv('MAILCHANNELS_API_KEY') or exit("Error: MAILCHANNELS_API_KEY is not set\n");
  $from_email = getenv('FROM_EMAIL') or exit("Error: FROM_EMAIL is not set\n");
  $to_email_1 = getenv('TO_EMAIL_1') or exit("Error: TO_EMAIL_1 is not set\n");
  $to_email_2 = getenv('TO_EMAIL_2') or exit("Error: TO_EMAIL_2 is not set\n");

  $client = new Client(apiKey: $api_key);

  // Each personalization can carry its own headers, which override the
  // top-level `headers` for that recipient.
  $response = $client->emails->send([
      'from'             => ['email' => $from_email, 'name' => 'Your Name'],
      'personalizations' => [
          [
              'to'      => [['email' => $to_email_1]],
              'headers' => ['X-Campaign-Id' => 'welcome-2026-cohort-a'],
          ],
          [
              'to'      => [['email' => $to_email_2]],
              'headers' => ['X-Campaign-Id' => 'welcome-2026-cohort-b'],
          ],
      ],
      'subject' => 'Hello from MailChannels',
      'text'    => 'Hello from MailChannels.',
  ]);

  print_r($response->toArray());
  ```
</CodeGroup>

<Info>
  When a header is defined both globally and in a personalization object, the value in the personalization object is used.
</Info>

## Restricted headers

The following names cannot be used as keys in the `headers` JSON object. This list is non-exhaustive.

* Authentication-Results
* BCC
* CC
* Content-Transfer-Encoding
* Content-Type
* DKIM-Signature
* From
* Message-ID
* Received
* Reply-To
* Subject
* To
