Skip to content

Commit 0eb5264

Browse files
authored
Create opt-out-api-quickstart.md
1 parent dd93c56 commit 0eb5264

File tree

1 file changed

+357
-0
lines changed

1 file changed

+357
-0
lines changed
Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
---
2+
title: Send OptOut API requests with API (HMAC)
3+
description: Learn how to send OptOut API requests with API (HMAC).
4+
services: azure-communication-services
5+
author: besh2014
6+
product manager: dbasantes
7+
ms.service: azure-communication-services
8+
ms.subservice: sms
9+
ms.date: 12/04/2024
10+
ms.topic: quickstart
11+
---
12+
13+
# Send OptOut API requests with API (HMAC)
14+
15+
This article describes how to enable opt-out management for your Azure Communication Services resource using hash message authentication code (HMAC) based authentication.
16+
17+
## Global Prerequisites
18+
19+
- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F).
20+
- An active Communication Services resource and connection string. See [Create a Communication Services resource](../create-communication-resource.md).
21+
- To enable Opt-out management on your Azure Communication Services resource, contact [[email protected]](mailto:[email protected]) to allow-list your `Immutable resource Id` (as listed on the **Properties** page in the Azure portal), and associated SMS-enabled toll-free numbers.
22+
23+
# Quickstart: Send OptOut API requests with API (HMAC)
24+
Sending is similar to SMS as described in the [Azure Communication Services Postman Tutorial](https://learn.microsoft.com/en-us/azure/communication-services/tutorials/postman-tutorial) with the difference of endpoints for OptOut Actions (Add, Remove, or Check) and body. The request body has the same structure for all actions, while the response content slightly differs.
25+
26+
### Endpoints
27+
28+
| Action | Endpoint |
29+
|--------|----------|
30+
| Add | `{{endpoint}}/sms/optouts:add?api-version=2024-12-10-preview` |
31+
| Remove | `{{endpoint}}/sms/optouts:remove?api-version=2024-12-10-preview` |
32+
| Check | `{{endpoint}}/sms/optouts:check?api-version=2024-12-10-preview` |
33+
34+
Here are some examples in different languages.
35+
36+
### Sample Request
37+
38+
#### Request Headers
39+
40+
| Header | Value |
41+
|-----------------------|-----------------------------------------------------------------|
42+
| Content-Type | application/json |
43+
| x-ms-date | Thu, 10 Aug 2023 12:39:55 GMT |
44+
| x-ms-content-sha256 | JKUqoPANwVA55u/NOCsS0Awa4cYrKKNtBwUqoaqrob0= |
45+
| Authorization | HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=IMbd3tE3nOgEkeUQGng6oQew5aEcrZJQqHkyq8qsbLg= |
46+
47+
#### Request Body
48+
49+
```json
50+
{
51+
"from": "+15551234567",
52+
"recipients": [
53+
{
54+
"to": "+15550112233"
55+
},
56+
{
57+
"to": "+15550112234"
58+
}
59+
]
60+
}
61+
```
62+
63+
### Sample Response
64+
In general, response content are the same for all actions and contain the success or failure `HttpStatusCode` per recipient. The only difference is that the `Check` action, which also returns the `isOptedOut` flag.
65+
66+
#### Response Status
67+
- 200 Ok
68+
69+
#### Add OptOut action response body
70+
71+
```json
72+
{
73+
"value": [
74+
{
75+
"to": "+15550112233",
76+
"httpStatusCode": 200
77+
},
78+
{
79+
"to": "+15550112234",
80+
"httpStatusCode": 200
81+
}
82+
]
83+
}
84+
```
85+
86+
#### Remove OptOut Action Response Body
87+
```json
88+
{
89+
"value": [
90+
{
91+
"to": "+15550112233",
92+
"httpStatusCode": 200
93+
},
94+
{
95+
"to": "+15550112234",
96+
"httpStatusCode": 200
97+
}
98+
]
99+
}
100+
```
101+
102+
#### Check OptOut Action Response Body
103+
```json
104+
{
105+
"value": [
106+
{
107+
"to": "+15550112233",
108+
"httpStatusCode": 200,
109+
"isOptedOut": true
110+
},
111+
{
112+
"to": "+15550112234",
113+
"httpStatusCode": 200,
114+
"isOptedOut": false
115+
}
116+
]
117+
}
118+
```
119+
## Sample Code to Use API
120+
121+
### C#
122+
123+
#### Prerequisites
124+
125+
- The .NET Core SDK version mush be higher than v6 for your operating system.
126+
- An active Communication Services resource and connection string. [Create a Communication Services resource](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/create-communication-resource).
127+
- An SMS-enabled telephone number. See [Get a phone number](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/telephony/get-phone-number) and `allowlisted` as described in the beginning of the article.
128+
129+
```cs
130+
using System.Globalization;
131+
using System.Security.Cryptography;
132+
using System.Text;
133+
134+
// Sample for Add action. Replace with Check or Remove as necessary.
135+
async Task SendOptOutAdd(string acsResourceConnectionString, string payload)
136+
{
137+
const string ApiPrivatePreviewVersion = "2024-12-10-preview";
138+
139+
const string dateHeader = "x-ms-date";
140+
141+
string accesskey = GetConnectionStringPart(acsResourceConnectionString, "accesskey");
142+
var endpointUri = new Uri(GetConnectionStringPart(acsResourceConnectionString, "endpoint"));
143+
144+
using var httpClient = new HttpClient();
145+
httpClient.BaseAddress = endpointUri;
146+
147+
string method = "POST";
148+
string baseAddress = httpClient.BaseAddress.ToString().TrimEnd('/');
149+
var requestUri = new Uri($"{baseAddress}/sms/optouts:add?api-version={ApiPrivatePreviewVersion }", UriKind.RelativeOrAbsolute);
150+
string hashedBody = ComputeSha256Hash(payload);
151+
string utcNowString = DateTimeOffset.UtcNow.ToString("r", CultureInfo.InvariantCulture);
152+
string stringToSign = $"{method}\n{requestUri.PathAndQuery}\n{utcNowString};{requestUri.Host};{hashedBody}";
153+
string signature = ComputeHmacSha256Hash(accesskey, stringToSign);
154+
string authHeader = $"HMAC-SHA256 SignedHeaders={dateHeader};host;x-ms-content-sha256&Signature={signature}";
155+
156+
using HttpRequestMessage request = new();
157+
request.Headers.TryAddWithoutValidation(dateHeader, utcNowString);
158+
request.Headers.TryAddWithoutValidation("x-ms-content-sha256", hashedBody);
159+
request.Headers.TryAddWithoutValidation("Authorization", authHeader);
160+
request.RequestUri = requestUri;
161+
request.Method = new HttpMethod(method);
162+
request.Content = new StringContent(payload, Encoding.UTF8, "application/json");
163+
164+
HttpResponseMessage response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
165+
166+
Console.WriteLine(response.StatusCode);
167+
Console.WriteLine(await response.Content.ReadAsStringAsync());
168+
Console.WriteLine(response.Headers.ToString());
169+
}
170+
171+
string ComputeSha256Hash(string rawData)
172+
{
173+
using SHA256 sha256Hash = SHA256.Create();
174+
byte[] bytes = sha256Hash.ComputeHash(Encoding.UTF8.GetBytes(rawData));
175+
return Convert.ToBase64String(bytes);
176+
}
177+
178+
string ComputeHmacSha256Hash(string key, string rawData)
179+
{
180+
using HMACSHA256 hmacSha256 = new HMACSHA256(Convert.FromBase64String(key));
181+
byte[] bytes = hmacSha256.ComputeHash(Encoding.ASCII.GetBytes(rawData));
182+
return Convert.ToBase64String(bytes);
183+
}
184+
185+
string GetConnectionStringPart(string acsResourceConnectionString, string key)
186+
{
187+
return acsResourceConnectionString.Split($"{key}=").Last().Split(';').First();
188+
}
189+
190+
// Usage
191+
192+
const string ConnectionString = "endpoint=https://[CONTOSO].communication.azure.com/;accesskey=******";
193+
var payload = System.Text.Json.JsonSerializer.Serialize(new
194+
{
195+
from = "+15551234567", //replace with your allowed sender number
196+
recipients = new[] {
197+
new { to = "+15550112233" } //replace with your recipient
198+
},
199+
});
200+
201+
await SendOptOutAdd(ConnectionString, payload);
202+
203+
```
204+
### JavaScript
205+
206+
#### Prerequisites
207+
208+
- Browser or Node.js Active LTS and Maintenance LTS versions (8.11.1 and 10.14.1 are recommended).
209+
- An active Communication Services resource and connection string. See [Create a Communication Services resource](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/create-communication-resource).
210+
- An SMS-enabled telephone number. See [Get a phone number](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/telephony/get-phone-number).
211+
- [CryptoJS](https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/crypto-js/CryptoJS%20v3.1.2.zip) is JavaScript implementations of standard and secure cryptographic algorithms.
212+
213+
214+
```html
215+
<script src="Scripts/CryptoJS/sha256-min.js" type="text/javascript"></script>
216+
<script src="Scripts/CryptoJS/hmac-sha256.js" type="text/javascript"></script>
217+
<script src="Scripts/CryptoJS/enc-base64-min.js" type="text/javascript"></script>
218+
219+
```
220+
221+
```js
222+
const ConnectionString = "endpoint=https://[CONTOSO].communication.azure.com/;accesskey=******";
223+
224+
// Sample for Add action. Replace with Check or Remove as necessary.
225+
function sendOptOutAdd(acsResourceConnectionString, payload, apiVersion = "2024-12-10-preview")
226+
{
227+
try
228+
{
229+
var acsRCS = acsResourceConnectionString
230+
.split(";")
231+
.map(i =>
232+
{
233+
var p = i.indexOf("=");
234+
return [i.substr(0, p), i.substr(p + 1)];
235+
})
236+
.reduce((a, i) => ({ ...a, [i[0]]: i[1] }), {});
237+
var uri = `${trimEnd(acsRCS.endpoint, "/")}/sms/optouts:add?api-version=${apiVersion}`;
238+
var url = new URL(uri);
239+
var method = "POST";
240+
var utcNow = new Date().toUTCString();
241+
var bodyJson = JSON.stringify(payload);
242+
var hashedBody = CryptoJS.SHA256(bodyJson).toString(CryptoJS.enc.Base64);
243+
var stringToSign = `${method}\n${url.pathname}${url.search}\n${utcNow};${url.host};${hashedBody}`;
244+
var signature = CryptoJS.HmacSHA256(stringToSign, CryptoJS.enc.Base64.parse(acsRCS.accesskey)).toString(CryptoJS.enc.Base64);
245+
246+
fetch(uri, {
247+
method: method,
248+
headers: {
249+
"content-type": "application/json",
250+
"x-ms-date": utcNow,
251+
"x-ms-content-sha256": hashedBody,
252+
Authorization: `HMAC-SHA256 SignedHeaders=x-ms-date;host;x-ms-content-sha256&Signature=${signature}`
253+
},
254+
body: bodyJson
255+
})
256+
.then(response => response.json())
257+
.then(console.warn)
258+
.catch(console.error);
259+
}
260+
catch (ex)
261+
{
262+
console.error(ex);
263+
}
264+
}
265+
266+
function trimEnd(s, c)
267+
{
268+
while (s.slice(-1) == c)
269+
s = s.slice(0, -1);
270+
return s;
271+
}
272+
273+
// Usage
274+
275+
var payload = {
276+
from: "+15551234567",
277+
recipients: [
278+
{ to: "+15550112233" }
279+
],
280+
};
281+
282+
sendOptOutAdd(ConnectionString, payload);
283+
284+
```
285+
### Java
286+
287+
#### Prerequisites
288+
289+
- Java Development Kit (JDK) version 8 or above.
290+
- An active Communication Services resource and connection string. See [Create a Communication Services resource](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/create-communication-resource).
291+
- An SMS-enabled telephone number. See [Get a phone number](https://learn.microsoft.com/en-us/azure/communication-services/quickstarts/telephony/get-phone-number).
292+
293+
294+
```java
295+
// Sample for Add action. Replace with Check or Remove as necessary.
296+
public class App
297+
{
298+
public static void main(String[] args) throws Exception
299+
{
300+
String connectionString = "endpoint=https://[CONTOSO].communication.azure.com/;accesskey=******";
301+
302+
OptOutRequest payload = new OptOutRequest();
303+
payload.from = "+15551234567";
304+
payload.recipients = new ArrayList<Recipient>();
305+
payload.recipients.add(new Recipient("+15550112233"));
306+
307+
SendOptOut(connectionString, payload);
308+
}
309+
310+
public static void SendOptOutAdd(String connectionString, OptOutRequest payload) throws Exception
311+
{
312+
String apiVersion = "2024-12-10-preview";
313+
314+
String[] arrOfStr = connectionString.split(";");
315+
String endpoint = arrOfStr[0].split("=")[1];
316+
String accessKey = arrOfStr[1].split("=")[1];
317+
String body = new ObjectMapper().enable(SerializationFeature.INDENT_OUTPUT).writeValueAsString(payload);
318+
String dateHeaderName = "x-ms-date";
319+
DateTimeFormatter headerDateFormat = DateTimeFormatter.ofPattern("EEE, dd MMM yyyy HH:mm:ss z", Locale.ENGLISH).withZone(ZoneId.of("GMT"));
320+
String dateHeader = headerDateFormat.format(Instant.now());
321+
String verb = "POST";
322+
URI uri = URI.create(endpoint + "sms/optouts:add?api-version==" + apiVersion);
323+
String hostName = uri.getHost();
324+
String pathAndQuery = uri.getPath() + "?" + uri.getQuery();
325+
String encodedHash = Base64.getEncoder().encodeToString(MessageDigest.getInstance("SHA-256").digest(body.getBytes(StandardCharsets.UTF_8)));
326+
String stringToSign = verb + '\n' + pathAndQuery + '\n' + dateHeader + ';' + hostName + ';' + encodedHash;
327+
Mac mac = Mac.getInstance("HmacSHA256");
328+
SecretKeySpec secretKeySpec = new SecretKeySpec(Base64.getDecoder().decode(accessKey.getBytes()), "HmacSHA256");
329+
mac.init(secretKeySpec);
330+
String signature = Base64.getEncoder().encodeToString(mac.doFinal(stringToSign.getBytes()));
331+
String authHeader = "HMAC-SHA256 SignedHeaders=" + dateHeaderName + ";host;x-ms-content-sha256&Signature=" + signature;
332+
333+
HttpClient client = HttpClients.custom().build();
334+
HttpUriRequest request = RequestBuilder
335+
.post(uri)
336+
.setHeader(HttpHeaders.CONTENT_TYPE, "application/json")
337+
.setHeader(dateHeaderName, dateHeader)
338+
.setHeader("x-ms-content-sha256", encodedHash)
339+
.setHeader("Authorization", authHeader)
340+
.setEntity(new StringEntity(body, "UTF-8"))
341+
.build();
342+
HttpResponse r = client.execute(request);
343+
HttpEntity entity = r.getEntity();
344+
}
345+
}
346+
347+
public class OptOutRequest
348+
{
349+
public String from;
350+
public ArrayList<Recipient> recipients;
351+
}
352+
353+
public class Recipient
354+
{
355+
public String to;
356+
}
357+
```

0 commit comments

Comments
 (0)