Skip to main content
The Azure Content Safety policy sends the request text to the Analyze Text operation of Azure AI Content Safety and applies the verdict at the gateway. Azure scores four harm categories — Hate, Violence, Self-harm and Sexual content — and the policy refuses the request when any category you enforce reaches the severity you set for it. It blocks, or it allows. It cannot mask, redact or rewrite anything. And it never sees the model’s answer: this policy has no response leg at all. Everything below is about what goes in.
The endpoint is the value that will go wrong. The gateway POSTs the URL you configure exactly as you typed it and appends nothing — no operation path, no api-version. The endpoint the Azure portal shows on Keys and Endpoint is the resource root, not an operation, so it is accepted when you save the policy and then fails on every single request.
You will not see Azure’s 404. Any non-2xx from Azure becomes one generic gateway error, so the symptom is every request failing with nothing naming the cause.
2024-09-01 is the version to use today, and it will not be the version forever. Microsoft deprecated every other Analyze Text version on 1 March 2025, leaving it the only generally available one. Their own policy then deprecates a GA version 90 days after its successor ships, so check Azure’s Analyze Text reference before you paste this in — a version Azure has retired fails the same way a wrong path does, with one generic gateway error.
This policy puts a synchronous call in front of every request. What that costs depends on your region, your resource tier and the size of the conversation, so measure it in your own environment rather than assuming.

Part 1 — Set up Azure AI Content Safety

Everything in this part happens in your own Azure subscription. TrustGate never creates or changes anything there. This is the lightest of the three provider setups, because of what is not in it:
None of the filtering is configured on the Azure side. There is no object to build, no categories to enable, no thresholds to set and no list to maintain. Azure scores the text; everything that decides what is blocked lives on the TrustGate policy in Part 3.If you have come from the Bedrock or Model Armor pages, this is the difference. You create a resource and copy two values out of it.
1

Find Content safety in the portal

It is not a standalone service any more. It sits under Microsoft Foundry, reached from More services → Content safety in the left-hand navigation.An empty list here shows a Welcome to Azure! panel offering a free trial, which looks like it is telling you that you have no subscription. It is not — it is telling you that you have no Content Safety resources yet. Use Create.
2

Fill in the Basics tab

The region field arrives pre-filled with Azure’s default, and it decides where your text is analysed. Change it deliberately rather than leaving it as you found it: every request makes that round trip, and the text is processed in whichever region ends up here. For anything with a data-residency constraint this is the field that matters most on the page, and it is the one most easily left alone.Content Safety is not offered in every Azure region, so check that the one you need is in the list before you plan around it.
Azure may note that the subscription has not registered the Cognitive Services resource type before, and offer to register it for you. Let it. On a subscription with restrictive policies that registration can fail, and the error it produces later is not obviously about registration.The Network, Identity and Tags tabs can be left as they are. Note that Identity switches on a system-assigned managed identity by default — this policy authenticates with a key and never uses it.
3

Create it, then open the resource

Review + create, then Create. Deployment takes a minute or two, and the resource’s Status reads Active when it is ready.The resource group’s own location does not have to match the resource’s, and often will not. What matters for this policy is the resource’s.
4

Copy the key and build the endpoint

Keys and Endpoint, under Resource Management in the resource’s left-hand navigation. The resource issues two keys so you can rotate one while the other is live; either works.The endpoint is also shown on the resource’s Overview, and this is where the one real trap on this page lives.
The endpoint the portal gives you is not the endpoint the policy needs. What both pages show is the resource root:
The policy needs the Analyze Text operation, with its api-version:
Paste the first and the policy saves without complaint, then fails on every request — and you will not see Azure’s 404, because any non-2xx becomes one generic gateway error. Build the second by hand.

The values the policy will ask you for

Nothing else about the resource is read by this policy. There is no blocklist, no custom term list and no per-category configuration on the Azure side.

Part 2 — Give the gateway a key

The policy authenticates with the resource’s subscription key. Copy it from the resource and paste it into the policy. The key is never returned to the browser. Reopen the policy and the field shows only that a key is set, not the key itself: you replace it rather than read it, and the Azure portal is the only place you can read one. Because the resource issues two keys, you can rotate by pasting the second one into the policy and then regenerating the first.

Part 3 — Configure the policy in TrustGate

Open Policies → Library → Azure Content Safety.

Azure connection

The console checks that the endpoint is an absolute http(s) URL, and nothing more. Whether that URL is an operation Azure will answer is between you and Azure.

Category thresholds

Severity scale — 4 levels - 0,2,4,6 (the default) or 8 levels - 0-7. This is sent to Azure with every call and decides the severities it returns. It also decides which thresholds you may pick below: 2, 4 or 6 on the four-level scale, 1 to 7 on the eight-level one. The console offers only the legal values, and a threshold that becomes illegal when you switch scale is snapped to the nearest one that is — so re-read the thresholds after changing the scale. Then one row per category: Hate, Violence, Self-harm, Sexual content. The toggle decides whether the category is sent to Azure at all; Block at is the severity at which it blocks. A category breaches when the severity Azure returns is greater than or equal to its Block at value.
A policy saved before this rule existed can have a threshold on a category whose toggle is off — and Azure still scores that category. The gateway requests every category with a Block at value alongside the ones you toggled on, so an older policy with this gap keeps working exactly as before. Saving a policy with that gap today is rejected, and the console cannot produce it: toggling a category off while leaving its threshold set is no longer a state you can create from here.
A new policy starts with all four categories enabled at Block at 4. At least one category must stay enabled or the policy will not save. A category you turn off is not sent to Azure and is never scored, so there is no “score it but do not block on it” for a single category. Recording without blocking is what Observe does, and it applies to the whole policy.

