Skip to content

emails.send forwards unknown keys verbatim, so a useSend-shaped payload sends an empty email #9

Description

@AminDhouib

The failure mode

emails.send() forwards the request body verbatim (request({ body }), no client-side validation), and components.schemas.SendEmailV1 declares required: ["to"] with additionalProperties unset. Together that means a caller cannot tell — from the SDK types, the runtime, or the published contract — whether a wrong key is refused or silently dropped.

That matters far more than it sounds, because of what the wrong key usually is. Sendly's send endpoint is POST /api/v1/emails with Authorization: Bearer, byte-identical in shape to useSend's — but the HTML content field is body, not html, and reply-to is reply, not replyTo. Every mainstream provider (Resend, useSend, SendGrid, Postmark, Mailgun, Nodemailer) calls it html.

So the naive migration — point the base URL at Sendly, keep the payload — compiles, connects, authenticates, and sends an empty email. TypeScript does not catch it either: html is an excess property, and excess-property checking does not fire when the object is built elsewhere and passed as a variable, which is what every real transport does.

What I would like the SDK to do

Any one of these closes it:

  1. Validate the send payload client-side and throw on an unknown key, with a message that names the likely intent — unknown field "html"; Sendly's HTML content field is "body". This is the highest-value option: it converts a silent production incident into a message that fixes itself.
  2. Accept html and replyTo as aliases and map them before the request. That reduces the whole migration to a base-URL change, which is what integrators already expect it to be.
  3. At minimum, say what the server does with unknown keys, in the README and in the schema (additionalProperties: false plus a 422, or true plus "extras are ignored").

Option 1 matches an instinct the SDK already has: sendTest refuses a from rather than ignoring it, on the reasoning that a request expecting a different sender should never get a success it would misread. An email with no content is the same class of misread.

What we had to do instead

Build the request body key-by-key from a literal, never spreading caller input, and assert the exact key set in a unit test — a strictness the provider's own contract should be supplying.

Context

Found while migrating a production app onto Sendly. Verified against sendly-sdk@1.0.0 and https://api.sendly.now/api/openapi.json on 2026-09-03.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions