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

# Channels & Providers

> Email, SMS, push, and web push delivered through your own provider connections.

Delivery in SenderKit has two layers. A **channel** is the kind of message — email,
SMS, push, or web push. A **provider** is the service that actually delivers it —
Postmark, Twilio, APNs, and so on. You build templates against channels; SenderKit
routes each send to the right provider behind a single interface.

## Channels

SenderKit supports four channels:

* **`email`**
* **`sms`**
* **`push`** — mobile push (APNs / FCM / Expo)
* **`web-push`** — browser notifications via the Web Push Protocol + VAPID

`push` and `web-push` are distinct channels. They differ in recipient format,
credentials, and payload shape, so each has its own provider connection. Both can be
active in a workspace simultaneously.

A [template](/concepts/templates) targets exactly one channel, fixed when it's
created. A send goes out on that channel; for [raw sends](/concepts/sending) you name
the channel explicitly.

## Providers

You connect your own provider accounts in the dashboard. SenderKit supports:

| Channel    | Providers                                                 |
| ---------- | --------------------------------------------------------- |
| `email`    | Postmark, SendGrid, Amazon SES, Mailgun, SparkPost        |
| `sms`      | Twilio, Amazon SNS, Vonage, MessageBird                   |
| `push`     | APNs, FCM, Expo                                           |
| `web-push` | VAPID (Web Push Protocol — no third-party service needed) |

For `web-push`, connect a VAPID keypair in the dashboard (public key + private key +
contact email). The recipient of each send is the JSON-encoded browser
`PushSubscription` object (endpoint + `p256dh`/`auth` keys) — your application
stores these after the user grants permission, then passes the JSON string as `to` on
each send.

Credentials are encrypted at rest. Each channel has one **default** connection, and a
template can override that default to use a specific connection.

## Built-in email sender

Every workspace comes with a built-in email connection that works immediately — no
provider account or credentials required. Email sends from `@tx.senderkit.email` (a
dedicated sending domain), so you can start sending in live mode on day one and
connect your own provider later when you're ready to use a custom from-address.

The built-in sender enforces caps per plan to protect the shared domain's reputation.
The caps depend on whether you're using the shared `@tx.senderkit.email` sender or a
[verified custom sending domain](#custom-sending-domains):

**Shared sender (`@tx.senderkit.email`)**

| Plan    | Monthly cap | Daily cap |
| ------- | ----------- | --------- |
| Free    | 500         | 50        |
| Starter | 1,500       | 150       |
| Pro     | 15,000      | 1,500     |

The shared sender is capped below the plan's message quota because
heavy volume on `tx.senderkit.email` would affect every workspace's
deliverability. Verifying a custom sending domain moves you to the higher
tier below, isolating your reputation to your own domain.

**Verified custom sending domain** (Starter and Pro only)

| Plan    | Monthly cap | Daily cap |
| ------- | ----------- | --------- |
| Starter | 5,000       | 500       |
| Pro     | 50,000      | 5,000     |

Sends that exceed the cap return HTTP `402` with code `managed_send_limit_reached`.
Once you connect your own provider and set it as the workspace default, it replaces
the built-in sender and the caps no longer apply.

## Custom sending domains

Starter and Pro workspaces can connect their own sending domains and send from any
address on a verified domain — without the shared `@tx.senderkit.email` address
appearing to recipients.

Adding a domain creates a dedicated **managed connection** in the Channels page.
The built-in shared sender remains as the implicit fallback; your domain connection
can be set as the workspace default or targeted per template.

### DNS records to publish

After clicking **Add domain** in **Dashboard → Channels**, you'll see the required
and recommended DNS records:

| Record                          | Type | Purpose                       | Required?   |
| ------------------------------- | ---- | ----------------------------- | ----------- |
| `senderkit._domainkey.{domain}` | TXT  | DKIM public key (white-label) | Yes         |
| `send.{domain}`                 | MX   | Custom MAIL FROM subdomain    | Yes         |
| `send.{domain}`                 | TXT  | MAIL FROM SPF                 | Yes         |
| `_dmarc.{domain}`               | TXT  | DMARC policy                  | Recommended |
| `{domain}`                      | TXT  | Root domain SPF               | Recommended |

The dashboard probes DNS live and shows a green **Set** indicator once each record is
detected. Recommended records improve deliverability reputation (e.g. third-party
checkers like MXToolbox test the root domain) but don't gate verification. The
dashboard shows the exact record values to copy — if you already have a root SPF
record, merge the SenderKit SPF mechanism into it rather than publishing a second
`v=spf1` record.

### Verification

SenderKit polls DNS every 5 minutes. You can also click **Check now** at any time.
Verification typically completes within minutes once records propagate. Claims expire
after 72 hours — if the domain isn't verified in time, you can resubmit.

### From-address override on raw sends

Once a domain is verified, `sendRaw` accepts a `from` address whose domain exactly
matches the verified domain:

```ts theme={null}
await senderkit.sendRaw({
  channel: "email",
  to: "customer@example.com",
  from: "billing@acme.com",   // acme.com must be a verified custom sending domain
  content: { subject: "Your invoice", html: "…" },
});
```

The `from` override is only honored on raw sends — template sends route through the
template's configured connection.

### Caps

Connections on a verified custom domain use the **verified custom sending domain** cap
tier shown in the table above — higher than the shared `@tx.senderkit.email` caps
because your domain's reputation is isolated from other workspaces. The caps are still
enforced as a guard on the shared SES account's overall AWS quota.

<Note>
  Custom sending domains require a **Starter** or **Pro** plan. Free workspaces
  see an upgrade prompt in the Channels page.
</Note>

## How a send picks a provider

You don't choose a provider per send — SenderKit resolves one for you:

1. The template's **provider override**, if it has one.
2. Otherwise, the **workspace default** for the channel.
3. For `email`, if no custom provider is configured the built-in sender is used
   (subject to the caps above).
4. For other channels without a configured provider (in live mode), the send fails
   with `provider_not_configured`.

<Note>
  Provider **failover is not available yet** — it's planned. Today, if the chosen
  provider rejects a message, SenderKit retries the same connection a few times and
  then marks the message [`failed`](/concepts/messages). It does not automatically
  fall back to a different provider.
</Note>

In [test mode](/concepts/environments), no provider is called at all — SenderKit
short-circuits delivery, so an unconfigured channel still "sends" fine while you're
developing.

<CardGroup cols={2}>
  <Card title="Sending" icon="paper-plane" href="/concepts/sending">
    How a send is dispatched to a provider.
  </Card>

  <Card title="Messages" icon="list-check" href="/concepts/messages">
    Which provider handled a message, and what happened.
  </Card>
</CardGroup>
