Skip to content
zap.ts
Esc
navigateopen⌘Jpreview
On this page

Swap a provider

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:

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:

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.

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:

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.

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:

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

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

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:

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.

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 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.

Last updated on September 22, 2026

Was this page helpful?