Skip to contentYourMail

Changelog

Last release 8 October 2026

What changed, newest first. Platform releases cover the API, the dashboard and billing; internal tooling and operational changes are left out. The Node SDK is versioned separately on npm.

Platform releases

Release 1.6.0

Features

  • Simplify the dashboard and landing page (phase 1)

Release 1.5.0

Features

  • Bold UI refresh of the public surface

Release 1.4.0

Bug Fixes

  • Serve robots.txt on api.yourmail.dev so crawlers skip the API

Release 1.2.0

Features

  • Refund and dispute policy, Stripe error capture

Release 1.1.3

Bug Fixes

  • Harden the Stripe integration

Release 1.1.1

Bug Fixes

  • Emit datePublished on article JSON-LD and link the UK vs EU page from the footer

Node SDK

The yourmail package on npm. Its reference is the Node SDK docs.

yourmail 0.8.3

Patch Changes

  • List the company support inbox (support@mathersoftware.com) as the package's bug-report contact.

yourmail 0.8.2

Patch Changes

  • Update the package author contact email, so the npm page lists the address that actually answers support mail.

yourmail 0.8.1

Patch Changes

  • Attribute the package to Mather Software Ltd, the company that builds and runs YourMail.

    package.json author and the LICENSE copyright line named "YourMail", which is a product rather than a legal person, so the copyright notice had no holder that could own it. Both now name Mather Software Ltd (https://mathersoftware.com). Neither value is read at runtime; the code is unchanged.

yourmail 0.8.0

Minor Changes

  • Errors carry the call's request id, and the README's first example works on day one.
    • err.requestId — a YourMailError raised from an API response carries the x-request-id that response returned (including invalid_response). Search the dashboard's Logs tab for it, or quote it to support. Absent on errors raised locally (timeouts, network failures) and on a response without the header.
    • README: the quickstart sent from hello@mail.acme.com, which a brand-new account cannot send from until that domain is verified — it now uses the shared test-mode sender and says when to switch. The domains.verify example no longer reads a variable inside its own declaration, and there is now a section on verifying webhooks.
  • The client stops hanging, webhook verification takes what frameworks hand you, and attachments take bytes.
    • A `signal` no longer switches off the timeout. Passing one only to be able to cancel used to replace timeoutMs, so a stalled connection could hang the call for ever. Both now apply, whichever fires first, on every supported Node version. The other side of that: a signal can no longer EXTEND a call past timeoutMs — if you passed AbortSignal.timeout(60_000) to get a longer window, raise timeoutMs on the client instead.
    • A signal of yours that fires rejects with its own reason — during the request, while the body is read, or during a retry backoff. For AbortSignal.timeout(n) that is a TimeoutError DOMException, where it used to be a YourMailError with type: "timeout", so a catch branching on that type needs err.name === "TimeoutError" too; cancelling during a backoff now rejects with the AbortError rather than the previous attempt's ServerError. The client's own timeoutMs still rejects as YourMailError type: "timeout" — including when it fires mid-body, which used to read as invalid_response.
    • Webhook verification takes what frameworks hand you. header accepts string | string[] | null | undefined (a missing header is false); payload accepts a string or bytes (Buffer, Uint8Array, ArrayBuffer); secret accepts an array so a rotation can accept old and new at once, with unset (undefined or empty) entries skipped and an all-unset secret a clear TypeError.
    • constructWebhookEvent verifies and returns the parsed, typed body — or throws — so a handler cannot act on an unverified one. WebhookEvent and WebhookEventType are exported.
    • Attachments take raw bytes (Buffer, Uint8Array, ArrayBuffer, from any realm), base64-encoded for you; a Buffer in JSON used to arrive as {type, data} and 400. `scheduledAt` takes a `Date`.
    • `react` now also renders the plain-text part unless you pass text.

yourmail 0.7.0

Minor Changes

  • apiKeys.create returns the new key's id (and name, scope, last4)

    beside the plaintext. It returned only { key }, so a script that minted a key could revoke it later only by listing every key and guessing which one it had made. Typed as CreatedApiKey.

yourmail 0.6.1

Patch Changes

  • Correct what domains.verify actually does, and document its two failure modes.
    • 502 `domain_error` (a ServerError) when SES itself cannot be reached. Transient and worth retrying with backoff — the poll throws before any status write, so nothing has changed. A DNS resolver failure is deliberately not an error: the SES verdict still applies and dnsCheckedAt stays put, which is the signal that the records were not re-read.
    • A ~10s cooldown. A call within roughly ten seconds of the last completed check answers from the stored row without going out to SES again, so the loop the docs recommend is harmless but a tighter one buys nothing. Poll every 10–30s; DNS propagation is minutes.

    0.6.0 shipped saying an already-verified domain is "a no-op that still returns its row". It is not: a verified domain IS re-checked — that is how a repaired DNS record gets confirmed — and only dnsCheckedAt and the advisory dnsRegressedAt move, never status. A failed domain is the one that is genuinely skipped, because it is terminal.

    Also documented, because both are things a caller has to branch on and neither was mentioned:

    Docs only — no behaviour change in the client.

yourmail 0.6.0

Minor Changes

  • Finish the flows the client could start but not complete.
    • domains.verify(id) — re-check a domain's DNS and verification status now, returning the refreshed row. domains.create hands back the DNS records and there was then no way to ask whether they had taken: a script could begin domain setup and had to finish it in the dashboard. Safe to poll — an already-verified domain is a no-op that still returns the row.
    • `listAll()` / `listAllSuppressions()` — async iterators that page for you. GET /v1/emails may legally return an empty page with hasMore: true, so a hand-rolled loop that stops on a short page silently reports nothing for an account that has rows further back; a nextCursor that does not advance is the other easy mistake, and it never ends. Both are handled once, here.
    • createSuppressions(emails) — the array form POST /v1/suppressions has always accepted (up to MAX_SUPPRESSION_IMPORT, now exported). Migrating a list of 5,000 addresses no longer means 5,000 round trips against a batch endpoint that was already there.

yourmail 0.5.0

Minor Changes

  • Add usage() and the management namespaces, and stop dropping list({ search }).
    • yourmail.usage() — GET /v1/usage. The README and /docs/api/usage have shown this call for some time; the method did not exist.
    • yourmail.domains.{list,create,get,delete}, yourmail.apiKeys.{list,create,revoke} and yourmail.webhooks.{list,create,update,delete} — the eleven management routes the API has served since they shipped, so onboarding can be scripted without raw fetch. All need a full-scope key. The webhook signing secret is typed on the create result only, matching the API.
    • list({ search }) now sends the search parameter. It was documented on ListEmailsOptions and implemented server-side; the client omitted it from the query string, so a search returned an unfiltered list.
    • EmailStatus gains cc, bcc, replyTo, html and text, which GET /v1/emails/:id already returned, plus scheduledAt, which it now returns. DeliveryEventMeta gains link and userAgent, the two fields the event timeline has always carried on an open or a click.
    • list({ status }) accepts "scheduled" and "canceled". The type already offered them; the accompanying API release is what stops rejecting them, so an older client against the current API can send them too.
    • The three create methods no longer retry. send/batch are safe to retry because they carry an idempotency key; these do not. For apiKeys.create and webhooks.create nothing dedups them server-side either, so a lost response could leave a live API key whose plaintext nobody holds, or a duplicate webhook endpoint. domains.create does dedup — a repeated name is a 409 — so there a retry cannot duplicate anything, but it would report a hard failure for a create that in fact succeeded. Pass { maxRetries } to opt back in per call; a non-finite value is treated as zero rather than falling back to the client default.
    • Any validation_error is now a ValidationError, whatever its status. That covers the 403 plan ceilings on webhooks.create, which previously surfaced as an AuthenticationError and sent the caller off to check a key that was working perfectly — and the 409 from cancel() on a message that has already gone, which was previously a bare YourMailError.
    • domains.delete and webhooks.delete answer 404 once the resource is gone, so a retry after a lost success reports NotFoundError. They are retried like any other read; treat a 404 from a delete you just issued as success.
    • A 5xx is now always a ServerError, including 502 domain_error — which domains.create returns when SES refuses to create the identity. The policy-type branch (domain_error / deliverability_blocked / account_pending_review are deliberately not AuthenticationError) was matched before the status branch, so that transient failure arrived as a bare YourMailError and a caller branching on ServerError gave up on an outage that clears by itself.

    Minor: new surface, an observable behaviour change on list, a retry-policy change on three methods, and a reclassification a caller's catch can see.

yourmail 0.4.0

Minor Changes

  • Classify the account_pending_review 403 as a policy error, not an auth error.

    Minor rather than patch, because this is an observable classification change. A consumer catching AuthenticationError currently sees this type and will stop seeing it — which is the whole point of the fix, and is still a behaviour change on a released package rather than a silent repair.

    A brand-new account is walled to its own verified domains, its owner address and the AWS SES simulator until an operator approves it; sends to anyone else answer 403 account_pending_review. That fell through errorFromResponse's type branch into AuthenticationError, which sends the caller off to check an API key that is working perfectly. It is now a bare YourMailError, alongside the two other 403s that are policy rather than credentials (domain_error, deliverability_blocked).

    errors.test.ts now asserts its case table covers API_ERROR_TYPES exhaustively, so the next error type added to the API cannot reach a release without someone stating what the SDK should throw for it — the omission that produced this fix. That guard immediately found two more unpinned types (quota_exceeded, sandbox_recipient); both were already classified correctly and are now covered.

yourmail 0.3.2

Patch Changes

  • Attribute the package to YourMail rather than a personal name.

    package.json author and the LICENSE copyright line both named an individual. Both are published verbatim in the npm tarball and rendered on the package page, so the personal name was public on every install. Neither value is read by anything at runtime.

    Published versions are immutable, so this only cleans the versions from here on — 0.2.0, 0.3.0 and 0.3.1 keep the old attribution unless they are unpublished.

    Note the registry's maintainers email is a separate, account-level value and is not affected by any publish: it is changed with npm profile set email.

yourmail 0.3.1

Patch Changes

  • createSuppression now honours a per-request maxRetries.

    It was the one method that didn't forward the option, so a caller passing { maxRetries: 0 } silently got the client default instead — the failure mode being an opt-out of retrying a write that quietly didn't take.

yourmail 0.3.0

Minor Changes

  • Automatic retries and typed error subclasses.
    • The client now retries transient failures — rate_limited 429s, 5xx, network errors and timeouts — with exponential backoff and full jitter, honouring a server-sent Retry-After up to a 20s ceiling. Configurable via the new maxRetries option (default 2; set 0 to opt out), overridable per call. A 4xx is never retried, nor is a quota_exceeded 429 or any 429 asking for longer than the ceiling — a monthly quota or a daily cap can't clear inside a retry loop, so the error surfaces at once and you schedule off err.retryAfter. A caller's own AbortSignal stops the loop rather than retrying behind their back, including mid-backoff.
    • Retrying a send can't double-deliver. send() and batch() now attach an idempotencyKey when the caller supplies none, and reuse it across attempts, so a replay after a lost response is deduped by the API rather than delivered again. Batch keys are per item. An explicitly supplied key (either casing) always wins.
    • timeoutMs now bounds the whole call including retries rather than each attempt, so enabling retries doesn't multiply the worst-case latency a caller already budgeted for.
    • Errors now surface as ValidationError, AuthenticationError, NotFoundError, RateLimitError or ServerError where the status maps onto one, so callers can branch with instanceof instead of comparing err.type strings. All extend YourMailError, so existing catches are unaffected — but note err.name on those responses is now the subclass name rather than "YourMailError". The two 403s that aren't authentication problems (domain_error, deliverability_blocked) deliberately stay the base class.

yourmail 0.2.0

Minor Changes

  • Managed unsubscribe + transport-error hardening (publishes changes that had no changeset).
    • Add bulk?: boolean to the send options — flags a non-transactional message so the API injects a one-click List-Unsubscribe header and scopes unsubscribes to bulk mail only.
    • Add "unsubscribe" to the SuppressionReason union (recipient used the one-click link; blocks bulk mail only).
    • YourMailError now accepts a { cause } option and network/transport failures are normalized instead of leaking raw fetch errors.

yourmail 0.1.1

Patch Changes

  • Publish via npm OIDC trusted publishing (tokenless). No package

    changes — this release verifies the automated publish pipeline. Provenance is off because the source repo is private (npm requires a public repo for it).

yourmail 0.1.0

Minor Changes

  • Flatten the client API and default baseUrl.

    BREAKING: the emails namespace is removed — call yourmail.send(...), yourmail.batch(...), and yourmail.get(...) instead of yourmail.emails.*.

    baseUrl now defaults to https://api.yourmail.dev, so a live-key client is new YourMail(key) with no options; pass baseUrl only for a dev or self-hosted deployment.

  • Rename the client and error classes for the YourMail rebrand.

    BREAKING: the client class is renamed Emailr → YourMail and the error class EmailrError → YourMailError. Update imports: import { YourMail, YourMailError } from "yourmail".

    First published release. Zero-dependency TypeScript client for the YourMail API (dual ESM + CommonJS build). Covers send, batch send, and email retrieval. No scheduled sends / cancel() (cut for V1).