I didn't really know where to post this.
Admins: Feel free to move it to Share your nodes if it belongs there
Just something for the community however:
This is not a published node, but it's a Subflow I put together that we use to send email using our O365 Mail Services via the Graph API - it's been working flawlessly for quite some time
The subflow asks for 3 env vars
- TENANT_ID
- CLIENT_ID
- SECRET
The application you register on the tenant admin portal must have the ability to send email on behalf of the person specified in from
.
Then to send an email, send the following.
const msg = {
payload: '<h1>Hello, World</h1>',
topic : `Email Subject`,
to : string[],
cc: string[], /* Optional */
bcc : string[], /* Optional */
from : string,
replyto: string, /* Optional */
attachments: Attachment[], /* Optional */
importance : 'high' | 'normal' | 'low' /* Optional, Default: normal */
}
Attachment
{
name: 'MyDocument.docx',
type: 'Document/MimeType',
content:'Base64 Representation of the file bytes'
}
Enjoy
[{"id":"2e1e2fbf2cfbde59","type":"subflow","name":"Graph Email Send","info":"","category":"","in":[{"x":120,"y":80,"wires":[{"id":"1d386c851314975d"}]}],"out":[{"x":1540,"y":80,"wires":[{"id":"e530be0ffd2cdcb5","port":0}]},{"x":1540,"y":140,"wires":[{"id":"9eb88ef4116b1bf8","port":0}]}],"env":[{"name":"TENANT_ID","type":"str","value":"","ui":{"label":{"en-US":"Tenant ID"}}},{"name":"CLIENT_ID","type":"str","value":"","ui":{"label":{"en-US":"Client ID"}}},{"name":"SECRET","type":"str","value":"","ui":{"label":{"en-US":"Secret"}}}],"meta":{},"color":"#A6BBCF","icon":"node-red/envelope.svg","status":{"x":1300,"y":400,"wires":[{"id":"4236da927971018d","port":0},{"id":"1242fc29a03fbf12","port":0},{"id":"a708cf1f0d1bad1e","port":0},{"id":"70f015e724f84bee","port":0}]}},{"id":"f1bae2e151ea1a77","type":"function","z":"2e1e2fbf2cfbde59","name":"Email Send Request","func":"const AccessToken = msg.payload.access_token\nconst URL = `https://graph.microsoft.com/v1.0/users/${msg.email.from}/sendMail`;\nconst Body = {\n \"message\": {\n \"subject\": msg.email.subject,\n \"body\": {\n \"contentType\": \"HTML\",\n \"content\": msg.email.body\n },\n \"importance\": msg.email.importance,\n \"toRecipients\": [],\n \"from\": {\n \"emailAddress\": {\n \"address\": msg.email.from\n }\n }\n }\n}\n\nconst Headers = {\n \"Authorization\": `Bearer ${AccessToken}`,\n \"Content-Type\": \"application/json\"\n}\n\nif(msg.email.attachments !== undefined){\n Body.message.attachments = [];\n for (let i = 0; i < msg.email.attachments.length; i++) {\n Body.message.attachments.push({\n \"@odata.type\": \"#microsoft.graph.fileAttachment\",\n \"name\": msg.email.attachments[i].name,\n \"contentType\": msg.email.attachments[i].type,\n \"contentBytes\": msg.email.attachments[i].content\n });\n \n }\n \n}\n\nif (msg.email.replyto !== undefined) {\n Body.message.replyTo = [];\n Body.message.replyTo.push({\n \"emailAddress\": {\n \"address\": msg.email.replyto\n }\n });\n}\n\nif (msg.email.bcc !== undefined) {\n Body.message.bccRecipients = []\n for (let i = 0; i < msg.email.bcc.length; i++) {\n Body.message.bccRecipients.push({\n \"emailAddress\": {\n \"address\": msg.email.bcc[i]\n }\n });\n }\n}\n\n\n\nif (msg.email.cc !== undefined) {\n Body.message.ccRecipients = []\n for (let i = 0; i < msg.email.cc.length; i++) {\n Body.message.ccRecipients.push({\n \"emailAddress\": {\n \"address\": msg.email.cc[i]\n }\n });\n }\n}\n\nfor (let i = 0; i < msg.email.to.length; i++) {\n Body.message.toRecipients.push({\n \"emailAddress\": {\n \"address\": msg.email.to[i]\n }\n });\n}\n\nreturn {\n payload: Body,\n url: URL,\n headers: Headers\n}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":700,"y":200,"wires":[["1242fc29a03fbf12","d27d27c58ca27a08"]]},{"id":"1d386c851314975d","type":"function","z":"2e1e2fbf2cfbde59","name":"Access Token Request","func":"const ClientID = env.get(\"CLIENT_ID\");\nconst Secret = env.get(\"SECRET\");\nconst Tenant = env.get(\"TENANT_ID\");\nconst Scope = \"https://graph.microsoft.com/.default\";\nconst GrantType = \"client_credentials\";\n\nif(msg.req !== undefined){\n flow.set(\"req\",msg.req);\n flow.set(\"res\",msg.res);\n}\nelse{\n flow.set(\"req\",undefined);\n flow.set(\"res\",undefined);\n}\n\nconst Email = {\n to:msg.to,\n from:msg.from,\n subject:msg.topic,\n importance:msg.importance || \"normal\",\n body:msg.payload,\n}\n\nif(msg.replyto !== undefined){\n Email.replyto = msg.replyto\n}\n\nif(msg.cc !== undefined){\n Email.cc = msg.cc\n}\n\nif(msg.attachments !== undefined){\n Email.attachments = msg.attachments\n}\n\nif(msg.bcc !== undefined){\n Email.bcc = msg.bcc\n}\n\nconst URL = `https://login.microsoftonline.com/${Tenant}/oauth2/v2.0/token`;\n\nconst Payload = {\n client_id:ClientID,\n scope:Scope,\n client_secret:Secret,\n grant_type:GrantType\n}\n\nconst Headers = {\n \"Content-Type\":\"application/x-www-form-urlencoded\"\n}\n\nreturn {headers:Headers, payload:Payload, url: URL, email:Email}\n\n\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":280,"y":200,"wires":[["898310f49121e920","4236da927971018d"]]},{"id":"898310f49121e920","type":"http request","z":"2e1e2fbf2cfbde59","name":"","method":"POST","ret":"obj","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","credentials":{},"x":490,"y":200,"wires":[["f1bae2e151ea1a77"]]},{"id":"4236da927971018d","type":"function","z":"2e1e2fbf2cfbde59","name":"Token Status","func":"return {payload:{fill:\"yellow\",shape:\"dot\",text:\"Fetching Access Token\"}}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":450,"y":400,"wires":[[]]},{"id":"1242fc29a03fbf12","type":"function","z":"2e1e2fbf2cfbde59","name":"Send Status","func":"return {payload:{fill:\"yellow\",shape:\"dot\",text:\"Sending Email\"}}","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":810,"y":380,"wires":[[]]},{"id":"d27d27c58ca27a08","type":"http request","z":"2e1e2fbf2cfbde59","name":"","method":"POST","ret":"txt","paytoqs":"ignore","url":"","tls":"","persist":false,"proxy":"","authType":"","credentials":{},"x":1030,"y":200,"wires":[["67b073d65e9591c9"]]},{"id":"a708cf1f0d1bad1e","type":"function","z":"2e1e2fbf2cfbde59","name":"Send Status","func":"return {payload:{fill:\"green\",shape:\"dot\",text:\"Sent\"}}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1610,"y":200,"wires":[[]]},{"id":"67b073d65e9591c9","type":"switch","z":"2e1e2fbf2cfbde59","name":"","property":"statusCode","propertyType":"msg","rules":[{"t":"eq","v":"202","vt":"str"},{"t":"else"}],"checkall":"true","repair":false,"outputs":2,"x":1190,"y":200,"wires":[["a708cf1f0d1bad1e","e530be0ffd2cdcb5"],["70f015e724f84bee","9eb88ef4116b1bf8"]]},{"id":"70f015e724f84bee","type":"function","z":"2e1e2fbf2cfbde59","name":"Send Status","func":"return {payload:{fill:\"red\",shape:\"dot\",text:\"Error\"}}\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1330,"y":260,"wires":[[]]},{"id":"e530be0ffd2cdcb5","type":"function","z":"2e1e2fbf2cfbde59","name":"","func":"if(flow.get(\"req\") !== undefined){\n msg.req = flow.get(\"req\");\n msg.res = flow.get(\"res\");\n}\nelse{\n msg.req = undefined\n msg.res =undefined\n}\n\n\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1420,"y":80,"wires":[[]]},{"id":"9eb88ef4116b1bf8","type":"function","z":"2e1e2fbf2cfbde59","name":"","func":"if(flow.get(\"req\") !== undefined){\n msg.req = flow.get(\"req\");\n msg.res = flow.get(\"res\");\n\n}\nelse{\n msg.req = undefined\n msg.res =undefined\n}\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1420,"y":140,"wires":[[]]}]