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

# Unisender plugin

> Email marketing and transactional email with Unisender - manage lists, contacts, custom fields and tags, subscribe and import contacts, send transactional email, and create and list campaigns.

Use **Unisender** through Corsair: one client, typed API calls.

Find the key in Unisender under Settings, Integration and API (Интеграция и API); the dashboard asks for the account password before it shows the whole key. Calls go to `https://api.unisender.com/en/api/<method>` and pass the key in the form body. The free plan includes API access.

* **The English docs are gone.** `unisender.com/en/support/api/` answers `410 Gone`. The Russian pages under `/ru/support/api/` still document every method in full, and the field names are identical.
* **Wrong key versus no key.** A wrong key answers `401 invalid_api_key`; *no* key at all answers `403 access_denied`. Both are treated as auth errors, so a missing credential surfaces as one either way.
* **Double opt-in.** `contacts.subscribe` takes `double_optin` of 0, 3 or 4, and 0 is the default. 0 sends a confirmation email and the contact stays `invited`, outside the list, until they accept; an `invited` address cannot be mailed. 3 adds the contact as `new` with no email, for consent you already hold. 4 adds a contact who is already `new` or `active` silently and sends the invitation to anyone else. Any other value is refused. Omitting `double_optin` therefore sends an email.
* **Unsubscribe versus exclude.** `contacts.unsubscribe` marks the contact unsubscribed, and only the contact can reverse that from a confirmation email. `contacts.exclude` only removes them from the given lists, and they can be subscribed again later.
* **Imports skip some addresses.** `contacts.import` takes at most 500 rows per call. List membership is a column: add `email_list_ids` to `field_names` with comma-separated list ids in each row. Unisender deliberately skips role addresses (`sales@`, `no-reply@`) and spam traps and counts them under `invalid` (and `role_based`), with the reason in `log`, so an import that inserts fewer rows than it was sent is normal, not a failure. Imports send no email.
* **Exports are asynchronous.** `contacts.export` returns a `task_uuid` and posts the file to your `notify_url` when it is ready. Unisender's `async/getTaskResult` method reports the task and, once it is `completed`, a `file_to_download` URL; this plugin does not wrap it yet, so pass a `notify_url` you can receive.
* **Sending limits.** `messages.sendEmail` accepts a body up to 1 MB, is limited to 1,000 messages a day and 60 calls a minute. `messages.createEmail` accepts a body up to 8 MB. The general allowance is 1,200 calls a minute.
* **Delivery status is short-lived.** `messages.status` takes up to 500 `email_id` values from `messages.sendEmail`, and Unisender keeps those records for one month. A status beginning `err_` is a failure, `ok_` is a success or still in progress, and `not_sent` is its own case.
* **A campaign without `start_time` sends immediately.** `campaigns.create` needs a `message_id` from `messages.createEmail`, and `campaigns.list` needs a `from` date and returns at most 10,000 rows a page.

**What you get:**

* 19 typed API operations

## Setup

**1. Install**

```bash npm theme={null}
npm install corsair @corsair-dev/unisender better-sqlite3
npm install --save-dev @types/better-sqlite3
```

```bash yarn theme={null}
yarn add corsair @corsair-dev/unisender better-sqlite3
yarn add --dev @types/better-sqlite3
```

```bash pnpm theme={null}
pnpm install corsair @corsair-dev/unisender better-sqlite3
pnpm install --save-dev @types/better-sqlite3
```

```bash bun theme={null}
bun add corsair @corsair-dev/unisender better-sqlite3
bun add --dev @types/better-sqlite3
```

**2. Add the plugin**

```ts corsair.ts theme={null}
import Database from 'better-sqlite3';
import { createCorsair } from 'corsair';
import { unisender } from '@corsair-dev/unisender';

export const corsair = createCorsair({
	plugins: [
		unisender(),
	],
	database: new Database('corsair.db'),
	kek: process.env.CORSAIR_KEK!,
	multiTenancy: true,
	hub: {
		projectApiKey: process.env.CORSAIR_API_KEY!,
		signingSecret: process.env.CORSAIR_SIGNING_SECRET!,
	},
});
```

With `multiTenancy: true`, scope calls with `corsair.withTenant(id)`. See [Quick start](https://docs.corsair.dev/quick-start.md) for KEK + Hub keys, and [Multi-tenancy](https://docs.corsair.dev/concepts/multi-tenancy.md) for account isolation.

**3. Choose authentication**

> [tab] API Key (Recommended)
>
> No setup required yet. When you make your first request as a tenant, Corsair prompts for the API key.
>
> ```ts theme={null}
> unisender()
> ```
>
> More: [API Key](https://docs.corsair.dev/concepts/api-key)

**4. Connect a tenant**

Mint a connect link and send the tenant to it. Hub hosts the page and delivers the result to your app — see [Connect / OAuth](https://docs.corsair.dev/management/connect).

```ts theme={null}
const { connectUrl } = await corsair.manage.connect.createLink({
	plugin: 'unisender',
	tenantId: 'acme',
});
// redirect the user's browser to connectUrl
```

## Example API calls

**List contact lists**

```ts theme={null}
const tenant = corsair.withTenant('acme');
await tenant.unisender.api.lists.list({});
```

**Create a contact list**

```ts theme={null}
const tenant = corsair.withTenant('acme');
await tenant.unisender.api.lists.create({ title: 'Newsletter' });
```

See the full list on the [API](https://docs.corsair.dev/plugins/unisender/api.md) page.

## What's next

> [card] [API reference](https://docs.corsair.dev/plugins/unisender/api.md)
>
> Every `unisender.api.*` operation with input and output types.

> [card] [Connect / OAuth](https://docs.corsair.dev/management/connect)
>
> createLink, Hub delivery, and tenant connect flows.

> [card] [Use with agents](https://docs.corsair.dev/mcp-adapters/mcp-adapters)
>
> Expose this plugin's operations as MCP tools.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.