← Plugin catalog
Communication

Emailit

Emailit v1.0.1

Send email and run campaigns

Create a free account to save this plugin.

Competitor profile

Developer declarations; not independently tested. Listing last checked Nov 4, 2026. Package observed Nov 3, 2026.

Declared capabilities

  • Send and schedule email
  • Track delivery and bounces
  • Set up and verify domains

Declared limitations

  • Unrelated creative writing request
  • Personal inbox access is out of scope
  • Purchased list sending violates the acceptable use policy

Not specified in the available declarations: pricing & account requirements.

Onboarding example

Which of my Emailit sending domains are not verified yet?

View onboarding source →

View package source →

Description

From the marketplace listing

Emailit is an email delivery platform for transactional email, SMTP and email marketing. This plugin lets ChatGPT and Codex work with your Emailit account: send transactional email from your verified sending domains, check whether a message was delivered, opened or clicked, and find out why an email bounced or was marked as spam. You can also add and verify sending domains with step-by-step DNS help, manage contacts, audiences, suppressions and templates, draft and send marketing campaigns, run automations, verify email lists, and set up webhooks. Sign in with your Emailit account and choose which workspaces to allow: all of them, or only the ones you select. Tools that send email or delete data are marked so ChatGPT and Codex ask you before running them. Codex users also get skills for integrating the Emailit API and SDKs into their code.

Language: English · Automatically detected from descriptions.

What changed

Keywords

Files & skills

File archives

Plugin package32 files · 75.1 KBBrowse files →
Skill instructions
emailit8.28 KB

View saved version →

---
name: emailit
description: This skill should be used when the user asks to "send an email with Emailit", "check if an email was delivered", "list my Emailit domains", "add a contact", "create a template", "manage suppressions", "create an API key", or mentions Emailit, api.emailit.com, or the Emailit MCP server. Covers choosing between the Emailit MCP tools and the REST API v2, safe sending, IDs, pagination, and error handling.
---

# Emailit

Emailit is an email platform for transactional email, marketing campaigns, automations, contacts, and sending domains. Work with it through the hosted MCP server when it is connected, and through the REST API v2 when writing application code.

## Pick the interface

1. **MCP tools are available** (tool names like `send-email`, `list-domains`, `get-current-workspace`): use them for anything the user wants done now, in their account.
2. **The user is writing code**: use the REST API or an official SDK. Load the `emailit-sdk` skill for language specific code.
3. **Neither**: explain how to connect the MCP server (see `emailit-get-started`) or ask for an API key stored in `EMAILIT_API_KEY`.

Never paste an API key into code, chat, or a commit. Keys start with `secret_`. Read them from the environment.

## First call

Call `get-current-workspace` before any other tool. It returns the default workspace name and ID, your role in it, the auth type (`oauth` or `api_key`), and the granted scope (`sending`, `full`, or both, such as `sending full`). Tell the user which workspace you are acting on before you change anything.

## Multiple workspaces

An OAuth connection acts as the signed-in user and can reach every workspace they allowed when connecting: all of them, or a chosen set.

- `list-workspaces` returns only the workspaces this connection may use, with the user's role (`ADMIN` or `MEMBER`) in each. Workspace owners count as `ADMIN`.
- When the user says "in Acme Client, do X", pass `workspace: "Acme Client"` (an ID or the exact name) to that tool call. It applies to that call only. Do not switch first.
- `switch-workspace` changes the default workspace for later calls. Use it only when the user wants to keep working in another workspace.
- If a workspace the user names is not in `list-workspaces`, the connection does not have access to it. Tell the user to add it under **Account > Connected apps > Edit access** in the Emailit dashboard. No reconnect is needed.
- Members can use every tool except creating, renaming, deleting, or regenerating API keys and deleting domains. Those need the Admin role. When a tool returns `admin_role_required`, tell the user that a workspace admin must do it or change their role, and do not retry.

API key connections are bound to the one workspace the key belongs to. They have no `workspace` argument and no role limits.

## Safety rules

Some tools reach real people or remove data. Before calling them, show the user exactly what will happen and wait for a clear yes.

- **Sends to real recipients**: `send-email`, `retry-email`, `forward-email`, `send-campaign`, `start-automation`, `trigger-automation`. Confirm the from address, recipients, subject, and content. Offer to send a test to the user first.
- **Destructive**: `delete-*`, `bulk-delete-contacts`, `bulk-unsubscribe-contacts`, `update-contact` (replaces audiences), `update-campaign`, `update-automation`, `stop-automation`, `cancel-email`, `cancel-campaign`, `regenerate-api-key`, `reset-webhook-secret`, `reset-form-token`. Name the object by its human name and ID.
- **External calls**: `test-webhook` and the webhook retries send requests to the user's URL; `create-webhook` and `update-webhook` point events at a URL; `verify-domain` and `verify-email` query DNS and mail servers; `publish-form` makes a form public.

Never send to purchased or scraped lists. Emailit requires consent from every recipient. Never invent a from address: it must belong to a verified sending domain in the workspace (check with `list-domains`).

## Tools by area

The server exposes 113 tools. Clients may trim them with toolsets, so some may be missing.

