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.jsonauthorand theLICENSEcopyright 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— aYourMailErrorraised from an API response carries thex-request-idthat response returned (includinginvalid_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. Thedomains.verifyexample 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 pasttimeoutMs— if you passedAbortSignal.timeout(60_000)to get a longer window, raisetimeoutMson 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 aTimeoutErrorDOMException, where it used to be aYourMailErrorwithtype: "timeout", so acatchbranching on that type needserr.name === "TimeoutError"too; cancelling during a backoff now rejects with theAbortErrorrather than the previous attempt'sServerError. The client's owntimeoutMsstill rejects asYourMailErrortype: "timeout"— including when it fires mid-body, which used to read asinvalid_response. - Webhook verification takes what frameworks hand you.
headeracceptsstring | string[] | null | undefined(a missing header isfalse);payloadaccepts a string or bytes (Buffer,Uint8Array,ArrayBuffer);secretaccepts an array so a rotation can accept old and new at once, with unset (undefinedor empty) entries skipped and an all-unset secret a clearTypeError. constructWebhookEventverifies and returns the parsed, typed body — or throws — so a handler cannot act on an unverified one.WebhookEventandWebhookEventTypeare exported.- Attachments take raw bytes (
Buffer,Uint8Array,ArrayBuffer, from any realm), base64-encoded for you; aBufferin 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.
- A `signal` no longer switches off the timeout. Passing one only to be able to cancel used to replace
yourmail 0.7.0
Minor Changes
apiKeys.createreturns the new key'sid(andname,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 asCreatedApiKey.
yourmail 0.6.1
Patch Changes
- Correct what
domains.verifyactually 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 anddnsCheckedAtstays 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
dnsCheckedAtand the advisorydnsRegressedAtmove, neverstatus. Afaileddomain 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.
- 502 `domain_error` (a
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.createhands 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/emailsmay legally return an empty page withhasMore: true, so a hand-rolled loop that stops on a short page silently reports nothing for an account that has rows further back; anextCursorthat does not advance is the other easy mistake, and it never ends. Both are handled once, here. createSuppressions(emails)— the array formPOST /v1/suppressionshas always accepted (up toMAX_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 droppinglist({ search }).yourmail.usage()—GET /v1/usage. The README and/docs/api/usagehave shown this call for some time; the method did not exist.yourmail.domains.{list,create,get,delete},yourmail.apiKeys.{list,create,revoke}andyourmail.webhooks.{list,create,update,delete}— the eleven management routes the API has served since they shipped, so onboarding can be scripted without rawfetch. All need afull-scope key. The webhook signing secret is typed on thecreateresult only, matching the API.list({ search })now sends thesearchparameter. It was documented onListEmailsOptionsand implemented server-side; the client omitted it from the query string, so a search returned an unfiltered list.EmailStatusgainscc,bcc,replyTo,htmlandtext, whichGET /v1/emails/:idalready returned, plusscheduledAt, which it now returns.DeliveryEventMetagainslinkanduserAgent, 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
createmethods no longer retry.send/batchare safe to retry because they carry an idempotency key; these do not. ForapiKeys.createandwebhooks.createnothing 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.createdoes 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_erroris now aValidationError, whatever its status. That covers the403plan ceilings onwebhooks.create, which previously surfaced as anAuthenticationErrorand sent the caller off to check a key that was working perfectly — and the409fromcancel()on a message that has already gone, which was previously a bareYourMailError. domains.deleteandwebhooks.deleteanswer404once the resource is gone, so a retry after a lost success reportsNotFoundError. They are retried like any other read; treat a404from a delete you just issued as success.- A
5xxis now always aServerError, including502 domain_error— whichdomains.createreturns when SES refuses to create the identity. The policy-type branch (domain_error/deliverability_blocked/account_pending_revieware deliberately notAuthenticationError) was matched before the status branch, so that transient failure arrived as a bareYourMailErrorand a caller branching onServerErrorgave 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'scatchcan see.
yourmail 0.4.0
Minor Changes
- Classify the
account_pending_review403 as a policy error, not an auth error.Minor rather than patch, because this is an observable classification change. A consumer catching
AuthenticationErrorcurrently 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 througherrorFromResponse's type branch intoAuthenticationError, which sends the caller off to check an API key that is working perfectly. It is now a bareYourMailError, alongside the two other 403s that are policy rather than credentials (domain_error,deliverability_blocked).errors.test.tsnow asserts its case table coversAPI_ERROR_TYPESexhaustively, 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.jsonauthorand theLICENSEcopyright 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
maintainersemail is a separate, account-level value and is not affected by any publish: it is changed withnpm profile set email.
yourmail 0.3.1
Patch Changes
createSuppressionnow honours a per-requestmaxRetries.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_limited429s,5xx, network errors and timeouts — with exponential backoff and full jitter, honouring a server-sentRetry-Afterup to a 20s ceiling. Configurable via the newmaxRetriesoption (default2; set0to opt out), overridable per call. A4xxis never retried, nor is aquota_exceeded429or any429asking 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 offerr.retryAfter. A caller's ownAbortSignalstops the loop rather than retrying behind their back, including mid-backoff. - Retrying a send can't double-deliver.
send()andbatch()now attach anidempotencyKeywhen 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. timeoutMsnow 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,RateLimitErrororServerErrorwhere the status maps onto one, so callers can branch withinstanceofinstead of comparingerr.typestrings. All extendYourMailError, so existing catches are unaffected — but noteerr.nameon 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.
- The client now retries transient failures —
yourmail 0.2.0
Minor Changes
- Managed unsubscribe + transport-error hardening (publishes changes that had no changeset).
- Add
bulk?: booleanto the send options — flags a non-transactional message so the API injects a one-clickList-Unsubscribeheader and scopes unsubscribes to bulk mail only. - Add
"unsubscribe"to theSuppressionReasonunion (recipient used the one-click link; blocks bulk mail only). YourMailErrornow accepts a{ cause }option and network/transport failures are normalized instead of leaking raw fetch errors.
- Add
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
emailsnamespace is removed — callyourmail.send(...),yourmail.batch(...), andyourmail.get(...)instead ofyourmail.emails.*.baseUrlnow defaults tohttps://api.yourmail.dev, so a live-key client isnew YourMail(key)with no options; passbaseUrlonly for a dev or self-hosted deployment. - Rename the client and error classes for the YourMail rebrand.
BREAKING: the client class is renamed
Emailr→YourMailand the error classEmailrError→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).