When content is blocked

There is nothing to configure. The 403 body always says This content violates our usage policy.

Stage and mode

This policy runs on the request leg, and that is not a choice. Screening the request on its way to the model is the only thing it can do, so the form draws no selector for it — there is nothing to select between. Start in Observe, which records every decision without refusing anything, read the severities Azure actually returns for your traffic, and only then set the thresholds and switch to Enforce.

How the decision is made

1

The whole conversation becomes one string

The system prompt, if present, and the text of every message in the request are joined with newlines and sent as a single value. Roles are not sent. Tool definitions, tool calls and tool results are not sent. Attachments are not sent.Sending the whole conversation is what lets this policy catch something assembled across several turns. It is also what makes it collide with Azure’s length limit — see Limits.
2

Azure scores the enabled categories

One Analyze Text call per request, carrying the categories you enabled and the severity scale you chose. A request whose text is empty after joining is forwarded without calling Azure at all.
3

Any category at or above its threshold refuses the request

The request never reaches the model. The caller gets 403, and the response names every category that breached, with the severity Azure returned and the threshold it crossed. The wording is This content violates our usage policy.Those category names are Azure’s own — Hate, Violence, SelfHarm and Sexual — rather than the labels this form shows, so a colleague reporting a refusal may name a category you do not recognise from the console.

Modes and failures

The decision recorded on the event is allowed, block, reported, failed_open or failed_closed (the content caused the failure and Enforce refused it). failed_open is the one to look for first on a new policy: it is what a wrong endpoint, a wrong key or an unreachable resource records on every request, in Enforce and Observe alike, and it is by far the most common first-run outcome. reported is what Observe records in place of a block. The event’s failure_reason field says why: transport (the call failed, timed out, or Azure answered non-2xx), verdict_incomplete (a thresholded category was missing from Azure’s response), config_invalid (the stored settings could not be used), or decode_failed (the gateway’s own body could not be decoded before Azure was ever called).
What happens when Azure cannot evaluate depends on the cause.
  • Azure is unavailable, or the policy is misconfigured: fail open. A wrong key, a wrong endpoint, a throttled resource, a timeout or a server error lets the request through unchecked, with nothing in the response to say so. Only the event and the trace record it, as failed_open with a failure_reason. Validate in Observe, and treat a run of failed_open as the policy not guarding anything.
  • The content caused it: blocked in Enforce. A conversation past Azure’s length limit, or a body the gateway cannot decode, is refused with 403 and recorded as failed_closed.
  • Observe only records either case and never blocks.

Limits

  • Request leg only. There is no response leg, so this policy cannot see, score or block what the model says — not for streaming responses, not for any response. This is a larger limit than anything you can configure on it. If you need output screening, pair it with a guardrail that has a response leg.
  • It blocks; it cannot mask. No anonymisation and no rewriting. Sensitive content is refused or forwarded, never redacted.
  • Four categories, and nothing else. No blocklists, no custom term lists, no jailbreak or prompt-shield detection, no protected-material detection.
  • Azure’s length limit applies to the whole conversation. Azure documents a 10,000-character limit on Analyze Text. The policy screens the system prompt and every message, counts the joined conversation in characters before calling Azure and never truncates it, so a conversation over the limit is refused in Enforce and only recorded in Observe. Because a conversation only grows, in Enforce that turn and every turn after it is refused. Treat this policy as suited to short exchanges until you have measured your own.
  • Subscription key only. No Microsoft Entra ID, no managed identity.
  • Text only. Files, images and audio are not sent to Azure. A request on a route that carries no chat, such as image generation, audio or files, is not inspected: it is forwarded and recorded as not inspected, never refused.
  • LLM traffic only. The policy does not apply to MCP (Model Context Protocol) tool calls.
Microsoft documents the Analyze Text limit as a default maximum of 10,000 characters, and tells callers to split longer text themselves. Being a default, it may be raised on your resource — but nothing in this policy splits anything, so until you have confirmed otherwise on your own resource, treat 10,000 as the ceiling this policy works within.

Verify the policy

Open the Playground, pick the application the policy applies to, and send these prompts in this order. Starting with the control means a failure tells you something specific.
1

Control — the path works at all

Expect a normal reply and the decision allowed.This step is also the endpoint test. A harmless prompt recorded as failed_open means the gateway could not get an answer out of Azure: wrong endpoint, wrong key, or the resource unreachable. The reply looks normal, so read the decision on the event rather than the reply. Fix that before going on — nothing below will work until this does.
2

Observe — read the severities before you trust the thresholds

Switch the policy to Observe and send content you consider borderline for your application. Nothing is refused, and the decision recorded is reported when a category breached and allowed when none did.This is how you find out what severities Azure returns for your traffic rather than guessing. Set Block at from what you see here.
3

Enforce — blocking works

Back in Enforce, send something that should breach a category you enabled.Expect 403 and the decision block. To prove the request never reached the model, open Activity, open the request, and read its Policies tab: the Aggregated Latency breakdown there has a Provider row, and that row is empty. The Playground shows a single latency figure and does not break it down. Read the categories the refusal names to confirm it fired on the one you expected.
Every decision is emitted as a metadata event, in Observe mode too, where nothing is blocked. See the event schema for the fields recorded.

Troubleshooting