Groundedness Guard Policy
General Information
Policy Types
ai-groundedness-guard
Response-phase only, non-streaming responses only. Always judge-based — there is no cheap deterministic check for "is this text supported by that text", so a judge configuration is required.
Endpoints
List Policies
GET /apiops/projects/{projectName}/apiProxies/{apiProxyName}/policies/
Add Policy
POST /apiops/projects/{projectName}/apiProxies/{apiProxyName}/policies/{policyName}/
Update Policy
PUT /apiops/projects/{projectName}/apiProxies/{apiProxyName}/policies/{policyName}/
Delete Policy
DELETE /apiops/projects/{projectName}/apiProxies/{apiProxyName}/policies/{policyName}/
Scope
This policy only checks the response against the RAG context your own Apinizer proxy retrieved and injected in the same request/response cycle — it is not a general "is this fact true" check against outside knowledge, and it produces no per-claim citations. It only detects the retrieved context when the RAG Injection policy on the same proxy uses injectionMode=TEMPLATE (the default template, or a custom one that still contains the <CONTEXT>...</CONTEXT> markers). When no RAG context is detectable — including a request the RAG Injection policy did not touch at all — this policy is a silent no-op, not an error.
Fields
| Field | Type | Required | Description |
|---|---|---|---|
| action | string | No | FLAG (monitoring-only, default) or BLOCK. |
| failMode | string | No | FAIL_OPEN or FAIL_CLOSED — behaviour when the judge call itself errors or times out. Default FAIL_CLOSED: the response is blocked rather than let an unverified answer through. |
| latencyMode | string | No | INLINE, ASYNC or SHADOW. Default INLINE — this is a response-blocking security check, so it does not default to fire-and-forget the way some other AI guardrails do. |
| maxWaitMs | integer | No | Bounded max wait (ms) for ASYNC/INLINE. Default 3000. |
| externalGuardrail | object | Yes | The judge configuration. See Judge fields. Always required — this policy has no builtin, non-judge leg. |
| maxContextChars | integer | No | Truncation cap (chars) for the retrieved context before it is composed into the judge's input. Default 6000. |
| maxResponseChars | integer | No | Truncation cap (chars) for the response text before it is composed into the judge's input. Default 4000. |
Judge fields
externalGuardrail is always type: "llm-judge" for this policy — there is no http-dlp option, unlike the DLP-family guardrails.
| Field | Type | Required | Description |
|---|---|---|---|
| type | string | Yes | Must be llm-judge. |
| llmProviderName | string | Yes | Name of the LLM connection to call as the judge. Resolved server-side; an unknown name is rejected with 400. Apinizer looks for the name in the project of the API proxy first, then in the admin project (shared providers). A provider in the own project wins when both have the same name. |
| modelId | string | No | Judge model identifier. |
| promptTemplate | string | Conditional | The judge's prompt template. Required for a chat judge. Unlike the Prompt Guard policy, a blank template is rejected, not defaulted — a blank template falls back to a generic safety-taxonomy judge that does not answer a groundedness question at all (error key prompttemplaterequired). Not required when decisionQuestion (TypeSafe Jev) or unsafeLabels (TEI) is set. |
| jsonMode | boolean | No | Whether the judge is asked to respond in JSON mode. |
| maxOutputTokens | integer | No | Judge max output tokens. |
| timeoutMs | integer | No | HTTP call timeout (ms). |
| maxInputChars | integer | No | Max input characters sent to the judge. |
| decisionMode | string | No | STANDARD or REASONING. Unset is STANDARD. llm-judge only, chat judge. STANDARD: temperature 0, small output limit, default timeoutMs 2000, default maxOutputTokens 64. REASONING: for thinking models; no temperature is sent, maxOutputTokens is the budget for thinking and verdict together, default timeoutMs 15000 (up to 60000), default maxOutputTokens 1024. A value that you set always wins over the default |
| decisionThreshold | number | No | Verdict threshold for TypeSafe Jev and TEI judges. Range 0.05 to 0.99. Default 0.5. A value outside the range is set to the nearest limit. Jev: unsafe when the probability is at or above the value. TEI: unsafe when a selected label scores at or above the value |
| unsafeLabels | array[string] | Conditional | TEI judge only. Labels that mean unsafe, for example INJECTION and JAILBREAK. Matching is not case-sensitive. Required for a TEI judge: at least one label (error key unsafelabelsrequired). |
| decisionQuestion | string | Conditional | TypeSafe Jev judge only. The yes/no question that Jev answers about the text. yes means unsafe. Required for a TypeSafe Jev judge on this policy (error key decisionquestionrequired). Default question in the Manager UI: Is the answer not supported by the given context? Answer yes when it states facts, numbers or names that the context does not contain. |
Judge providers. llmProviderName can name an OpenAI-compatible chat provider, an Anthropic provider, a TypeSafe Jev provider (decision model), or a TEI provider (guard classifier). AWS Bedrock, Google Vertex AI, Cohere, the native Gemini wire format, embedding-only providers, and custom providers without an adapter family cannot judge. The provider must use credentials held by the AI gateway (Provided by AI gateway). TypeSafe Jev and TEI make decisions only: they do not use promptTemplate, jsonMode, or maxOutputTokens, and they cannot be a routing target, a pool member, a failover target, or an embedding provider.
If the judge provider refuses to answer for safety reasons, the verdict is UNSAFE with the category provider-refusal. FAIL_OPEN does not let this request through. maxWaitMs still limits the whole check, so a slow reasoning judge is cut off by it and failMode decides.
List Policies
Endpoint
GET /apiops/projects/{projectName}/apiProxies/{apiProxyName}/policies/
Request
Headers
| Header | Value |
|---|---|
| Authorization | Bearer {token} |
| Content-Type | application/json |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| projectName | string | Yes | Project name |
| apiProxyName | string | Yes | API Proxy name |
Response
Success Response (200 OK)
{
"status": "SUCCESS",
"resultList": [
{
"apiProxy": {
"name": "MyAiProxy",
"requestPolicyList": [],
"responsePolicyList": [
{
"type": "ai-groundedness-guard",
"name": "kb-groundedness-check",
"description": "Flags support-chat answers not grounded in the retrieved KB context",
"active": true,
"action": "FLAG",
"failMode": "FAIL_CLOSED",
"latencyMode": "INLINE",
"maxWaitMs": 3000,
"maxContextChars": 6000,
"maxResponseChars": 4000,
"externalGuardrail": {
"type": "llm-judge",
"llmProviderName": "openai-judge",
"modelId": "gpt-4o-mini",
"promptTemplate": "Task: Determine whether the ASSISTANT RESPONSE is fully grounded in the RETRIEVED CONTEXT...\n\n<<<PROMPT>>>",
"jsonMode": false,
"maxOutputTokens": 200,
"timeoutMs": 8000,
"maxInputChars": 12000
}
}
],
"errorPolicyList": []
}
}
],
"resultCount": 1
}
The judge's LLM connection carries its own credentials in the LLM provider connection config, not in this policy — there is no secret field on ai-groundedness-guard itself.
cURL Example
curl -X GET \
"https://demo.apinizer.com/apiops/projects/MyProject/apiProxies/MyAiProxy/policies/" \
-H "Authorization: Bearer YOUR_TOKEN"
Add Policy
Endpoint
POST /apiops/projects/{projectName}/apiProxies/{apiProxyName}/policies/{policyName}/
Request
Headers
| Header | Value |
|---|---|
| Authorization | Bearer {token} |
| Content-Type | application/json |
Path Parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
| projectName | string | Yes | Project name |
| apiProxyName | string | Yes | API Proxy name |
| policyName | string | Yes | Policy name |
Request Body
Full JSON Body Example
{
"operationMetadata": {
"targetScope": "ALL",
"targetPipeline": "RESPONSE",
"deploy": true,
"deployTargetEnvironmentNameList": ["production"],
"order": 1
},
"policy": {
"type": "ai-groundedness-guard",
"description": "Flags support-chat answers not grounded in the retrieved KB context",
"active": true,
"action": "FLAG",
"failMode": "FAIL_CLOSED",
"latencyMode": "INLINE",
"maxWaitMs": 3000,
"maxContextChars": 6000,
"maxResponseChars": 4000,
"externalGuardrail": {
"type": "llm-judge",
"llmProviderName": "openai-judge",
"modelId": "gpt-4o-mini",
"promptTemplate": "Task: Determine whether the ASSISTANT RESPONSE is fully grounded in the RETRIEVED CONTEXT...\n\n<<<PROMPT>>>",
"jsonMode": false,
"maxOutputTokens": 200,
"timeoutMs": 8000,
"maxInputChars": 12000
}
}
}
Example — TypeSafe Jev Judge
With a decision-only judge, promptTemplate is not needed. decisionQuestion is required:
"externalGuardrail": {
"type": "llm-judge",
"llmProviderName": "typesafe-jev",
"modelId": "jev-latest",
"decisionQuestion": "Is the answer not supported by the given context? Answer yes when it states facts, numbers or names that the context does not contain.",
"decisionThreshold": 0.5
}
Body Fields
See Fields and Judge fields above.
Response
Success Response (200 OK)
{
"status": "SUCCESS",
"deploymentResult": {
"success": true,
"deploymentResults": [
{
"environmentName": "production",
"success": true,
"message": "Deployment successful"
}
]
}
}
Error Responses
| Code | Reason |
|---|---|
| 400 | Blank name; maxWaitMs/maxContextChars/maxResponseChars not positive; externalGuardrail missing; externalGuardrail.type blank; externalGuardrail.llmProviderName blank or does not resolve to an existing LLM connection in the project; the judge instruction that matches the provider kind is missing: externalGuardrail.decisionQuestion for a TypeSafe Jev provider, externalGuardrail.unsafeLabels (at least one) for a TEI provider, externalGuardrail.promptTemplate for a chat provider (the error message names the missing field); externalGuardrail.timeoutMs/maxOutputTokens/maxInputChars not positive; proxy not found. |
| 401 | Missing or invalid token. |
| 403 | The token's account lacks permission on the project. |
cURL Example
curl -X POST \
"https://demo.apinizer.com/apiops/projects/MyProject/apiProxies/MyAiProxy/policies/kb-groundedness-check/" \
-H "Authorization: Bearer YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"operationMetadata": {
"targetScope": "ALL",
"targetPipeline": "RESPONSE",
"deploy": true,
"deployTargetEnvironmentNameList": ["production"],
"order": 1
},
"policy": {
"type": "ai-groundedness-guard",
"description": "Flags ungrounded support-chat answers",
"active": true,
"action": "FLAG",
"externalGuardrail": {
"type": "llm-judge",
"llmProviderName": "openai-judge",
"promptTemplate": "Task: Determine whether the ASSISTANT RESPONSE is fully grounded in the RETRIEVED CONTEXT...\n\n<<<PROMPT>>>"
}
}
}'
Update Policy
Endpoint
PUT /apiops/projects/{projectName}/apiProxies/{apiProxyName}/policies/{policyName}/
Request
The body is the same shape as Add Policy. The update replaces the policy, so send the full externalGuardrail block, not just the field you are changing.
Full JSON Body Example — Switching to BLOCK
{
"operationMetadata": {
"targetScope": "ALL",
"targetPipeline": "RESPONSE",
"deploy": true,
"deployTargetEnvironmentNameList": ["production"],
"order": 1
},
"policy": {
"type": "ai-groundedness-guard",
"active": true,
"action": "BLOCK",
"failMode": "FAIL_CLOSED",
"externalGuardrail": {
"type": "llm-judge",
"llmProviderName": "openai-judge",
"modelId": "gpt-4o-mini",
"promptTemplate": "Task: Determine whether the ASSISTANT RESPONSE is fully grounded in the RETRIEVED CONTEXT...\n\n<<<PROMPT>>>",
"timeoutMs": 8000
}
}
}
Response
Success Response (200 OK)
{
"status": "SUCCESS",
"deploymentResult": {
"success": true,
"deploymentResults": [
{
"environmentName": "production",
"success": true,
"message": "Deployment successful"
}
]
}
}