POST /signage/devices/{deviceId}/send-broadcast.POST /signage/devices/{deviceId}/send-broadcast supports text, image, youtube, and canva.POST /signage/contents supports a broader content library: image, audio, video, youtube, canva, rss, and webUrl.image, youtube, or canva.image, audio, and video.
Requests a SAS upload URL for file-based content.
This endpoint is required before creating image, audio, or video content.
Method: GET
Path: /signage/contents/upload
Auth: x-api-key header
Query parameters
fileName (required, string): original filename such as image.png or video.mp4; OpenAPI pattern is (?i).*\\.(jpg|png|mp3|mp4)$.fileSize (required, int64): file size in bytes and must be greater than 0.
Success response: 200
{
"guidFileName": "a1b2c3d4_image.png",
"uploadUrl": "https://blob.core.windows.net/...?sv=...&sig=...",
"verifyKey": "abc123"
}
Response fields
guidFileName: use this exact value in POST /signage/contentsuploadUrl: Azure Blob SAS URL, valid for 60 minutesverifyKey: required later when creating file-based content
Error responses
401: unauthorized403: storage quota exceeded
This is not an OMS endpoint.
It is the Azure Blob Storage upload step returned by GET /signage/contents/upload.
Recommended request
PUTuploadUrlx-ms-blob-type: BlockBlob and Content-Type set to the real MIME type such as image/png
Important
POST /signage/contents afterward.
Creates a content record in the signage content library.
Method: POST
Path: /signage/contents
Auth: x-api-key header
Body: application/json
Always required
displayNamecontentType
Required for file-based content
guidFileNamefileSizeverifyKey
Required for URL-based content
sourceUrl
Supported contentType values
imageaudiovideoyoutubecanvarsswebUrl
Example: create image content
{
"displayName": "Lobby Banner",
"contentType": "image",
"guidFileName": "a1b2c3d4_image.png",
"fileSize": 204800,
"verifyKey": "abc123"
}
Example: create YouTube content
{
"displayName": "Welcome Video",
"contentType": "youtube",
"sourceUrl": "https://youtube.com/watch?v=dQw4w9WgXcQ"
}
Example: create Canva content
{
"displayName": "Promo Deck",
"contentType": "canva",
"sourceUrl": "https://www.canva.com/design/your-design-id/view"
}
Success response
204: content created successfully
Error responses
400: invalid input or verification failed401: unauthorized403: forbidden
Returns a paginated content list.
Method: GET
Path: /signage/contents
Auth: x-api-key header
Query parameters
filter (optional, string): supported values in the endpoint description are image, video, audio, youtube, and canva.num (optional, int32): page number, minimum 1, default 1.size (optional, int32): page size, range 1 to 100, default 10.
Success response: 200
{
"pageInfo": {
"num": 1,
"size": 10,
"items": 25,
"pages": 3
},
"data": [
{
"contentId": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
"displayName": "Lobby Banner",
"contentType": "image",
"contentUrl": "https://blob.core.windows.net/...?sv=..."
}
]
}
Response fields
pageInfo.num: current page numberpageInfo.size: requested page sizepageInfo.items: total itemspageInfo.pages: total pagesdata[].contentId: unique content identifierdata[].displayName: display namedata[].contentType: content typedata[].contentUrl: SAS URL for file-based content, or the actual source URL for URL-based content.
Error responses
400: invalid request parameters401: unauthorized403: forbidden
Sends a broadcast to the specified device.
Method: POST
Path: /signage/devices/{deviceId}/send-broadcast
Auth: x-api-key header
Body: application/json
Path parameter
deviceId (required, uuid)
Request body fields
isText (boolean): enables text broadcast.textMessage (string): required when isText is true.textPosition (string): supported values are top, middle, bottom; default is top.textColor (string): hex color pattern #RRGGBB, for example #FFFFFF; default is #FFFFFF.textBgColor (string): hex color pattern #RRGGBB, for example #000000; default is #000000.textSize (string): supported values are xs, s, m, l, xl; default is xs.isScrollingText (boolean): default is false.isMedia (boolean): enables media broadcast; supported broadcast media content types are image, youtube, and canva; default is false.contentId (string): required when isMedia is true.mediaSize (string): supported values are original and full; default is original; applies only to image.mediaPosition (string): supported values are center, lefttop, righttop, leftbottom, and rightbottom; default is center; applies only when the content type is image and mediaSize is original.displayTime (int32): minimum 0, maximum 86400, default 30.isForcePlay (boolean): default is false.
Validation checklist
isText is true, provide textMessage.isMedia is true, provide a valid contentId.image, youtube, or canva.textColor and textBgColor.displayTime within 0 to 86400.mediaPosition for image content with mediaSize: "original".
Example: text-only broadcast
{
"isText": true,
"textMessage": "Emergency notice from OMS API",
"textPosition": "top",
"textColor": "#FFFFFF",
"textBgColor": "#FF0000",
"textSize": "m",
"isScrollingText": true,
"displayTime": 60
}
Example: text + image broadcast
{
"isText": true,
"textMessage": "Welcome to the lobby",
"textPosition": "top",
"textColor": "#FFFFFF",
"textBgColor": "#000000",
"textSize": "s",
"isScrollingText": false,
"isMedia": true,
"contentId": "your-image-content-id",
"mediaSize": "original",
"mediaPosition": "center",
"displayTime": 30,
"isForcePlay": false
}
Example: YouTube broadcast
{
"isText": true,
"textMessage": "Now playing on lobby signage",
"textPosition": "bottom",
"textColor": "#FFFFFF",
"textBgColor": "#000000",
"textSize": "xs",
"isMedia": true,
"contentId": "your-youtube-content-id",
"displayTime": 120,
"isForcePlay": false
}
Success response
204: broadcast command accepted and dispatched
Error responses
400: invalid request body401: unauthorized403: forbidden404: device not found or content not found409: device is currently performing another operation
Stops the current broadcast on the device.
Method: POST
Path: /signage/devices/{deviceId}/stop-broadcast
Auth: x-api-key header
Path parameter
deviceId (required, uuid)
Success response
204: stop command accepted and dispatched
Error responses
401: unauthorized403: forbidden404: device not found
Deletes a content record and removes associated blob files when applicable.
Method: DELETE
Path: /signage/contents/{contentId}
Auth: x-api-key header
Path parameter
contentId (required, uuid)
Success response
204: content deleted successfully
Error responses
400: invalid request401: unauthorized403: forbidden404: content not found// Device Broadcast sample
// Demonstrates:
// 1) text-only broadcast
// 2) URL-based content creation (YouTube)
// 3) send media broadcast
// 4) stop broadcast
// 5) optional cleanup
// 6) optional file upload flow for image content
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
class DeviceBroadcastSamples
{
static async Task Main()
{
var baseUrl = "OMS_API_BASE_URL";
var deviceId = "YOUR_DEVICE_ID";
var apiKey = "YOUR_API_KEY"; // Or leave empty and use subscriptionKey
var subscriptionKey = "";
using var client = new HttpClient();
void AddAuth(HttpRequestMessage req)
{
if (!string.IsNullOrEmpty(apiKey))
{
req.Headers.Add("x-api-key", apiKey);
}
else if (!string.IsNullOrEmpty(subscriptionKey))
{
var uri = req.RequestUri;
var builder = new UriBuilder(uri);
if (string.IsNullOrEmpty(builder.Query))
{
builder.Query = "subscription-key=" + Uri.EscapeDataString(subscriptionKey);
}
else
{
builder.Query = builder.Query.TrimStart('?') + "&subscription-key=" + Uri.EscapeDataString(subscriptionKey);
}
req.RequestUri = builder.Uri;
}
}
async Task<string> SendOmsAsync(HttpMethod method, string relativeUrl, object payload = null)
{
var req = new HttpRequestMessage(method, $"{baseUrl}{relativeUrl}");
AddAuth(req);
if (payload != null)
{
var json = JsonSerializer.Serialize(payload);
req.Content = new StringContent(json, Encoding.UTF8, "application/json");
}
else if (method == HttpMethod.Get)
{
req.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
}
var resp = await client.SendAsync(req);
var body = resp.Content == null ? "" : await resp.Content.ReadAsStringAsync();
Console.WriteLine($"{method} {relativeUrl}");
Console.WriteLine($"-> {(int)resp.StatusCode} {resp.ReasonPhrase}");
if (!string.IsNullOrWhiteSpace(body))
{
Console.WriteLine(body);
}
Console.WriteLine();
if (!resp.IsSuccessStatusCode)
{
throw new Exception($"OMS call failed: {(int)resp.StatusCode} {resp.ReasonPhrase}");
}
return body;
}
async Task<string> FindContentIdByDisplayNameAsync(string displayName, string filter = null)
{
var url = "/signage/contents?num=1&size=100";
if (!string.IsNullOrEmpty(filter))
{
url += "&filter=" + Uri.EscapeDataString(filter);
}
var body = await SendOmsAsync(HttpMethod.Get, url);
var json = JsonDocument.Parse(body);
var items = json.RootElement.GetProperty("data");
foreach (var item in items.EnumerateArray())
{
var name = item.GetProperty("displayName").GetString();
if (string.Equals(name, displayName, StringComparison.Ordinal))
{
return item.GetProperty("contentId").GetString();
}
}
throw new Exception($"Content not found by displayName: {displayName}");
}
async Task<string> CreateYoutubeContentAsync(string displayName, string sourceUrl)
{
await SendOmsAsync(HttpMethod.Post, "/signage/contents", new
{
displayName,
contentType = "youtube",
sourceUrl
});
return await FindContentIdByDisplayNameAsync(displayName, "youtube");
}
async Task<string> UploadImageAndCreateContentAsync(string localFilePath, string displayName)
{
var fileInfo = new FileInfo(localFilePath);
if (!fileInfo.Exists)
{
throw new FileNotFoundException("Image file not found.", localFilePath);
}
var uploadBody = await SendOmsAsync(
HttpMethod.Get,
$"/signage/contents/upload?fileName={Uri.EscapeDataString(fileInfo.Name)}&fileSize={fileInfo.Length}"
);
var uploadJson = JsonDocument.Parse(uploadBody).RootElement;
var guidFileName = uploadJson.GetProperty("guidFileName").GetString();
var uploadUrl = uploadJson.GetProperty("uploadUrl").GetString();
var verifyKey = uploadJson.GetProperty("verifyKey").GetString();
using (var fileStream = File.OpenRead(localFilePath))
{
var putReq = new HttpRequestMessage(HttpMethod.Put, uploadUrl)
{
Content = new StreamContent(fileStream)
};
putReq.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
putReq.Headers.Add("x-ms-blob-type", "BlockBlob");
var putResp = await client.SendAsync(putReq);
Console.WriteLine($"PUT Azure Blob -> {(int)putResp.StatusCode} {putResp.ReasonPhrase}");
Console.WriteLine();
if (putResp.StatusCode != HttpStatusCode.Created && !putResp.IsSuccessStatusCode)
{
throw new Exception("Blob upload failed.");
}
}
await SendOmsAsync(HttpMethod.Post, "/signage/contents", new
{
displayName,
contentType = "image",
guidFileName,
fileSize = fileInfo.Length,
verifyKey
});
return await FindContentIdByDisplayNameAsync(displayName, "image");
}
// 1) Text-only broadcast
await SendOmsAsync(HttpMethod.Post, $"/signage/devices/{deviceId}/send-broadcast", new
{
isText = true,
textMessage = "Emergency notice from OMS API",
textPosition = "top",
textColor = "#FFFFFF",
textBgColor = "#FF0000",
textSize = "m",
isScrollingText = true,
displayTime = 60
});
// 2) URL-based content flow (YouTube) -> create content -> get contentId
var youtubeDisplayName = "OMS Demo Youtube " + DateTime.UtcNow.ToString("yyyyMMddHHmmss");
var youtubeContentId = await CreateYoutubeContentAsync(
youtubeDisplayName,
"https://youtube.com/watch?v=dQw4w9WgXcQ"
);
// 3) Send YouTube broadcast
await SendOmsAsync(HttpMethod.Post, $"/signage/devices/{deviceId}/send-broadcast", new
{
isText = true,
textMessage = "Now playing",
textPosition = "bottom",
textColor = "#FFFFFF",
textBgColor = "#000000",
textSize = "xs",
isMedia = true,
contentId = youtubeContentId,
displayTime = 120,
isForcePlay = false
});
// 4) Stop current broadcast
await SendOmsAsync(HttpMethod.Post, $"/signage/devices/{deviceId}/stop-broadcast");
// 5) Optional cleanup for YouTube content
await SendOmsAsync(HttpMethod.Delete, $"/signage/contents/{youtubeContentId}");
// 6) Optional file-based image flow
// Uncomment when you have a real local image file.
//
// var imageContentId = await UploadImageAndCreateContentAsync("image.png", "OMS Demo Banner");
// await SendOmsAsync(HttpMethod.Post, $"/signage/devices/{deviceId}/send-broadcast", new
// {
// isText = true,
// textMessage = "Welcome to the lobby",
// textPosition = "top",
// textColor = "#FFFFFF",
// textBgColor = "#000000",
// textSize = "s",
// isMedia = true,
// contentId = imageContentId,
// mediaSize = "original",
// mediaPosition = "center",
// displayTime = 30,
// isForcePlay = false
// });
// await SendOmsAsync(HttpMethod.Delete, $"/signage/contents/{imageContentId}");
Console.WriteLine("Done");
}
}