Changelog

What shipped, newest first.

  1. Sign in with an authenticator app

    Sign in with your email and a 6-digit code from an authenticator app, instead of waiting for an emailed link.

    The emailed link still signs everyone in and is still the only way to create an account. You can now also sign in with an authenticator app: any app on your phone or in your password manager that shows 6-digit codes.

    Setting it up. Under Settings, then Security, choose Add an authenticator app. Scan the QR code, or type the key if you cannot scan it, then enter one code from the app to confirm. The key is shown only then and is stored encrypted. Adding it needs a sign-in from the last 10 minutes, like adding a passkey, and you get an email whenever an authenticator app is added to or removed from your login.

    Signing in. On the login page, choose Use an authenticator app and enter your email and the code the app shows. The page remembers which way you signed in last on that browser and opens on it next time. Each code works once, and after a few wrong codes for an address, codes for it are refused for a while; the emailed link always still works.

    One per login. Adding another app replaces the one you have, once you confirm a code from the new one. You can remove it from the same place. See Security for how it is kept safe.

  2. Import from Resend

    Account owners can now bring their domains, templates, webhook endpoints and suppression list across from Resend in the dashboard, with a preview first and a checklist of what to change in their code.

    Import from Resend. Under Settings, an account owner can paste a Resend API key with Full access and see what Avelto would bring across: domains, templates, webhook endpoints, and suppressed and unsubscribed addresses. Nothing is written until you tick what you want and press Import, and running it again skips anything already on your account. The key is used only while your Resend account is read; it is never stored or logged. See Import from Resend.

    What happens to each item. Domains are added the usual way, ownership record first, and the preview shows your current records beside the ones Avelto needs, so you can switch DNS without a gap. Templates are converted to Avelto's {{name}} syntax, and anything that cannot be converted exactly, such as a conditional or a helper, is flagged rather than guessed. Webhook endpoints are recreated with the matching events and a new signing secret, switched off until your handler verifies Avelto-Signature. Suppressed addresses are optional and unticked by default. Each change is recorded in Activity like any other.

    What to change in your code. The import page and the docs list the changes for the switch: the API address and key, the SDK for Node, Python, Go or PHP, the send request fields that differ, and webhook signature verification. The MCP server's integrate prompt can make those changes for you in a project that already sends email.

  3. Let an AI agent set up your account

    A new key for an AI agent lets your agent add domains, register webhooks, write templates and hand your app its own key through the MCP server, and the new Activity log shows everything it changed.

    A key for an AI agent. On the API keys page, an account owner can now create a Key for an AI agent. Add the MCP server with it and your agent can set the account up for you: integrate sending into your project, add and remove domains and tell you which DNS records to publish, create, change and delete webhook endpoints, manage templates and the suppression list, change the account's name and alert settings, and create narrower keys, such as a send-only key for your production server. The dashboard shows the key once, with the setup for Claude Code, Claude Desktop, Cursor and other clients filled in. See Let an AI agent set up your account.

    What it can never do. Plans and payment, deleting the account, the team, sign-in methods and sessions, and our sending limits stay with a person in the dashboard, and an agent key cannot export the account or erase a recipient. A key it creates never has more access than its own, and it can revoke only the keys it created and itself, so it cannot switch off the keys your application already uses.

    Two new scopes, owner only. account:settings and keys:manage are what the agent key adds. Only an account owner can grant them, and only by asking for them: Full access, and any key created without a list of scopes, still means the seven scopes it always did, so no existing key gains anything. Members see why the option is not theirs.

    New API routes. GET, POST and DELETE /v1/api-keys for listing, creating and revoking keys, GET and PATCH /v1/account/settings for the account name and alerts (a new alert address still confirms by link first), and PATCH /v1/webhooks/:id to change which events an endpoint receives or switch it off. The API reference has the fields.

    Activity. Settings has a new Activity section, with the full log one click away: API keys created and revoked, domains, webhooks, templates, suppressions, account settings, the team and sign-in changes, each with who made it. A change made by an API key names the key, so you can see exactly what your agent did. Suppressed addresses never appear in full, and message content and secrets are never recorded. On the API keys page, a key made by another key says which one.

  4. Your data, your owners, and when our team looks

    Erasing a recipient now reaches webhook logs, bounce details and headers; exports stream and are limited; account mail follows the current owner; and you can see when our team opened your dashboard.

    Erasing a recipient goes further. POST /v1/recipients/erase (and the button in Settings) now removes the address everywhere it is held for your account: the recipients, reply-to, subject and headers of your emails, the delivery events (including the bounce text a mail server sends back, which usually quotes the address), what was sent to your webhook endpoints about those emails and what they answered, and the samples on any bounce or complaint record. Emails sent only to that person are still deleted, and the stored content of any email sent to them. The response is unchanged.

    Webhook delivery logs follow your plan's retention. What we sent to your endpoint and what it answered are now cleared after the same window as message bodies. The delivery itself, and its status, stay.

    Exports stream, and are limited. GET /v1/account/export returns the same JSON document as before, but it is written as it is read, so a large account starts downloading at once. An account can be exported three times an hour and one export at a time: a fourth answers 429 rate_limited with Retry-After, and a second while one is running answers 409 conflict. The file no longer includes our own notes about your account or our sending provider's identifiers for your domains.

    Account mail follows the current owner. Security alerts, suspension notices and the deletion confirmation go to the account's owner address. When the owner who signed up steps down or is removed, that address now moves to the longest-standing remaining owner. Removing a teammate also clears your alert address if it was theirs.

    Deleting the account now. "Delete account now" in Settings is now the same deletion as the scheduled one: it cancels any subscription first and is recorded in your account's audit log. A suspended account cannot be deleted from the dashboard; write to us and we will handle it with you.

    You can see when our team looks. If someone at Avelto opens a read-only view of your dashboard to help with a question, you get a notice in your dashboard and a row in your account's audit log, with the time and the reason they gave. That view never shows message content.

  5. Alert addresses confirm first

    A new address for your alerts now gets a link to confirm it before any alert goes there. Until then, alerts go to the account owner.

    When you set where your alerts go, that address now gets a short email with a link to confirm it. Nothing else is sent to it until the link is followed, and until then your alerts keep going to the account owner. An address that already signs in to your account counts as confirmed straight away.

    If you set an alert address before today and it is not the sign-in address of someone on your account, your alerts are going to the owner again. Open the Alerts tab in your settings and press "Send the link again" to confirm it.

    Creating and rotating API keys in the dashboard is now limited to 20 an hour per account, and an account gets at most 10 alert emails an hour.

  6. Domains are verified in two steps

    A new domain first proves it is yours with one TXT record; its DKIM records follow once that is found. New error code domain_unavailable, and unproven domains are removed after 7 days.

    Adding a domain now starts with proving it is yours. POST /v1/domains returns one ownership record to publish, a TXT at _avelto.<your domain> with a value unique to that domain, alongside the SPF and DMARC records. Once we find it, the domain is set up for sending and its three DKIM records appear in dns_records, the same as before. The new ownership_verified field on a domain says which step it is on, and the dashboard walks you through both. You can remove the ownership record once it has been found.

    Domains you have already verified are not affected and need nothing from you. A domain that was still pending also needs the ownership record before it can verify.

    Also new:

    • A domain whose ownership record is not found within 7 days of being added is removed. You can add it again at any time.
    • 409 domain_unavailable means a name cannot be added: it is a subdomain or a parent of a domain on another account, it is one of ours, or it is already set up elsewhere. Contact support if it is yours. You can still add subdomains of domains on your own account.
    • When a scheduled email goes out, we check again that its domain is still verified on your account. An email from a domain you have since removed fails with email.failed instead of sending.
  7. Fair sharing, account-wide limits and a stricter SMTP relay

    Throttling no longer fails an email, sending and webhooks are shared fairly between accounts, and there are new limits on templates, webhook endpoints, retries and SMTP connections.

    Sending. Live mail goes to our sending provider at the provider's rate, in a fair order between accounts, with at most 3 of one account's emails handed over at a time. If the provider asks us to slow down, the email waits and tries again without using up its retries; it fails only if that goes on for six hours.

    Webhooks. Retrying a delivery (POST /v1/webhooks/:id/deliveries/:deliveryId/retry) now only takes a delivery that has failed: a delivered one, or one still being retried, gets 409 conflict and nothing is sent. Retries are limited to 100 an hour per endpoint, and Send again in the dashboard to 30. An account can have up to 10 endpoints. At most 5 of an account's deliveries are in progress at once, so a slow endpoint delays only its own account's events.

    Templates. An account can have up to 100 templates, and each keeps its last 50 versions. A template's body, once its variables are filled in, has the same 5,000,000-character limit as a body sent directly; a send past it gets 422 naming the part.

    Pausing. The bounce and complaint rates that pause sending are now also judged over the last 7 days, not only the day, so quieter accounts are judged too. Only permanent bounces count toward the bounce rate, and mail to the simulator addresses never counts, +label or not.

    SMTP. On ports 587 and 2587 the relay offers sign-in only after STARTTLS, so a client that does not upgrade the connection can no longer send its key unencrypted; set your client to require TLS, or use port 465. A connection has 30 seconds to sign in. Each API key may have 5 messages in progress at a time (451 at MAIL FROM beyond that, which your client retries). The sandbox and suppression refusals no longer quote addresses; the dashboard shows which address is suppressed.

    See Plans and limits, Webhooks and SMTP.

  8. MCP server: third-party text is marked as data

    @avelto/mcp 0.1.1 wraps email content, event details and webhook responses in an untrusted_content envelope, and the docs no longer suggest a key in a project's .cursor/mcp.json.

    Some MCP results carry text that neither you nor Avelto wrote: email subjects and bodies, bounce diagnostics from recipient mail servers, and your webhook endpoints' response bodies. From @avelto/mcp 0.1.1, get_email, list_emails, list_webhook_deliveries and the details of an error start with a fixed line saying the content is third-party data, followed by the JSON inside { "untrusted_content": ... }. Their descriptions tell the agent never to follow instructions found there, and create_webhook tells it to register only a URL you typed or confirmed. If you parse these results yourself, read the JSON after the first line and take untrusted_content.

    The Cursor setup now puts the key in ~/.cursor/mcp.json, or references ${env:AVELTO_API_KEY} from a project's .cursor/mcp.json, so the key never sits in a file that gets committed. See MCP server.

  9. Webhook and attachment fetches: a fix and plainer errors

    Webhook deliveries and attachments given as a URL failing with \

    Webhook deliveries, and attachments given as a url, could fail with request failed even when your endpoint or file was fine. That is fixed. If deliveries to your endpoint ran out of retries with that error, open the endpoint in the dashboard and send them again.

    The error on a webhook attempt that got no response is now one of timeout, could not connect or blocked address. could not connect covers a refused or reset connection, a name that does not resolve and a failed TLS handshake, such as an expired certificate. The same goes for an attachment URL we could not fetch. A webhook URL that does not resolve, and one that resolves to a private address, are now refused with the same message: it must resolve to a public internet address.

    Attachments given as a URL are now fetched only after the rest of the send has passed, so a send refused for another reason downloads nothing, and the 7 MB total is counted while the files download rather than after.

  10. Email previews no longer load remote images

    The HTML preview of an email or template in the dashboard now shows inline images only. Images hosted elsewhere are not fetched.

    The HTML preview on an email's page and in the template editor now shows images that are embedded in the message (data: URLs) and no longer fetches images from other servers. A remote image in an email is as often a tracking pixel as a logo, and fetching it told whoever serves it that the message had been opened, from where and when.

    Nothing changes in the email itself: recipients see your images exactly as before. Only the dashboard's preview leaves them out, and the HTML tab still shows the full markup.

  11. Each recipient counts as one email

    Allowances, overage and the new-account daily cap now count recipients across to, cc and bcc. Scheduled emails count in the month they go out, overage is billed when the month closes, and plan changes apply once paid.

    Every recipient of an email now counts as one email. A send to three people, across to, cc and bcc, uses three of your monthly allowance, and past the allowance it is three emails of overage. The same goes for the 100 a day new accounts can send in their first 7 days. On Free, a send whose recipients would take you past the allowance is refused whole with plan_limit. Extra emails pay for every recipient of a send from one batch.

    Also new:

    • A scheduled email counts in the month it is sent, not the month you scheduled it. Cancelling it gives its count back, and any extra emails it used.
    • Overage is priced at the step you were on when each email was sent, and billed when the month closes, on an invoice of its own charged to your card. Moving to another step, or to Free, never re-prices or cancels overage already sent.
    • Overage on a paid plan stops at ten times your included volume in a month, so a loop in your code or a leaked key cannot run up a bill nobody meant. Sending then pauses with plan_limit until the 1st, and your dashboard tells you. Get in touch to raise it.
    • A plan change on the billing page applies once the difference is paid. If your bank asks you to approve the payment, the page shows its check; a declined payment leaves your plan as it was.
    • Starting, previewing or changing a plan, the card and the billing details are for owners only, and these billing requests are rate limited.
    • VAT is decided from your billing country, the billing address on your payment details and your card's country.
  12. Safer sign-in methods and invitations

    Invitations are accepted from their own page, adding a password or passkey needs a recent sign-in, every change to your sign-in methods is emailed to you, and Settings lists your sessions.

    Invitations are accepted from the invitation email. The link in it now opens a page that shows who invited you, to which account and in which role. Accept sends a sign-in link to the invited address, and following that link is what joins the account. Signing in from the login page no longer joins an account you were invited to, so if you have a pending invitation, use the invitation email.

    Adding a way in needs a recent sign-in. To add a password or a passkey, you need to have signed in within the last 10 minutes; if not, Settings asks you to confirm with a passkey, your password or an emailed link first. Every password or passkey added or removed is emailed to you, and setting a new password signs out your other sessions.

    Your sessions, listed. Settings, Security now shows every device you are signed in on, and you can end any one of them. Sign out everywhere can also remove your password and passkeys, for when someone else may have had your login.

    The session cookie now has a __Host- name, which no other site or subdomain can set for the dashboard. Existing sessions move over on their own.

  13. Stricter custom headers and addresses

    Custom headers are now an allow-list of X- headers plus threading, list and auto-reply headers, at most 20 per email. Addresses and template subjects are checked more strictly.

    Custom headers. The headers field now takes X- headers plus In-Reply-To, References, Keywords, Comments, Auto-Submitted, Precedence, Importance, Feedback-ID, List-Id, List-Unsubscribe and List-Unsubscribe-Post. Any other name is refused with validation_error, as are X- headers that claim where a message came from or how it was filtered, such as X-Originating-IP, X-Original-From or X-Spam-Status. An email may carry at most 20 custom headers, and each value must be printable ASCII of at most 900 characters. Over SMTP, headers outside the list are removed rather than refused, so ordinary mail clients keep working.

    Addresses. Each address must be exactly one mailbox. A display name with an unquoted comma, semicolon, colon, @ or angle bracket is refused: write "Doe, Jane" <jane@example.com>. Control characters and bidirectional formatting characters are refused anywhere in an address or a filename. We store and send each address in one canonical form, so the email log shows Jane Doe <jane@example.com> for Jane Doe <jane@Example.COM>.

    Template subjects. Once the variables are filled in, a template's subject follows the same rules as a literal subject: no line breaks or control characters, and at most 998 characters. A variable that would break that is named in the 422 response.

    Unsubscribe URLs may be up to 900 printable ASCII characters, which is what fits in the List-Unsubscribe header.

    See Send email for the full rules.

  14. Test keys stay out of live configuration

    Webhook endpoints, templates and the suppression list now need a live key on the API, and idempotency keys are kept per mode.

    A test key is meant to be the credential you can put in CI without a second thought, so it no longer reaches anything your live mail depends on. On the API, these now need a live key and answer a test key with 403 live_key_required:

    • creating or deleting a webhook endpoint, because every endpoint receives your live events;
    • creating, changing, deleting or restoring a template, because a live send renders the template as it is at that moment;
    • reading the suppression list, which is made of your real recipients' bounces and complaints (adding and removing already needed a live key).

    A test key can still add and read domains, read templates and webhook endpoints, send with a template, and read and retry the deliveries of test events. Nothing changes in the dashboard, where everyone on your team can still manage all of these.

    Two related changes. Test sends are no longer checked against the suppression list, and POST /v1/emails/validate only reports checks.suppressed for a live key, so a test key cannot ask about addresses on the list one at a time. And idempotency keys are now kept per mode: a test send that reuses a live send's Idempotency-Key creates a test email instead of replaying the live one. Batch messages are stored under batch:<your key>:<index>, so a single send's key may no longer start with batch:.

    See Test mode for the full list.

  15. Send over SMTP

    Anything that speaks SMTP can now send through Avelto. Point your mailer at the relay, sign in with an API key, and the mail lands in the same log as the API.

    Avelto now has an SMTP relay. Point a mail library, a framework's mailer, a CMS plugin or an auth provider at the SMTP host with the username avelto and an API key as the password, on port 587 with STARTTLS or 465 with TLS. Ports 2587 and 2465 are there for networks that block the standard ones.

    Mail sent over SMTP follows the same rules as the API: your verified domains, suppressions, plan limits and rate limits, test keys that never deliver, and webhooks for every event. The relay answers with the email id, and the dashboard marks those emails as sent over SMTP. Headers such as X-Avelto-Tags, X-Avelto-Scheduled-At and Idempotency-Key give you tags, scheduling and safe retries without touching the API.

    The SMTP quickstart has the settings, the reply codes and copy-paste setups for Nodemailer, Laravel, Django, Rails, WordPress and Supabase Auth. Your API keys page in the dashboard shows the settings too, with copy buttons.

  16. Your Enterprise contract on the billing page

    Enterprise accounts now see their contract on the billing page: the fee, what it includes, the rate above that, and where this cycle's bill is heading.

    If your account is on an Enterprise contract, the billing page now shows it in place of the plan list: the monthly fee, the emails it includes, what happens above that, the day your cycle starts each month, and when the next invoice is raised. Beside it are this cycle's sends and the bill so far, with a projection to the end of the cycle.

    Your usage page and overview count by the same cycle, so the figures line up with the invoice. Each invoice is raised on your cycle day and emailed to the account owner with the PDF attached. To change anything in your contract, write to us from the support page.

  17. Extra emails on the usage page

    When we add extra emails to your account, the usage page shows how many are left in each batch and when it expires.

    We can now add extra emails to an account on top of its plan, as a one-off batch you buy or as a credit from us. Each batch lasts 30 days from the moment it is added, or from when its invoice is paid, and is used only once your plan's monthly emails have run out: on Free you keep sending instead of hitting the monthly limit, and on a paid plan they are spent before any overage.

    The usage page shows the emails left in each batch and the exact time it expires, and the account owner is emailed when a batch is added. Sends that use extra emails are never counted as overage on your invoice.

  18. Download your invoices as PDF

    Every invoice on your billing page now has a PDF to download, in one consistent layout, whichever way it was raised.

    Every invoice on your billing page now has a download button. The PDF shows the invoice number, the dates, the billing period, each line with its quantity and unit price, the subtotal, VAT where it applies, and the total, along with the billing details you gave us. An unpaid invoice that can be paid online also carries a link and a QR code to pay it.

    Any member of your account can download invoices, and an invoice we send you by email has the same PDF attached. Your billing details on the invoice are the ones you had when it was raised, so changing them later does not rewrite past invoices.

  19. Every email now says how to report it

    Emails sent through Avelto carry an X-Avelto-Email-ID header and an X-Report-Abuse link to our report page with the email's id filled in.

    Every email you send through Avelto now carries two headers that we add:

    • X-Avelto-Email-ID: the email's id, the same one the API returned when you sent it.
    • X-Report-Abuse: a link to our report page with that id filled in.

    They identify the message and nothing else. No account id, key or other detail of yours is in them. You cannot set or override either one, and a send whose headers names X-Report-Abuse or anything starting X-Avelto- is refused with validation_error.

    When a recipient follows the link, the report reaches us already tied to the exact email, so we can look at the right message straight away instead of asking the reporter for headers. If one of your emails is ever reported, the id is the one you can look up with GET /v1/emails/:id.

    The report page also has a new choice for phishing or impersonation, which we treat as urgent.

  20. Every webhook attempt, logged

    A webhook delivery now keeps a log of every attempt: when it ran, how long it took, the status code or error, and the start of your endpoint's response.

    A delivery that took several goes used to show only how it ended. Now every attempt is logged on its own: when it started, how long it took, the status code your endpoint returned or why there was no response (a timeout, a DNS failure, a refused address), and the first 1 KB of the response body.

    In the dashboard, expand a delivery on the webhook page to see its attempts as a list, oldest first, and open any of them to read what your endpoint said that time. From the API, GET /v1/webhooks/:id/deliveries/:deliveryId/attempts returns the same list; it needs the webhooks:manage scope. Retries add to the same log, and attempts are kept for your plan's log retention period.

    Deliveries made before today keep only their last response.

  21. The Python SDK

    pip install avelto: the official Python SDK, sync and asyncio, with the same retries, idempotency and webhook verification as the Node SDK.

    pip install avelto and from avelto import Avelto. The client has every call the API has, named the way the Node SDK names them: emails.send, emails.send_batch, emails.validate, emails.get, emails.list, emails.cancel, then domains, webhooks, templates, suppressions and account. AsyncAvelto is the same client for asyncio code, each method awaited.

    It behaves like the Node SDK, on purpose: every send carries an Idempotency-Key, a fresh UUID per call unless you pass one; a failed request is retried up to three times with backoff and jitter, with a 429 waiting for the server's Retry-After; and a request that may have reached the server is retried only when repeating it is harmless. verify_webhook_signature checks the Avelto-Signature header against the raw body, with the same five-minute tolerance.

    Responses are plain dicts, exactly as the reference documents them, with a TypedDict for each so an editor knows the keys. Python 3.9 or newer, one dependency. The Python SDK guide covers the whole client, and the Python and Django quickstarts now use it.

  22. Validate an address before you send

    POST /v1/emails/validate says whether an address is worth a send: syntax, a mail server, throwaway domains, role addresses, likely typos and your suppression list.

    POST /v1/emails/validate takes one address and answers deliverable, risky or undeliverable, with the most serious reason and every individual check behind it. It looks at the syntax, whether the domain has a mail server, whether the domain is a known throwaway service, whether the local part is a role such as info@, whether the domain is within a typo of a well-known one, and whether the address is on your own suppression list. When it spots a likely typo it returns the corrected address, so a signup form can offer "did you mean jane@gmail.com?".

    Nothing is sent and nothing is stored, so call it on every submit. It works with a test key, needs the emails:send scope, and does not count towards your plan. What it will not claim is that a mailbox exists: the large providers accept every address and bounce later, so nobody can know that without sending.

    The Node SDK has emails.validate(email), the MCP server has validate_email, and the reference and the send email guide cover the response.

  23. Billing without leaving the page

    Start a plan, change it, cancel it or change your card on the billing page itself, with the card fields inside it.

    Everything to do with paying now happens on your billing page. Pick a plan and the card fields appear under it; pay, and the plan starts that moment. Change plan and you see what is due now and what the next renewal costs before you confirm. Move down to Free and your plan runs until the period you have paid for closes, with a button to keep it if you change your mind.

    The card fields belong to the payment processor and sit inside the page, so your card number never passes through us. The card drawn beside them shows the brand as soon as it is recognised and turns over for the security code, and once saved it shows the card on file: brand, last four digits and expiry.

    A new card given because the last payment failed is charged straight away, so a past-due plan comes right without waiting for the next retry. Invoices are unchanged and still listed on the same page.

  24. Emails from Avelto look like Avelto

    Sign-in links, alerts, invitations and support replies now share one design, with a button for the one thing to do.

    Every email Avelto itself sends now comes from the same template: the sign-in link, the alerts about domain health, bounce and complaint rates, usage and new API keys, team invitations, the notice when you schedule a deletion, and replies from support.

    Each one says what happened at the top, keeps the details in a short list rather than a paragraph, and has a single button for the one thing to do about it. The link is also written out underneath, for clients that strip buttons, and the plain-text version says everything the HTML does. Under the card there is one line on why you got it and where to change that.

    Nothing about when they are sent has changed, and the alert settings you have already chosen still apply.

  25. Free tier and dedicated IP pricing

    Free now covers 5,000 emails a month with no daily cap. Dedicated IPs are priced per plan.

    Free now includes 5,000 emails a month. There is still no daily cap on it beyond the one every new account carries for its first week, so a burst on launch day goes out rather than queueing behind a per-day limit. Sending pauses at the monthly limit until the 1st, as before.

    Dedicated IP addresses are now priced per plan: £35 a month on Pro, £25 a month on Growth and Enterprise. They are not offered on Free, because a free account never reaches the volume an address needs to build a reputation. The add-on is arranged by talking to us, and the address is warmed by the provider over a few weeks before it carries full volume.

  26. Passkeys and passwords

    Two optional ways to sign in beside the emailed link, added from your settings.

    The emailed link still signs everyone in and is still the only way to create an account. From today you can add a second way in from Settings, for when you would rather not wait for an email.

    A passkey signs you in with your face, fingerprint or device PIN. There is nothing to remember and nothing that can leak, and it works when email does not. Add one from Settings, then use "Sign in with a passkey" on the login page.

    A password is there too, for people who prefer one. At least twelve characters, changed or removed only with the current one, and if you forget it the emailed link gets you back in to set another.

  27. Support conversations in the dashboard

    Every message you send us is kept on the Support page, with our replies, and you can write back from there.

    Writing to us from the dashboard now opens a conversation rather than sending an email into the dark. Each one is kept on the Support page with everything we said back, and you can reply from there or from your inbox. We still email you every reply, so nothing changes if you prefer email.

    Behind it, each conversation carries the reply time your plan promises, and our side of the screen shows us when we are late. That is the point: a promise we can measure is one we keep.

  28. Signups are invite-only for now

    New addresses join a waitlist while we onboard gradually. Existing accounts sign in as usual.

    We are opening the door slowly rather than all at once, because the first week with a new sending provider is the week that decides whether you keep it, and we would rather be there for it.

    If you already have an account, nothing changes: the sign-in page works exactly as it did, and so does every invitation you send to a colleague. If you are new, entering your address puts you on a list and we will email you when your spot is ready. That is the whole process: no form to fill in, no call to book.

    The queue is worked in the order people joined it. Asking again does not move you up, so there is nothing to gain by re-submitting, and we would rather say that plainly than have anyone wondering.

    One detail worth stating, because we thought about it and someone else might wonder: the confirmation page says the same thing whether or not the address already has an account. That is deliberate. Telling you which would turn the sign-in form into a way of finding out who uses us, and the answer belongs in the inbox of whoever owns the address rather than on a page anyone can load.

    We have also added a notice bar to the dashboard for the rare days when something is wrong on our side. If you ever see one, it is us telling you before you have to ask.

  29. The sending region is Stockholm

    Email now goes out through AWS eu-north-1 in Sweden. Data is stored in the EU, as it always has been.

    Our sending infrastructure runs in AWS eu-north-1, Europe (Stockholm), rather than the Irish region we named previously. Account data, email records and delivery events are stored in the EU exactly as before, so the commitment you signed up to is unchanged. Only the country inside the EU that it points at has moved.

    Nothing changes for you in the product. You do not need to re-verify a domain, republish a DNS record, or touch your API keys. Sending, webhooks and the dashboard behave identically, and there is no downtime associated with this.

    We have updated the places that state the region so they are accurate rather than approximately right: the privacy policy, the DPA's international-transfers clause, the sub-processors table, the security page and the data residency line in your settings all now say Sweden. If you have our DPA on file and your own records name the region, this is the note to attach to them.

    If you are self-hosting against our configuration, the AWS_REGION default is now eu-north-1.

  30. Templates, batch send, attachments by URL, and key scopes

    Four things the API was missing. Nothing you already send changes.

    Four additions, none of which change anything you are already doing.

    Templates

    Store a subject and body once, send it by name:

    JSON
    {  "from": "hello@mail.acme.com",  "to": "ada@example.com",  "template_slug": "welcome",  "variables": { "name": "Ada" }}

    The whole template language is {{name}}, escaped in the HTML part, and {{{name}}} when you are passing markup you built yourself. No loops, no conditionals, no expressions: that is a ceiling rather than a roadmap. If you need a list of order lines, build the markup in your own language, where you can test it, and pass it as one variable.

    A variable the template uses and you did not supply is a 422 naming every one that is missing, not an empty string. "Hi ," sent to a hundred thousand people is worse in every way than a send that did not happen.

    Every save keeps a version, and restoring writes the old one forward rather than rewinding, so a restore is itself something you can undo. The dashboard has an editor with a live preview, the variables it found in your body, and a button that sends it to you in test mode.

    Batch send

    POST /v1/emails/batch takes up to 100 messages. Each one is validated, limited and queued exactly as a single send is, so one suppressed address in a nightly run costs you that message and nothing else. The response reports per message, in request order.

    Attachments from a URL

    An attachment can now be a link instead of base64:

    JSON
    { "filename": "invoice.pdf", "url": "https://cdn.acme.com/invoices/123.pdf" }

    We fetch it when the send is accepted, so a broken link fails on the call that made it rather than two seconds later. Base64 still works exactly as before.

    Key scopes

    A key can now be limited to what it needs. The one worth having is send only: the key you put in an application server, which can send and cannot read back what it sent or change anything about the account. The create-key dialog offers it as a preset.

    Existing keys are unaffected: every key made before today has every scope, and a new key created without naming any still does. Narrowing is something you choose.

  31. Webhook debugging moved to the dashboard

    Three webhook routes left the API before launch, and Replay is now Send again.

    Reading back a delivery's stored request and response bodies, replaying a delivery, and sending a synthetic test event have moved out of the API and into the dashboard, where they were always meant to be used.

    The reasoning is about what we are promising. Anything on the API is versioned and supported for as long as it is published, so it should earn its place by being something a program does. These three are things a person does once, while getting a handler working. They now live on the webhook page and nowhere else.

    Retrying a failed delivery stays on the API, and in the SDK. That one is genuinely a script: when your endpoint comes back up you want to walk your failures and put them back on the queue without opening a browser.

    Nothing you can currently call has been taken away. These routes were never in the SDK and were published only briefly, before anyone could depend on them, which is exactly when a cleanup like this should happen.

    The dashboard button that was called Replay is now Send again, which describes what it does: it creates a new delivery, so your handler receives the event as if it had just happened. Replay suggested watching a recording of something that already went past, and the two operations are easy enough to confuse without the name working against you.

    Both are worth keeping, because they answer different questions. Retry re-attempts a delivery that failed and keeps its attempt count, so the history reads as one event that eventually got through; reach for it when the failure was transient. Send again produces a clean new delivery and leaves the original untouched; reach for it when you have fixed your handler and want to watch the new code receive a past event. Retry is recovery, send again is testing.

  32. Deploys you will not notice

    Updates now ship without dropped requests, failed sends or duplicate email.

    Shipping an update used to mean a short window where a request could fail. It no longer does. The API and the web app each run two copies, replaced one at a time, and a new copy only takes traffic once it reports healthy.

    A copy being retired stops accepting new connections and finishes the requests already in flight before it exits. The background sender does the same with the jobs it is running, and anything left waiting is picked up after the restart.

    Sending is also protected end to end. Each email is claimed before it is handed over and carries a token that identifies it, so an interrupted send is completed once, never twice.

  33. Docs that machines can read

    Read the API reference and every docs page as OpenAPI, markdown or a single text file.

    Anything you can read on the docs site, a script or an AI assistant can read too. The full API description is published as OpenAPI 3.1 at /openapi.json and /openapi.yaml, generated from the same definitions the API validates against, so it cannot drift from what actually runs.

    Every docs page, the home page and the pricing page also answer in plain markdown. Add .md to the path or ask for text/markdown and you get the page without any of the layout around it. /docs/llms.txt is the whole of the documentation concatenated in order, and /llms.txt explains what is available and links to it.

    There is also a published skill file telling an assistant how to use the API correctly.

  34. MCP server

    Let an AI assistant send email and check delivery through your Avelto account.

    @avelto/mcp connects an MCP client to your account. The assistant gets seven tools: send an email, fetch one by id, list emails, list and add sending domains, check a domain's verification status, and read your suppression list.

    It runs over standard input and output, so there is nothing to host and nothing to deploy. One environment variable, your API key, is all the configuration it needs. Setup instructions for Claude Desktop, Claude Code and Cursor are in the docs. Errors come back with their code intact, so an assistant can tell a rate limit from a plan limit and act accordingly.

    Start with a test key. Test sends run the full pipeline but never reach an inbox.

    shell
    claude mcp add avelto -e AVELTO_API_KEY=av_test_... -- npx -y @avelto/mcp
  35. Pricing ladder

    Pick the volume step that matches what you send, with published prices and overage.

    Pricing is now a ladder rather than a handful of tiers. Free covers 10,000 emails a month at no cost. Paid steps run from Pro 50k at £12 a month up to Growth 2.5M at £450, and the price per additional 1,000 emails falls as you climb, from £0.60 down to £0.30. Prices are in pounds and exclude VAT.

    Above three million emails a month, Enterprise adds custom volume and overage, ninety-day retention, a 99.9% uptime commitment, a named contact and EU data residency. There is a contact form for that.

    The pricing page carries a volume selector and a cost-at-scale table so you can see the effective rate per 1,000 at every step. Test-mode sends are never metered.

  36. Dashboard

    See and manage everything you send from the browser, without writing a request.

    The dashboard is open. Sign in with a link sent to your email, no password to remember. The overview shows sends today, over seven days and over thirty days, along with your delivery, bounce and complaint rates.

    The email log is filterable by status, mode and tag, and opening a message shows its headers, a timeline of every event, and a preview of the HTML rendered in isolation so it cannot affect the page. Add a domain and the DNS records appear with copy buttons while the status polls itself. You can also create and revoke API keys, add webhook endpoints and review their deliveries, and remove addresses from your suppression list. A banner makes it obvious when you are looking at test data.

  37. Webhooks

    Get signed delivery, bounce and complaint events posted to your own endpoint.

    Point Avelto at an endpoint and every email event arrives as a JSON POST. Choose the event types you want or take them all. The signing secret is shown once when you create the endpoint.

    Each request carries an Avelto-Signature header holding a timestamp and an HMAC-SHA256 of the raw body. Verify before you parse, and reject anything more than five minutes old. Failed deliveries are retried up to eight times with backoff, and every retry keeps the same event id so you can de-duplicate safely. You can list an endpoint's deliveries, see why one failed, and retry it by hand. Test-mode sends produce real webhooks too, marked as test.

    text
    Avelto-Signature: t=1758103929,v1=5f1c2a9b7e3d…
  38. Test mode and the sandbox

    Build and test the whole sending flow before you verify a domain or touch a real inbox.

    Keys now come in two modes. A key beginning av_test_ runs everything a live key does, including validation, sender rules, suppression checks, the event log and your webhooks. The only step that does not happen is the send itself. About two seconds later the email is marked sent and then delivered, and it appears in your logs alongside live traffic. Test sends are free and never count towards your monthly volume.

    Every account can also send from the sandbox domain before verifying one of its own, delivering to the account owner's verified address. Three simulator recipients force a fixed outcome, delivered, bounced or complained, so you can exercise each branch of your webhook handler. They never add anything to your suppression list.

  39. Node SDK

    Send email from Node with a typed client that handles retries and idempotency for you.

    The @avelto/sdk package is the official Node client. It needs Node 20 or newer, works with both ESM and CommonJS, is fully typed, and has no runtime dependencies. It covers emails, domains, webhooks and suppressions.

    Two things happen without you asking. Every send carries an idempotency key, a fresh one per call unless you pass your own, and failed requests are retried up to three times with exponential backoff and jitter, honouring Retry-After on rate limits. Requests that could have been processed but are not safe to repeat are left alone. The package also exports verifyWebhookSignature so your handler can check an incoming event in one line.

    TypeScript
    import { Avelto } from "@avelto/sdk";const avelto = new Avelto(process.env.AVELTO_API_KEY!);const { id } = await avelto.emails.send({  from: "hello@mail.acme.com",  to: "jane@example.com",  subject: "Welcome to Acme",  html: "<p>Thanks for signing up.</p>",});
  40. The Avelto API

    Verify a domain, create an API key and send email with a single request.

    Avelto is live. Verify a sending domain, create an API key, and post one JSON body to send an email. Every send returns an id you can look up, with a status and a full event log covering sent, delivered, bounced, complained and failed.

    Send an Idempotency-Key header and a retry can never create a second email. Set scheduled_at to hold a message for up to thirty days, and cancel it any time before it goes. Hard bounces and complaints are added to your suppression list automatically, so you never send to an address that already rejected you.

    shell
    curl -X POST https://api.avelto.dev/v1/emails \  -H "Authorization: Bearer av_live_..." \  -H "Content-Type: application/json" \  -d '{    "from": "hello@mail.acme.com",    "to": "jane@example.com",    "subject": "Welcome to Acme",    "html": "<p>Thanks for signing up.</p>"  }'