Hello,
Currently, we have configured an Azure app (Microsoft Graph) to send emails from our SAS Viya environment using SAS code. I am able to successfully send emails using this code.
However, the issue is that I can only send an email to a single email address. I am unable to specify multiple email addresses in the To field with the current code. I am struggling to update the code logic to support multiple recipients.
Could you please help me with this?
%macro send_graph_mail(
to=,
cc=,
subject=,
body=
);
%local access_token has_cc CLIENT_ID CLIENT_SECRET TENANT_ID;
%let has_cc = %sysfunc(ifc(%length(&cc) > 0, 1, 0));
%let CLIENT_ID = xxxxxxx;
%let CLIENT_SECRET = xxxxxxx;
%let TENANT_ID = xxxxxxx;
/*--------------------------------*/
/* Get OAuth token */
/*--------------------------------*/
filename resp temp;
proc http
url="https://login.microsoftonline.com/&TENANT_ID/oauth2/v2.0/token"
method="POST"
in="grant_type=client_credentials%str(&)
client_id=&CLIENT_ID%str(&)
client_secret=&CLIENT_SECRET%str(&)
scope=https://graph.microsoft.com/.default"
ct="application/x-www-form-urlencoded"
out=resp;
run;
libname auth json fileref=resp;
data _null_;
set auth.root;
call symputx('access_token', access_token );
run;
/*--------------------------------*/
/* Build mail JSON */
/*--------------------------------*/
filename mail temp;
data _null_;
length subject_txt body_txt addr $1000;
file mail lrecl=32767;
subject_txt = strip(symget('subject'));
body_txt = strip(symget('body'));
subject_txt = tranwrd(subject_txt, '"', '\"');
body_txt = tranwrd(body_txt, '"', '\"');
addr = strip(symget('to'));
put '{';
put ' "message": {';
put ' "subject": ' '"' subject_txt '"' ',';
put ' "body": {';
put ' "contentType": "Text",';
put ' "content": ' '"' body_txt '"' ;
put ' },';
put ' "toRecipients": [';
addr = symget('to');
addr = strip(addr);
addr = left(addr);
put ' { "emailAddress": { "address": "' addr +(-1) '" } }';
put ' ]';
put ' }';
put '}';
run;
/*-----------------------------*/
/* Send email */
/*-----------------------------*/
filename sendresp temp;
proc http
url="https://graph.microsoft.com/v1.0/users/&SENDER_EMAIL/sendMail"
method="POST"
in=mail
out=sendresp;
headers
"Authorization"="Bearer &access_token"
"Content-Type"="application/json";
run;
%mend;
data _null_;
format dte ddmmyy10. tme time8.;
dt = datetime();
call symputx(
'mail_body',
cats(
'Hello,',
'The scheduled job completed successfully at ',
put(datepart(dt), ddmmyy10.), ' ',
put(timepart(dt), time8.),
'Thank you',
'Platform Support Team'
),
'L'
);
run;
%send_graph_mail(
to=fresh.starter@org.com,
subject=Test SAS Completed Successfully,
body=%superq(mail_body)
);
This is not a complete answer, but you'll need to add some looping to generate the json array of email addresses.
You could try replacing your current line:
put ' { "emailAddress": { "address": "' addr +(-1) '" } }';
With a macro loop like below:
%local i ;
%do i=1 %to %sysfunc(countw(&to,%str( ))) ;
%if &i>1 %then %do ;
put "," ;
%end ;
put " { ""emailAddress"": { ""address"": ""%scan(&to,&i,%str( ))"" } }";
%end ;
That would allow you to pass a space-delimited list of email addresses.
This is not a complete answer, but you'll need to add some looping to generate the json array of email addresses.
You could try replacing your current line:
put ' { "emailAddress": { "address": "' addr +(-1) '" } }';
With a macro loop like below:
%local i ;
%do i=1 %to %sysfunc(countw(&to,%str( ))) ;
%if &i>1 %then %do ;
put "," ;
%end ;
put " { ""emailAddress"": { ""address"": ""%scan(&to,&i,%str( ))"" } }";
%end ;
That would allow you to pass a space-delimited list of email addresses.
Thank you so much @Quentin .it worked.
But when I update the same code for cc Recipients, it throws me an error message.
{"error":{"code":"BadRequest","message":"Unable to read JSON request payload. Please ensure Content-Type header is set and payload i
s of valid JSON format.","innerError":{"date":"2026-04-28T21:17:39","request-id":"xxxx","client-request-id":"xxxx"}}}
%macro send_graph_mail(
to=,
cc=,
subject=,
body=
);
%local access_token has_cc CLIENT_ID CLIENT_SECRET TENANT_ID;
%let has_cc = %sysfunc(ifc(%length(&cc) > 0, 1, 0));
%let CLIENT_ID = xxxxxxxxxxxxxxxxxxxxx;
%let CLIENT_SECRET = xxxxxxxxxxxxxxxxxxxxx;
%let TENANT_ID = xxxxxxxxxxxxxxxxxxxxx;
/*--------------------------------*/
/* Get OAuth token */
/*--------------------------------*/
filename resp temp;
proc http
url="https://login.microsoftonline.com/&TENANT_ID/oauth2/v2.0/token"
method="POST"
in="grant_type=client_credentials%str(&)
client_id=&CLIENT_ID%str(&)
client_secret=&CLIENT_SECRET%str(&)
scope=https://graph.microsoft.com/.default"
ct="application/x-www-form-urlencoded"
out=resp;
run;
libname auth json fileref=resp;
data _null_;
set auth.root;
call symputx('access_token', access_token );
run;
/*--------------------------------*/
/* Build mail JSON */
/*--------------------------------*/
filename mail temp;
data _null_;
length subject_txt body_txt addr $1000;
file mail lrecl=32767;
subject_txt = strip(symget('subject'));
body_txt = strip(symget('body'));
subject_txt = tranwrd(subject_txt, '"', '\"');
body_txt = tranwrd(body_txt, '"', '\"');
addr = strip(symget('to'));
put '{';
put ' "message": {';
put ' "subject": ' '"' subject_txt '"' ',';
put ' "body": {';
put ' "contentType": "HTML",';
put ' "content": ' '"' body_txt '"' ;
put ' },';
put ' "toRecipients": [';
%local i ;
%do i=1 %to %sysfunc(countw(&to,%str( ))) ;
%if &i>1 %then %do ;
put "," ;
%end ;
put " { ""emailAddress"": { ""address"": ""%scan(&to,&i,%str( ))"" } }";
%end ;
put ' ]';
put ' "ccRecipients": [';
%local j ;
%do j=1 %to %sysfunc(countw(&cc,%str( ))) ;
%if &j>1 %then %do ;
put "," ;
%end ;
put " { ""emailAddress"": { ""address"": ""%scan(&cc,&j,%str( ))"" } }";
%end ;
put ' ]';
put ' }';
put '}';
run;
/*-----------------------------*/
/* Send email */
/*-----------------------------*/
filename sendresp temp;
proc http
url="https://graph.microsoft.com/v1.0/users/&SENDER_MAIL/sendMail"
method="POST"
in=mail
out=sendresp;
headers
"Authorization"="Bearer &access_token"
"Content-Type"="application/json";
run;
data _null_;
infile sendresp;
input;
putlog _infile_;
run;
%mend;
data _null_;
format dte ddmmyy10. tme time8.;
dt = datetime();
call symputx(
'mail_body',
catx(
'',
'<html><body>',
'<p>Hello,</p>',
'<p>The scheduled job completed successfully on ',
put(datepart(dt), ddmmyy10.),
' at ',
put(timepart(dt), time8.),
'.</p>',
'<p>Thank you,<br>',
'Platform Support Team</p>',
'</body></html>'
),
'L'
);
run;
%send_graph_mail(
to=fresh.starter@org.com freshstarter@gmail.com,
cc=fresh.starter@org.com,
subject=Test SAS Extract Completed Successfully,
body=%superq(mail_body)
);
It is working now. I have missed a comma, Thanks for the support
%if &has_cc %then %do;
put ' , "ccRecipients": [';
%do j=1 %to %sysfunc(countw(&cc,%str( ))) ;
%if &j>1 %then %do ;
put "," ;
%end ;
put " { ""emailAddress"": { ""address"": ""%scan(&cc,&j,%str( ))"" } }";
%end ;
put ' ]';
%end;
Dive into keynotes, announcements and breakthroughs on demand.
Explore Now →Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.