| Area | Main tools |
| --- | --- |
| Workspace | `get-current-workspace`, `list-workspaces`, `switch-workspace`, `create-workspace` |
| Emails | `send-email`, `list-emails`, `get-email`, `get-email-body`, `get-email-meta`, `cancel-email`, `retry-email`, `forward-email` |
| Domains and DMARC | `create-domain`, `verify-domain`, `list-domains`, `get-dmarc-stats`, `list-dmarc-sources` |
| Templates | `create-template`, `update-template`, `publish-template` |
| Audiences and contacts | `create-audience`, `add-audience-subscriber`, `create-contact`, `list-contacts`, `export-contacts` |
| Suppressions | `list-suppressions`, `create-suppression`, `delete-suppression` |
| Campaigns | `create-campaign`, `update-campaign`, `send-campaign`, `cancel-campaign` |
| Automations | `create-automation`, `start-automation`, `list-automation-runs`, `get-automation-stats` |
| Forms | `create-form`, `publish-form` |
| Verification | `verify-email`, `create-verification-list`, `get-verification-list-results` |
| Webhooks and events | `create-webhook`, `test-webhook`, `list-events` |
| API keys | `create-api-key`, `list-api-keys`, `regenerate-api-key` |

The full list with scopes is in `references/tools.md`.

## Scopes

- `sending` scope covers `get-current-workspace`, `list-workspaces`, `switch-workspace`, and sending: `send-email`, `update-email`, `cancel-email`, `retry-email`, `forward-email`. It cannot read emails back (`list-emails`, `get-email`) or list domains.
- `full` scope covers everything. A connection can hold both.

On OAuth, the AI client requests the scopes when it connects (both, unless it asks for fewer), the consent page lists them, and the user picks the workspaces. A sending-only OAuth connection still lists every tool, and the others return an insufficient scope error. A sending API key hides the tools it cannot call.

When a tool returns an insufficient scope error, tell the user. On OAuth, reconnect so the client requests `full` (some clients offer this from the error). API key users need a key with full access.

## Working with results

- IDs have prefixes: `em_` email, `sed_` sending domain, `tem_` template, `aud_` audience, `sub_` subscriber, `con_` contact, `sup_` suppression, `wh_` webhook, `cmp_` campaign, `aut_` automation, `frm_` form, `evl_` verification list, `evt_` event, `key_` API key.
- Most list tools take `page` and `limit` (up to 100), and many accept `search`, `sort`, `order`, and `filters` as `[{field, condition, value}]`. `list-automations` and `list-automation-runs` take `page` and `per_page` instead, with plain filter arguments such as `status`. Fetch only what you need.
- Results come back as structured JSON. Quote IDs exactly; never guess them.
- Times are ISO 8601 in UTC. Convert to the user's time zone when you present them.

## Errors

| Status | Meaning | What to do |
| --- | --- | --- |
| 400 or 422 | Validation failed | Read the field errors, fix the input, retry once |
| 401 | Not authenticated | Reconnect the MCP server or check the API key |
| 403 | Missing scope or plan feature | Explain what is needed |
| 403 `admin_role_required` | The user is a Member in that workspace | Tell the user an Admin must do it; do not retry |
| 404 | Not found in this workspace | Check the ID and the workspace, or pass `workspace` for the right one |
| 409 | Conflict, such as a duplicate | Fetch the existing object instead |
| 429 | Rate limited | Wait, then retry with fewer calls |
| 5xx | Emailit error | Retry later; do not loop |

Sending is limited per workspace: messages per second and per day, set for each workspace (2 per second and 5,000 per day when none are set). Other tools are not covered by these limits. A 429 on `send-email` means wait for `retry_after` seconds. Details are in `references/rest-api.md`.

## Common tasks

- **Was my email delivered?** `list-emails` with `rcpt_to` and a date range, then `get-email` for status and events. Load `emailit-deliverability` for bounces.
- **Set up a domain**: load `emailit-domain-setup`.
- **Campaigns and automations**: load `emailit-campaigns`.
- **Webhooks**: load `emailit-webhooks`.
- **Code integration**: load `emailit-sdk`.

## Without MCP

Call `https://api.emailit.com/v2` with `Authorization: Bearer $EMAILIT_API_KEY`. Endpoints, request shapes, and curl examples are in `references/rest-api.md`.

Referenced files: 2 — file references are metadata, not package contents.

emailit-campaigns6.13 KB

View saved version →

---
name: emailit-campaigns
description: This skill should be used when the user asks to "create a campaign", "send a newsletter", "schedule a campaign", "build an audience", "import contacts", "create a welcome series", "set up an automation", "add a signup form", or "see campaign results" in Emailit. Covers audiences, contacts, campaigns, automations, and signup forms, with safe sending.
---

# Emailit campaigns, automations, and audiences

Marketing email in Emailit is built from audiences (lists of subscribers), campaigns (one send to one or more audiences), automations (event driven flows), and forms (signup capture). Every recipient must have opted in.

## Before anything sends

- Confirm the workspace with `get-current-workspace`.
- Confirm a verified sending domain with `list-domains`. The campaign `from_email` must use it.
- Confirm consent: never send to purchased, rented, or scraped lists. If the user mentions one, decline and explain why.
- Show a summary and wait for an explicit yes before `send-campaign`, `trigger-automation`, or `start-automation`.

## Audiences and contacts

