---
title: Swap a provider
description: Change the company that handles your payments, your email or your file storage, without rewriting your app.
---

zap.ts keeps outside companies behind its own packages. Your app calls
`@zap-ts/mail`, not Resend. It calls `@zap-ts/billing`, not Stripe.

So changing company is a settings change or a two-line change, not a rewrite.

This page covers the four you are most likely to change.

## Email

Email is the easiest. You change one setting.

Open the root `.env.schema` and find:

```bash
MAIL_PROVIDER=smtp
```

`MAIL_PROVIDER` accepts one of six values:

| Value      | What it does                                            |
| ---------- | ------------------------------------------------------- |
| `smtp`     | Sends to Mailpit on your computer. The default          |
| `console`  | Prints the email to your terminal instead of sending it |
| `resend`   | Sends with Resend                                       |
| `postmark` | Sends with Postmark                                     |
| `sendgrid` | Sends with SendGrid                                     |
| `brevo`    | Sends with Brevo                                        |

Pick one, then add its key. For Resend:

```bash
MAIL_PROVIDER=resend
RESEND_API_KEY=re_your_real_key
```

Put the real key in your `.env.local`, not in the schema.

If you name a provider and forget its key, the app stops and says so:

```
RESEND_API_KEY is required when MAIL_PROVIDER is "resend"
```

No code changes. Your emails are unchanged. See [Mail](/packages/mail).

## File storage

Storage speaks one shared protocol that most storage services support,
including Cloudflare R2 and MinIO.

Because they all speak the same protocol, you only change where you point.

On your computer, the settings point at MinIO:

```bash
R2_ACCOUNT_ID=zap_dev_r2_account
R2_ACCESS_KEY_ID=minioadmin
R2_SECRET_ACCESS_KEY=minioadmin
R2_BUCKET=zap
R2_ENDPOINT=http://127.0.0.1:9000
```

For a real service, put in your own account details and bucket name.

`R2_ENDPOINT` is the address of the storage service. Leave it empty when you
use Cloudflare R2 — R2 is worked out from the account ID. Set it when you use
anything else.

No code changes. See [Storage](/packages/storage).

## Payments

Payments need a code change, because each company stores subscriptions
slightly differently. It is still small: two import paths.

Seven companies are supported: Stripe, Polar, Chargebee, Dodo Payments, Creem,
Autumn and Commet.

**Step 1.** Open `packages/billing/src/server.ts`. Change the import path:

```ts
export { ... } from "./polar/server";
```

**Step 2.** Open `packages/billing/src/client.ts`. Change it to match:

```ts
export { ... } from "./polar/client";
```

**Step 3.** Add that company's keys to your `.env.schema` and `.env.local`.

**Step 4.** Open `packages/billing/src/plans.ts`. Each plan already has a line
for every company. Fill in the one for your new company with the real IDs from
its dashboard.

**Step 5.** Update the database:

```bash
pnpm run auth:generate
pnpm run db:migrate
```

You need this because the new company saves different fields. The first
command rewrites the table definition. The second applies it.

Nothing else in your app changes. Every company exports the same function
names, so your pages keep working. See [Billing](/packages/billing).

## AI models

AI is the one swap that lives in code, because each company ships its own
adapter package. `packages/ai/src/index.ts` names the `createOpenai*`
functions in three places: the imports, the `AIAdapters` type and the `AILive`
layer. Change those three, change `DEFAULT_MODELS` to the names the new
company uses, and declare its API key in the root `.env.schema`.

The seven adapters are independent, so you can keep text on one company and
speech on another. [AI](/packages/ai#changing-the-provider) has the steps, the
list of adapter packages, and which capabilities each one covers.

## Why this works

Each package exports the same names no matter which company is behind it.

Your app never imports `stripe` or `resend` directly. If it did, swapping
would mean finding every one of those imports.

Keep it that way in your own code. Import from `@zap-ts/mail`, not from the
mail company.

## Related

- [AI](/packages/ai) — how models are called
- [Billing](/packages/billing) — how payments are set up
- [Mail](/packages/mail) — how email is sent
- [Storage](/packages/storage) — how files are stored
- [Going to production](/guides/going-to-production) — the full checklist
