O365 Graph Mail Sender Subflow

I didn't really know where to post this.
Admins: Feel free to move it to Share your nodes if it belongs there :sweat_smile:

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":[[]]}]
2 Likes