- Create a list: `create-audience` with `name`.
- Add someone: `add-audience-subscriber` with the audience `id`, `email`, and optional `first_name`, `last_name`, `custom_fields`. It creates the contact if needed.
- Contacts across all audiences: `list-contacts`, `get-contact`, `create-contact` (can join `audiences` directly), `update-contact`.
- Many at once, up to 100 contact IDs per call: `bulk-add-contacts-to-audience`, `bulk-remove-contacts-from-audience`, `bulk-unsubscribe-contacts`, `bulk-resubscribe-contacts`, `bulk-delete-contacts`. Confirm deletes and unsubscribes, and only resubscribe people who asked for email again.
- Export: `export-contacts` returns CSV for the matching contacts.

For large imports (thousands of rows) point the user to CSV import in the dashboard; tool calls one at a time are slow and rate limited.

## Campaigns

Flow: **create draft, set recipients, review, test, send or schedule.**

1. `create-campaign` with `name`, `subject`, `from_email`, `from_name`, `preview_text`, `content`, and `content_type` (use `html`, or `text` for plain text). It returns a draft (`cmp_...`).
2. `update-campaign` with `recipients: [{ "audience_id": "aud_..." }]`. Add `"exclude": true` to leave out an audience. Passing `recipients` replaces the whole list, and at least one included audience is required.
3. Review: `get-campaign`. Read back the subject, from, and `recipients` (audiences and exclusions); it has no recipient count. For a rough size, `get-audience` shows each audience's `subscribers_count`, before exclusions, unsubscribes, and suppressions. Show the content summary.
4. Test: offer to `send-email` the same subject and HTML to the user's own address first.
5. Send: after a clear yes, `send-campaign` with the `id`. Add `scheduled_at` (ISO 8601) to schedule it instead.
6. Stop it: `cancel-campaign` works on draft and sending campaigns. A scheduled campaign cannot be canceled; `delete-campaign` removes it so it never sends. Confirm either one first.

Personalize with `{{first_name}}`, `{{last_name}}`, `{{email}}`, `{{cf.<field>}}` for custom fields, and `{{unsubscribe_url}}`. Empty values render as blank, so write copy that still reads well without a first name. Emailit adds List-Unsubscribe headers but no unsubscribe link or footer to the body, so every campaign body must contain `{{unsubscribe_url}}`.

Campaign writing guidance and an HTML starter are in `references/campaigns.md`.

### Results

`get-campaign` returns the status, `sent_at`, and the recipient audiences. Engagement totals per campaign are shown in the dashboard report. Through the tools, use `list-events` with `type` such as `campaign.sent`, `email.clicked`, or `email.bounced`, and note that opens are approximate.

## Automations

An automation is a graph of steps. A trigger step starts a run; action steps run in order along connections.

1. Pick the `context`:
   - `contact`: runs per contact (audience joins, updates, dates, clicks).
   - `email`: runs per email event (delivered, bounced, received).
   - `event`: runs on a custom `event.*` trigger.
2. `create-automation` with `name`, `context`, `steps`, and `connections`. It is created as a draft.
3. Check it with `get-automation`. Explain the flow in plain words.
4. `start-automation` after the user confirms. `pause-automation` and `stop-automation` stop new runs.
5. Monitor with `list-automation-runs` (`page` and `per_page`, optional `status`), `get-automation-run`, `get-automation-stats`, and `get-automation-step-stats`.

Step shape:

```json
{ "key": "welcome", "type": "action", "action": "send_email", "config": { "type": "template", "template_id": "tem_..." } }
```

Connections join step keys: `{ "from": "trigger", "to": "welcome" }`. After a `condition` step use `"branch": "yes"` or `"branch": "no"`. After an `experiment` step use the variant key as the branch.

Valid triggers and actions for each context, config fields, and full examples are in `references/automations.md`. `send_email` steps send a published template, so create and publish the template first (`create-template`, `publish-template`).

## Forms

- `create-form` with `name` and `type` (`popup`, `full_page`, `flyout`, `embed`, or `banner`). The form `definition` and `settings` are easiest to design in the dashboard editor; offer to create a draft and let the user style it there.
- `publish-form` starts collecting signups; `unpublish-form` stops it.
- `reset-form-token` invalidates the old embed token. Embedded forms stop working until the snippet is updated, so confirm first.

## Common requests

| Request | Tools |
| --- | --- |
| "Send our April newsletter to Customers" | `list-audiences`, `create-campaign`, `update-campaign`, `get-campaign`, then confirm and `send-campaign` |
| "Welcome new subscribers" | `create-template`, `publish-template`, `create-automation` (trigger `contact.added_to_audience` with `audience_id`), `start-automation` |
| "How did last week's campaign do?" | `list-campaigns`, `get-campaign` |
| "Remove everyone who bounced from this list" | `list-suppressions` (type bounce), `list-audience-subscribers`, `remove-audience-subscriber` after confirming |

Referenced files: 2 — file references are metadata, not package contents.

emailit-deliverability5.65 KB

View saved version →

---
name: emailit-deliverability
description: This skill should be used when the user asks "why did my email bounce", "why are my emails going to spam", "was this email delivered", "check my DMARC reports", "clean my email list", "remove a suppression", "lower my bounce rate", or "improve deliverability" in Emailit. Covers investigating delivery status, bounces, complaints, suppressions, DMARC data, list verification, and sending practices.
---

# Emailit deliverability

