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(
[email protected],
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(
[email protected] [email protected],
[email protected],
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;
It's your turn to help shape SAS Innovate 2027. Share your expertise and inspire the SAS community.
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.