SharePoint’s SP.Utilities.Utility.SendEmail lets you send email straight from a list workflow, a custom web part, or a script — without going through Outlook or Power Automate. Two ways to call it:
In this post: Real limitations · Option 1: raw REST API · Option 2: PnPjs · When would you actually use this? · Best practices
Real limitations
Worth knowing before you build around this: it doesn’t support file attachments — the SP.Utilities.EmailProperties object has no property for one. You can inline images hosted on the same SharePoint domain, but a real attachment isn’t possible through this API. It also can’t send to a distribution group/list, only individual mailboxes, and there’s a cap on total content size. If any of those are dealbreakers, that’s your sign to use Power Automate’s send-email action instead, which supports all three.
Option 1: raw REST API
function sendEmail() {
var siteUrl = _spPageContextInfo.webAbsoluteUrl;
$.ajax({
contentType: 'application/json',
url: siteUrl + "/_api/SP.Utilities.Utility.SendEmail",
type: "POST",
data: JSON.stringify({
'properties': {
'__metadata': { 'type': 'SP.Utilities.EmailProperties' },
'To': { 'results': ['user@domain.com'] },
'Subject': 'Custom Notification',
'Body': 'Hello! This is a custom email from SharePoint.'
}
}),
headers: {
"Accept": "application/json;odata=verbose",
"X-RequestDigest": $("#__REQUESTDIGEST").val()
},
success: function () {
console.log('Email sent successfully!');
},
error: function (err) {
console.error('Error sending email: ', err);
}
});
}
Option 2: PnPjs
import { sp } from "@pnp/sp/presets/all";
sp.utility.sendEmail({
To: ["user@domain.com"],
Subject: "PnPjs Email",
Body: "This email was sent using PnPjs!",
}).then(_ => {
console.log("Email Sent!");
}).catch(console.error);
PnPjs is less boilerplate if you’re already using it elsewhere in the project; the raw REST call is the way to go if you’re not pulling in the library just for this.
One thing worth flagging about the raw REST example above: it uses $.ajax and _spPageContextInfo, which only exist on classic SharePoint pages. Inside a modern SPFx web part, neither is available — use SPHttpClient from the SPFx context instead:
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
this.context.spHttpClient.post(
`${this.context.pageContext.web.absoluteUrl}/_api/SP.Utilities.Utility.SendEmail`,
SPHttpClient.configurations.v1,
{
headers: { 'Content-type': 'application/json;odata=verbose' },
body: JSON.stringify({
properties: {
__metadata: { type: 'SP.Utilities.EmailProperties' },
To: { results: ['user@domain.com'] },
CC: { results: ['manager@domain.com'] },
Subject: 'Custom Notification',
Body: 'Hello! This is a custom email from SharePoint.'
}
})
}
).then((response: SPHttpClientResponse) => {
if (!response.ok) { console.error('Send failed', response.statusText); }
});
Same underlying endpoint, but SPHttpClient automatically handles the request digest for you — one less thing to wire up manually inside a web part.
Validate email addresses before sending — this API won’t do it for you.
When would you actually use this?
- A list item hits a certain status and someone needs a notification — without spinning up a full Power Automate flow for something this small.
- You’re building a custom SPFx web part and need to send a confirmation email as part of a form submission.
- You need email sent from client-side code where a full workflow engine would be overkill.
Best practices
- Validate email addresses before sending — this API won’t do it for you.
- Sanitize any user input going into the HTML body to avoid injection.
- Wrap calls in try/catch (or
.catch()) — don’t let a failed send disappear silently. - Don’t fire these inside a tight loop without batching — you’ll hit throttling.
- Log success/failure somewhere if this matters for audit purposes.
That’s the fix — let me know in the comments if it doesn’t quite match your setup.