Answer with evidence from the account, not general advice. Pull the data first, then explain the cause and the fix.

## Investigate one email

1. Find it: `list-emails` with `rcpt_to`, `subject`, or `mail_from`, plus `date_from` and `date_to`. Use `status` to narrow to `bounced`, `failed`, `rejected`, `suppressed`, `complained`, or `held`.
2. Open it: `get-email` with the ID. Read:
   - `status`: the current state. Meanings are in `references/statuses.md`.
   - `deliveries[]`: each delivery attempt with `status`, `output` (the receiving server's SMTP reply), and `details`. The SMTP reply is the most useful evidence for a bounce.
   - `spam_score` and `spam_checks`: Emailit's own content scan.
   - `sending_domain`: the SPF, DKIM, DMARC, and return path status of the domain it was sent from.
3. Explain the cause in plain words, quoting the SMTP reply. Then give one concrete fix.

Common SMTP replies:

| Reply contains | Meaning | Fix |
| --- | --- | --- |
| `550 5.1.1`, "user unknown", "does not exist" | Hard bounce: the mailbox does not exist | Remove the address. Emailit suppresses it automatically if auto suppression is on |
| `552`, "mailbox full", "over quota" | Mailbox full | Retry later with `retry-email`; remove if it repeats |
| `421`, `451`, "try again later", "rate limited" | Temporary deferral | Nothing; Emailit retries. Status stays `attempted` meanwhile |
| `550 5.7.1`, "spam", "policy", "blocked" | Content or reputation block | Check DMARC alignment, spam score, links, and recent complaint rate |
| "SPF", "DKIM", "DMARC", "authentication" | Authentication failed | Fix the domain records (`emailit-domain-setup`) |

## Investigate a trend

- Volume and outcomes: `list-emails` with `status` and a date range; compare counts for `delivered`, `bounced`, and `complained`.
- Workspace events: `list-events` with `type` such as `email.bounced` or `email.complained` and `include_data: true`.
- Per domain authentication: `get-dmarc-stats`, `list-dmarc-sources`, and `list-dmarc-reporters` for the domain. See `references/dmarc.md`.

Benchmarks to compare against: bounce rate under 2 percent, complaint rate under 0.1 percent. Higher numbers put the account's reputation at risk and can pause sending.

## Suppressions

What a suppression blocks depends on its type and on how the email is sent:

- **API and SMTP sends** are blocked only by a `recipient` suppression. The email gets status `suppressed`. `bounce`, `complaint`, and `unsubscribe` entries do not stop them.
- **Campaigns** skip every address with a suppression of any type, plus unsubscribed contacts and subscribers.
- A suppression whose `keep_until` has passed no longer blocks anything.

Managing them:

- Automatic suppression (a workspace setting) adds a `recipient` suppression after bounces and a `complaint` suppression after a spam complaint. Pro and Business can limit it to one of the two or turn it off; other plans always have both on.
- Find one: `list-suppressions` with `search`. Read it with `get-suppression`.
- Add one: `create-suppression` with `email`, an optional `type` (default `recipient`, the only type that also stops API and SMTP sends), an optional `reason`, and optional `keep_until`.
- Remove one: `delete-suppression`. Only do this when the user confirms the address is valid and the person wants the email. Never remove a `complaint` or `unsubscribe` suppression just to get a campaign out; that breaks consent rules and hurts reputation.

## Verify addresses before sending

- One address: `verify-email` with `mode: "fast"` (syntax, MX, disposable, role) or `"full"` (adds an SMTP mailbox check). Both use verification credits; tell the user before running them.
- A list: `create-verification-list`, then poll `get-verification-list` until it finishes, then `get-verification-list-results`. Results are `safe`, `invalid`, `disposable`, `disabled`, `inbox_full`, `role`, or `unknown`. Suggest removing `invalid`, `disabled`, and `disposable` addresses, and reviewing `role`, `inbox_full`, and `unknown` ones before mailing them.

## Checklist for "my emails go to spam"

Work through these with real data and report each one:

1. **Authentication**: `get-domain` shows SPF, DKIM, and return path `ok`. A DMARC record exists.
2. **Alignment**: DMARC stats show passing alignment for Emailit sources. Unknown sources sending as the domain point to spoofing or a forgotten service.
3. **List quality**: recent bounce rate under 2 percent. Run list verification on old or imported lists.
4. **Complaints**: complaint rate under 0.1 percent. Every marketing email needs a working unsubscribe; campaign emails get a List-Unsubscribe header automatically.
5. **Content**: `spam_score` on recent emails. Avoid link shorteners, image only emails, and mismatched link text.
6. **Consistency**: a recognizable from name and address, and a steady sending volume. Warm up new domains by starting small.
7. **Separation**: transactional and marketing mail on different subdomains.

## Retrying

`retry-email` works only for `bounced`, `failed`, `suppressed`, or `held` emails less than 30 days old. It sends a new copy to the recipient with a new email ID, and a `recipient` suppression still blocks it. Confirm with the user, and do not retry hard bounces to addresses that do not exist.

Referenced files: 2 — file references are metadata, not package contents.

emailit-domain-setup4.22 KB

View saved version →

---
name: emailit-domain-setup
description: This skill should be used when the user asks to "add a sending domain", "verify my domain in Emailit", "set up SPF", "set up DKIM", "fix DNS records", "why is my domain not verified", "add a tracking domain", or "receive inbound email". Covers adding a domain, publishing the Emailit DNS records at any DNS provider, verifying, and fixing failed records.
---

# Emailit domain setup

Emailit sends only from verified domains. A domain is verified when its return path MX, SPF, and DKIM records resolve to the expected values.

## 1. Choose the domain

Ask which domain the user wants to send from. Recommend a subdomain (`mail.example.com`, `send.example.com`) instead of the root domain:

- Reputation stays separate from the company's own mailboxes.
- The root domain's existing SPF and MX records are not touched.

If the user already has the domain in Emailit, skip to step 3.

## 2. Add it

Confirm the name, then call `create-domain` with `name`. Optional flags:

- `incoming`: receive email on this domain. It needs the inbound MX.
- `dmarc_reports`: collect DMARC reports in Emailit. The suggested DMARC record then includes the reporting address. Needs the Pro or Business plan; on pay as you go it fails with 403 `plan_required`, so leave it off there.

The response contains `dns_records`. Each record has `type`, `name`, `value`, `priority`, `required`, and `status`.

## 3. Publish the records

Show the records as a table the user can copy. Use the exact `name` and `value` from `get-domain`; never rebuild them by hand. For `mail.example.com` they look like this:

| Type | Name | Value | Required |
| --- | --- | --- | --- |
| MX (priority 10) | `emailit.mail.example.com` | return path host from Emailit | Yes |
| TXT | `emailit.mail.example.com` | `v=spf1 include:_spf.emailit.com ~all` | Yes |
| TXT | `emailit._domainkey.mail.example.com` | `v=DKIM1; t=s; h=sha256; p=...` | Yes |
| TXT | `_dmarc.mail.example.com` | `v=DMARC1; p=none; ...` | Recommended |
| CNAME | `go.mail.example.com` | `go.emailitmail.com` | For tracking |
| MX (priority 10) | `inbound.mail.example.com` | `inbound.emailitmail.com` | For inbound |

The SPF and return path records sit on the `emailit.` subdomain, so they never conflict with an SPF record the user already has.

Provider notes are in `references/dns-providers.md`. The most common mistake is pasting the full name into a provider that appends the zone automatically, which creates `emailit.mail.example.com.example.com`.

## 4. Verify

DNS changes usually show up within minutes and can take up to 48 hours. When the user says the records are in, call `verify-domain` with the domain ID.

Then call `get-domain` and report each record's `status`: `ok`, `missing` (not found), `invalid` (found with the wrong value), `error` (the DNS lookup failed), or `pending` (not checked yet).

- All required records `ok` and `verification_status` is `verified`: done. Suggest sending a test with `send-email`.
- A record `missing`, `invalid`, or `error`: read its `error` and use the table in `references/troubleshooting.md`.
- Records are fine but `verification_status` is `pending_review` (`manual_review_required: true`): on pay as you go plans, domains registered less than 30 days ago get a manual review. Tell the user Emailit reviews it and there is nothing else to fix.

Do not call `verify-domain` in a tight loop. If records are missing, wait for the user to fix them.

## 5. Tracking and inbound

- **Tracking**: open and click tracking need the `go` CNAME. Do not pass `track_loads` or `track_clicks` to `create-domain`; Emailit rejects them until the CNAME verifies. After `verify-domain` shows the tracking record `ok`, turn them on with `update-domain`.
- **Inbound**: with `incoming: true` and the inbound MX in place, received emails appear in `list-emails` with `type: inbound`, and `email.received` webhooks fire.

## 6. DMARC

Start with `p=none` to monitor. After a few weeks of clean reports (`get-dmarc-stats`, `list-dmarc-sources`), move to `p=quarantine` and then `p=reject`. Load `emailit-deliverability` for reading reports.

## Removing a domain

`delete-domain` stops all sending from it and cannot be undone. Confirm the domain name with the user and warn that apps sending from it will start failing.

Referenced files: 2 — file references are metadata, not package contents.

emailit-get-started5.78 KB

View saved version →

---
name: emailit-get-started
description: This skill should be used when the user asks to "set up Emailit", "connect Emailit", "get started with Emailit", "send my first email", "is Emailit connected", or has just installed the Emailit plugin. Walks through connecting the hosted MCP server with OAuth, confirming the workspace, checking a sending domain, and sending a first test email.
---

# Get started with Emailit

Use this flow the first time a user works with Emailit in this client, or whenever the Emailit tools are missing or failing to authenticate. Keep each step short and tell the user what you found before moving on.

## 1. Check the connection

Call `get-current-workspace`.

- **It works**: say "You're connected to the <name> workspace (<id>) with <scope> access." and go to step 2.
- **The tool does not exist**: the MCP server is not connected. Give the user the steps for their client from `references/connect.md`, then stop until they confirm.
- **401 or an auth error**: the sign in expired or was revoked. Ask the user to reconnect (most clients have a "Reconnect" or "Authenticate" button next to the server).

With OAuth, the user chose which workspaces this connection may use: all of them, or a selected set. Call `list-workspaces` to show them with the user's role in each. To act in one of them, pass `workspace` to the tool call ("In Acme Client, list my domains"), or call `switch-workspace` to change the default. A workspace that is missing can be added under **Account > Connected apps > Edit access** in the Emailit dashboard without reconnecting.

## 2. Check for a verified sending domain

Call `list-domains`. It needs the `full` scope, like `get-domain`, `list-emails`, and `get-email` below. If it returns an insufficient scope error, or is missing while `get-current-workspace` works, the connection has `sending` scope only (or the tool list is trimmed). Emailit is still connected: ask the user which verified domain to send from, and see "Access levels" to get full access.

- **At least one verified domain**: note its name. Emails must be sent from an address on that domain.
- **A domain that is not verified**: call `get-domain` and list the DNS records still pending. Offer the `emailit-domain-setup` skill.
- **No domains**: ask which domain or subdomain they want to send from. Recommend a subdomain such as `mail.example.com` so marketing and transactional reputation stay separate from the root domain. Then follow `emailit-domain-setup`.

Do not continue to a real send until a domain is verified.

## 3. Send a test email to the user

Ask for the user's own address, or use one they already gave you. Propose the email first:

```
From:    Emailit Test <test@<verified-domain>>
To:      <user's address>
Subject: Emailit is connected
Body:    A short HTML and plain text message.
```

After the user confirms, call `send-email` with `from`, `to`, `subject`, `html`, and `text`. Report the returned `id` (`em_...`) and status.

Wait a few seconds, then call `get-email` with that ID (full scope). A `delivered` status means it worked. If it is `bounced`, `failed`, or `rejected`, read the reason from the email and load `emailit-deliverability`. With sending scope only, ask the user to check their inbox instead.

## 4. Suggest next steps

Offer two or three of these, based on what the user said they want:

- Add the API to their app: `emailit-sdk` (Node, Python, PHP, Laravel, Go, Ruby, Java, .NET, Rust, or SMTP).
- Receive delivery and bounce events: `emailit-webhooks`.
- Build an audience and send a campaign, or set up an automation: `emailit-campaigns`.
- Check DMARC reports and reputation: `emailit-deliverability`.
- Create a template with variables: `create-template`, then `publish-template`.

## Access levels

The AI client requests the scopes when it connects; unless it asks for fewer, it gets both. The consent page lists what the client will be able to do, and the user chooses the workspaces there, not the scopes.

- **Sending**: send email and manage sent email (`send-email`, `update-email`, `cancel-email`, `retry-email`, `forward-email`), plus `get-current-workspace`, `list-workspaces`, and `switch-workspace`. Enough for an agent that only sends; it cannot read emails back or check domains.
- **Full**: everything else, including reading emails, domains, contacts, campaigns, automations, webhooks, and API keys.

`get-current-workspace` shows the granted scope. If a tool returns an insufficient scope error, explain that the connection has sending access only and the user can reconnect so the client requests full access.

## API key option

Some clients cannot run OAuth, such as the xAI API or a CI job. In that case the user creates an API key on the **API Keys** page of the Emailit dashboard and sends it as `Authorization: Bearer <key>` to `https://api.emailit.com/mcp`. Keep the key in an environment variable or the client's secret store. Never ask the user to paste it into the chat.

## Troubleshooting

| Symptom | Likely cause | Fix |
| --- | --- | --- |
| No Emailit tools | Server not added, or plugin not enabled | Follow `references/connect.md` |
| 401 on every call | Token expired or connection revoked | Reconnect the server |
| Fewer tools than expected | Toolsets trimmed, read only mode, or a sending API key | Check the server URL query and headers, or use full access |
| "Domain not verified" on send | DNS not published or not verified yet | `emailit-domain-setup` |
| "Workspace not verified" (`unverified_workspace_recipient`) on send | New workspaces can only send to their members' account emails until Emailit verifies the workspace | Send the test to the address the user signs in with |
| "Pending verification" (`pending_review`) with all records valid | On pay as you go plans, domains registered less than 30 days ago get a manual review | Wait for the review, or upgrade to Pro or Business |

Referenced files: 1 — file references are metadata, not package contents.

emailit-sdk4.72 KB

View saved version →

---
name: emailit-sdk
description: This skill should be used when the user asks to "integrate Emailit", "send email from my app", "add Emailit to Node", "use Emailit with Laravel", "send email in Python with Emailit", "configure Emailit SMTP", "switch from SendGrid or Resend to Emailit", or is writing code that calls api.emailit.com. Covers the official SDKs (Node, Python, PHP, Laravel, Go, Ruby, Java, .NET, Rust), SMTP, configuration, templates, attachments, idempotency, and error handling.
---

# Emailit in application code

Write production ready code that sends through Emailit. Detect the project's language and framework from its files (`package.json`, `requirements.txt` or `pyproject.toml`, `composer.json`, `go.mod`, `Gemfile`, `pom.xml` or `build.gradle`, `*.csproj`, `Cargo.toml`) and use the matching SDK.

## Rules

1. **Read the key from the environment**: `EMAILIT_API_KEY`. Add it to `.env.example` with a placeholder, never to committed config. Keys start with `secret_`.
2. **Use a sending scope key** in apps that only send. Create one in the dashboard on the **API Keys** page (or with the `create-api-key` tool, `scope: "sending"`), optionally limited to one sending domain.
3. **Send from a verified domain**. Put the from address in config (`EMAIL_FROM`), not scattered through code.
4. **Send off the request path** for anything user facing: queue a job, so a slow or failed send does not break signup or checkout.
5. **Make sends idempotent**: pass an `Idempotency-Key` header derived from your own ID (for example `order-1234-shipped`). Emailit returns the first response for repeats within 24 hours.
6. **Handle errors by type**: retry 429 and 5xx with backoff; do not retry 400, 401, 403, or 422.

## Install and send

| Language | Install | Client |
| --- | --- | --- |
| Node.js 18+ | `npm install @emailit/node` | `new Emailit(process.env.EMAILIT_API_KEY)` |
| Python 3.7+ | `pip install emailit` | `EmailitClient(os.environ["EMAILIT_API_KEY"])` |
| PHP 8.1+ | `composer require emailit/emailit-php` | `Emailit::client(getenv('EMAILIT_API_KEY'))` |
| Laravel 10+ | `composer require emailit/emailit-laravel` | `MAIL_MAILER=emailit`, or the `Emailit` facade |
| Go 1.21+ | `go get github.com/emailit/emailit-go/v2` | `emailit.NewClient(os.Getenv("EMAILIT_API_KEY"))` |
| Ruby 3.0+ | `gem install emailit` | `Emailit::EmailitClient.new(ENV["EMAILIT_API_KEY"])` |
| Java 11+ | `com.emailit:emailit-java` | `new EmailitClient(System.getenv("EMAILIT_API_KEY"))` |
| .NET 8+ | `dotnet add package Emailit` | `new EmailitClient(Environment.GetEnvironmentVariable("EMAILIT_API_KEY"))` |
| Rust | `emailit = "2"` and `tokio` | `Emailit::new(&std::env::var("EMAILIT_API_KEY")?)` |

Node example:

```js
import { Emailit } from '@emailit/node';

const emailit = new Emailit(process.env.EMAILIT_API_KEY);

const email = await emailit.emails.send({
  from: process.env.EMAIL_FROM,
  to: ['user@example.com'],
  subject: 'Welcome to Acme',
  html: '<p>Thanks for signing up.</p>',
  text: 'Thanks for signing up.',
});
console.log(email.id); // em_...
```

Full examples for every language, plus SMTP settings for frameworks with a mail transport (Django, Rails Action Mailer, Nodemailer, Spring), are in `references/languages.md`.

## Beyond a basic send

`references/patterns.md` covers:

- Templates with `{{variables}}`, created once and referenced by alias.
- Attachments by base64 content or URL, and inline images with `content_id`.
- Scheduling with `scheduled_at`, and canceling.
- `meta` for correlating webhook events with your records.
- Idempotency keys, typed errors, and retry logic.
- Testing without sending to real people.

## Migrating from another provider

Map the old fields, then swap the client. Most providers use the same concepts:

| Other provider | Emailit |
| --- | --- |
| `from`, `to`, `cc`, `bcc`, `reply_to` or `replyTo` | Same names, `reply_to` |
| Template ID plus dynamic data | `template` (ID or alias) plus `variables` |
| Custom args, tags, or metadata | `meta` (string values) |
| `send_at` or `scheduledAt` | `scheduled_at` |
| Event webhooks | `create-webhook`, signature in `X-Emailit-Signature` |

Before switching traffic: verify the sending domain in Emailit (`emailit-domain-setup`), import suppressions from the old provider (`create-suppression`, type `bounce`, `complaint`, or `unsubscribe`), and set up webhooks (`emailit-webhooks`).

## Without an SDK

Plain HTTP works from any language:

```bash
curl https://api.emailit.com/v2/emails \
  -H "Authorization: Bearer $EMAILIT_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: welcome-user-42" \
  -d '{"from":"Acme <hello@mail.acme.com>","to":"user@example.com","subject":"Welcome","html":"<p>Hi</p>"}'
```

The full endpoint list is in the `emailit` skill's `references/rest-api.md`.

Referenced files: 2 — file references are metadata, not package contents.

emailit-webhooks5.63 KB

View saved version →

---
name: emailit-webhooks
description: This skill should be used when the user asks to "set up an Emailit webhook", "receive bounce notifications", "verify the webhook signature", "handle email.delivered events", "my webhook is failing", "retry failed webhooks", or "sync Emailit events to my database". Covers creating webhooks, choosing events and filters, verifying X-Emailit-Signature, handling batched payloads, retries, and debugging.
---

# Emailit webhooks

Emailit sends HTTPS POST requests to the user's endpoint when events happen: email delivery outcomes, opens and clicks, domain, contact, audience, subscriber, template, suppression, verification, and campaign changes.

## Create a webhook

1. Ask for the endpoint URL (HTTPS, publicly reachable) and which events the app needs. Do not subscribe to everything by default; fewer events mean less load.
2. Call `create-webhook` with `name`, `url`, and either `events: [...]` or `all_events: true`. Event names are in `references/events.md`.
3. Optional `filter` delivers only matching events:

```json
{ "match": "all", "rules": [{ "field": "meta.app", "operator": "equals", "value": "billing" }] }
```

   A `field` is a dotted path inside the event's `object` (shown below), so write `meta.app`, `status`, or `to`, not `data.meta.app`. Loads and clicks nest the email, so use `email.meta.app` there. Operators: `equals`, `not_equals`, `contains`, `not_contains`, `starts_with`, `ends_with`, `greater_than`, `less_than`, `is_set`, `is_not_set`, `in`, `not_in`. Up to 25 rules. Filters need the Pro or Business plan; `get-webhook` shows `filters_allowed`.

4. The response includes `secret` (`whsec_...`); `get-webhook` returns it too. Tell the user to store it as `EMAILIT_WEBHOOK_SECRET` in their app's secret store. Do not repeat it in chat more than needed, and never write it into code or a commit. If it leaks, `reset-webhook-secret` issues a new one and the old one stops working at once.
5. `test-webhook` sends a sample event to the URL. Confirm first, since it calls the user's server.

## The request

```
POST <your url>
Content-Type: application/json
X-Emailit-Signature: <hex HMAC-SHA256>
X-Emailit-Timestamp: <unix seconds>
```

The body is a **JSON array** of events. Emailit batches events, so loop over every item:

```json
[
  {
    "event_id": "evt_...",
    "type": "email.delivered",
    "object": { "id": "em_...", "object": "email", "to": "user@example.com", "status": "delivered", "meta": { "order_id": "1234" } },
    "data": {
      "object": { "id": "em_...", "object": "email", "to": "user@example.com", "status": "delivered", "meta": { "order_id": "1234" } }
    }
  }
]
```

Each event carries the resource twice: at `data.object` and, as a copy, at the top level `object`. Read it from `data.object` (for example `event.data.object.meta.order_id`). Fields per event type are in `references/events.md`.

## Verify every request

Signature = hex of HMAC-SHA256, keyed with the webhook secret, over the string `"{timestamp}.{raw body}"`.

1. Read the **raw** body bytes before any JSON parsing. Re-serialized JSON will not match.
2. Compute the HMAC and compare with `X-Emailit-Signature` in constant time.
3. Reject timestamps more than 300 seconds from now, to block replays.
4. Return 401 on failure.

Code for Node, Next.js, Python, PHP, and Go, and the SDK helpers (`WebhookSignature.verify`) are in `references/verify-signature.md`.

## Handle events safely

- **Respond fast**: return 2xx within a few seconds, then process in a background job. Requests time out after 30 seconds.
- **Deduplicate** on `event_id`. Retries and batching mean the same event can arrive more than once.
- **Order is not guaranteed**. Use the event's own timestamps and the email's current status rather than arrival order. A `delivered` can arrive after `loaded`.
- **Correlate** with your own data through `meta` set on `send-email` (for example `data.object.meta.order_id`).

## Retries and failures

Any non-2xx response or timeout is a failure. Emailit retries each request with backoff: 5 minutes, 30 minutes, 2 hours, 5 hours, then every 12 hours, up to 11 attempts. The owner is alerted after the third failed attempt. A webhook with no successful delivery for 3 days is disabled.

- Check state: `get-webhook` shows `enabled` and `last_used_at`. `list-events` shows the events that were generated. Per request response codes are on the webhook page in the dashboard.
- Resend failed requests: `retry-failed-webhook-requests` queues again every request that failed permanently (all attempts used) in the last 7 days. If it finds any, it also re-enables the webhook, so fix the endpoint first. It returns `retried` with the count; 0 means nothing was resent and the webhook was left as it was.
- Resend one request: `retry-webhook-request` with the request ID from the dashboard. Only permanently failed requests can be retried, and it re-enables the webhook too.
- Turn a webhook back on without resending: `update-webhook` with `enabled: true`.

## Debugging checklist

| Symptom | Check |
| --- | --- |
| Signature never matches | Using the raw body, the right secret (it changes on reset), and `timestamp + "." + body` |
| 401 or 403 from the endpoint | Auth middleware, CSRF protection, or a firewall blocking the POST |
| Timeouts | Do the work after responding |
| Only one event handled | Body is an array; loop over it |
| No requests at all | `enabled` is false, the event is not subscribed, or the filter excludes it |

## Without MCP

REST endpoints: `POST /v2/webhooks`, `POST /v2/webhooks/:id` (update), `POST /v2/webhooks/:id/test`, `POST /v2/webhooks/:id/reset-secret`, `POST /v2/webhooks/:id/retry-failed`, `POST /v2/webhooks/:id/requests/:requestId/retry`.

Referenced files: 2 — file references are metadata, not package contents.

Release notes

First release: hosted Emailit MCP server with OAuth, 113 tools, and skills for setup, domains, deliverability, campaigns, webhooks, and SDKs.

Declared in the saved package. Remote tools may change independently.

Package details

Publisher declarations from the archived package. These are separate from our research and the live service's terms.

Package license
MIT
Package author
Emailit
Keywords
See keywords
Getting started skill
./skills/emailit-get-started/SKILL.md
Commerce declaration
Does not support commerceThis does not establish whether access is free or paid.
Review scenarios
5 positive · 3 negativeScenario counts, not test results.

Declared capabilities

  • Send and schedule email
  • Track delivery and bounces
  • Set up and verify domains
  • Manage contacts and audiences
  • Draft and send campaigns
  • Configure webhooks

Package observed Nov 3, 2026.

Technical details
First seen
Oct 24, 2026 · 12:40 UTC
Last seen
Nov 4, 2026 · 06:00 UTC
Collection status
Collected

plugin_asdk_app_6ac773e202f0819199f20c6665fcfcde

Download plugin data (JSON)