Quick path by scenario

Important usage notes

1. GET /signage/contents/upload

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

Success response: 200

{
  "guidFileName": "a1b2c3d4_image.png",
  "uploadUrl": "https://blob.core.windows.net/...?sv=...&sig=...",
  "verifyKey": "abc123"
}

Response fields

Error responses

2. PUT {uploadUrl}

This is not an OMS endpoint.
It is the Azure Blob Storage upload step returned by GET /signage/contents/upload.

Recommended request

Important

3. POST /signage/contents

Creates a content record in the signage content library.

Method: POST
Path: /signage/contents
Auth: x-api-key header
Body: application/json

Always required

Required for file-based content

Required for URL-based content

Supported contentType values

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

Error responses

4. GET /signage/contents

Returns a paginated content list.

Method: GET
Path: /signage/contents
Auth: x-api-key header

Query parameters

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

Error responses

5. POST /signage/devices/{deviceId}/send-broadcast

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

Request body fields

Validation checklist

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

Error responses

6. POST /signage/devices/{deviceId}/stop-broadcast

Stops the current broadcast on the device.

Method: POST
Path: /signage/devices/{deviceId}/stop-broadcast
Auth: x-api-key header

Path parameter

Success response

Error responses

7. DELETE /signage/contents/{contentId}

Deletes a content record and removes associated blob files when applicable.

Method: DELETE
Path: /signage/contents/{contentId}
Auth: x-api-key header

Path parameter

Success response

Error responses

8. C# End-to-End Sample

// 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");
    }
}