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:
- 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.
- 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.
- 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.
The failure mode
emails.send()forwards the request body verbatim (request({ body }), no client-side validation), andcomponents.schemas.SendEmailV1declaresrequired: ["to"]withadditionalPropertiesunset. 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/emailswithAuthorization: Bearer, byte-identical in shape to useSend's — but the HTML content field isbody, nothtml, and reply-to isreply, notreplyTo. Every mainstream provider (Resend, useSend, SendGrid, Postmark, Mailgun, Nodemailer) calls ithtml.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:
htmlis 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:
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.htmlandreplyToas 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.additionalProperties: falseplus a 422, ortrueplus "extras are ignored").Option 1 matches an instinct the SDK already has:
sendTestrefuses afromrather 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.0andhttps://api.sendly.now/api/openapi.jsonon 2026-09-03.