curl --request POST \
--url https://api.promptguard.co/api/v1/guard \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"messages": [
{
"role": "<string>",
"content": ""
}
],
"direction": "input",
"model": "<string>",
"context": {
"framework": "<string>",
"chain_name": "<string>",
"agent_id": "<string>",
"session_id": "<string>",
"tool_calls": [
{}
],
"metadata": {}
},
"retrieved_context": [
{
"content": "<string>",
"source": "<string>",
"metadata": {}
}
],
"media": [
{
"type": "<string>",
"mime_type": "<string>",
"url": "<string>",
"base64": "<string>",
"metadata": {}
}
],
"device_findings": [
{
"count": 50000
}
]
}
'import requests
url = "https://api.promptguard.co/api/v1/guard"
payload = {
"messages": [
{
"role": "<string>",
"content": ""
}
],
"direction": "input",
"model": "<string>",
"context": {
"framework": "<string>",
"chain_name": "<string>",
"agent_id": "<string>",
"session_id": "<string>",
"tool_calls": [{}],
"metadata": {}
},
"retrieved_context": [
{
"content": "<string>",
"source": "<string>",
"metadata": {}
}
],
"media": [
{
"type": "<string>",
"mime_type": "<string>",
"url": "<string>",
"base64": "<string>",
"metadata": {}
}
],
"device_findings": [{ "count": 50000 }]
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
messages: [{role: '<string>', content: ''}],
direction: 'input',
model: '<string>',
context: {
framework: '<string>',
chain_name: '<string>',
agent_id: '<string>',
session_id: '<string>',
tool_calls: [{}],
metadata: {}
},
retrieved_context: [{content: '<string>', source: '<string>', metadata: {}}],
media: [
{
type: '<string>',
mime_type: '<string>',
url: '<string>',
base64: '<string>',
metadata: {}
}
],
device_findings: [{count: 50000}]
})
};
fetch('https://api.promptguard.co/api/v1/guard', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.promptguard.co/api/v1/guard",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'messages' => [
[
'role' => '<string>',
'content' => ''
]
],
'direction' => 'input',
'model' => '<string>',
'context' => [
'framework' => '<string>',
'chain_name' => '<string>',
'agent_id' => '<string>',
'session_id' => '<string>',
'tool_calls' => [
[
]
],
'metadata' => [
]
],
'retrieved_context' => [
[
'content' => '<string>',
'source' => '<string>',
'metadata' => [
]
]
],
'media' => [
[
'type' => '<string>',
'mime_type' => '<string>',
'url' => '<string>',
'base64' => '<string>',
'metadata' => [
]
]
],
'device_findings' => [
[
'count' => 50000
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.promptguard.co/api/v1/guard"
payload := strings.NewReader("{\n \"messages\": [\n {\n \"role\": \"<string>\",\n \"content\": \"\"\n }\n ],\n \"direction\": \"input\",\n \"model\": \"<string>\",\n \"context\": {\n \"framework\": \"<string>\",\n \"chain_name\": \"<string>\",\n \"agent_id\": \"<string>\",\n \"session_id\": \"<string>\",\n \"tool_calls\": [\n {}\n ],\n \"metadata\": {}\n },\n \"retrieved_context\": [\n {\n \"content\": \"<string>\",\n \"source\": \"<string>\",\n \"metadata\": {}\n }\n ],\n \"media\": [\n {\n \"type\": \"<string>\",\n \"mime_type\": \"<string>\",\n \"url\": \"<string>\",\n \"base64\": \"<string>\",\n \"metadata\": {}\n }\n ],\n \"device_findings\": [\n {\n \"count\": 50000\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.promptguard.co/api/v1/guard")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"messages\": [\n {\n \"role\": \"<string>\",\n \"content\": \"\"\n }\n ],\n \"direction\": \"input\",\n \"model\": \"<string>\",\n \"context\": {\n \"framework\": \"<string>\",\n \"chain_name\": \"<string>\",\n \"agent_id\": \"<string>\",\n \"session_id\": \"<string>\",\n \"tool_calls\": [\n {}\n ],\n \"metadata\": {}\n },\n \"retrieved_context\": [\n {\n \"content\": \"<string>\",\n \"source\": \"<string>\",\n \"metadata\": {}\n }\n ],\n \"media\": [\n {\n \"type\": \"<string>\",\n \"mime_type\": \"<string>\",\n \"url\": \"<string>\",\n \"base64\": \"<string>\",\n \"metadata\": {}\n }\n ],\n \"device_findings\": [\n {\n \"count\": 50000\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.promptguard.co/api/v1/guard")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"messages\": [\n {\n \"role\": \"<string>\",\n \"content\": \"\"\n }\n ],\n \"direction\": \"input\",\n \"model\": \"<string>\",\n \"context\": {\n \"framework\": \"<string>\",\n \"chain_name\": \"<string>\",\n \"agent_id\": \"<string>\",\n \"session_id\": \"<string>\",\n \"tool_calls\": [\n {}\n ],\n \"metadata\": {}\n },\n \"retrieved_context\": [\n {\n \"content\": \"<string>\",\n \"source\": \"<string>\",\n \"metadata\": {}\n }\n ],\n \"media\": [\n {\n \"type\": \"<string>\",\n \"mime_type\": \"<string>\",\n \"url\": \"<string>\",\n \"base64\": \"<string>\",\n \"metadata\": {}\n }\n ],\n \"device_findings\": [\n {\n \"count\": 50000\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"decision": "<string>",
"event_id": "<string>",
"confidence": 123,
"latency_ms": 123,
"weighted_score": 123,
"threat_type": "<string>",
"redacted_messages": [
{
"role": "<string>",
"content": ""
}
],
"threats": [
{
"type": "<string>",
"confidence": 123,
"details": "<string>",
"weighted_score": 123
}
],
"unscanned": [
{
"index": 123,
"reason": "<string>",
"detail": "<string>"
}
],
"unavailable": [
{
"detector": "<string>",
"reason": "<string>"
}
],
"entitlement": {
"plan": "<string>",
"seats": 123,
"scans_used": 123,
"scan_allowance": 123,
"mode": "<string>",
"as_of": "2023-11-07T05:31:56Z"
}
}{
"error": {
"code": "missing_api_key",
"message": "PromptGuard API key required. Please provide via X-API-Key header.",
"type": "authentication_error"
}
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}{
"error": {
"message": "<string>",
"type": "<string>",
"code": "<string>",
"current_plan": "<string>",
"requests_used": 123,
"requests_limit": 123,
"upgrade_url": "<string>",
"retry_after": 123
}
}Guard Content
Scan messages for security threats without proxying to an LLM.
This is the primary endpoint for auto-instrumentation and framework callback integrations. It runs the same policy engine, ML ensemble, preset configuration, custom rules, and entitlements checks as the proxy pipeline.
Use direction="input" before sending messages to the LLM and
direction="output" after receiving a response.
Returns a decision of allow, block, or redact along with
detailed threat information and optional redacted messages.
curl --request POST \
--url https://api.promptguard.co/api/v1/guard \
--header 'Content-Type: application/json' \
--header 'X-API-Key: <api-key>' \
--data '
{
"messages": [
{
"role": "<string>",
"content": ""
}
],
"direction": "input",
"model": "<string>",
"context": {
"framework": "<string>",
"chain_name": "<string>",
"agent_id": "<string>",
"session_id": "<string>",
"tool_calls": [
{}
],
"metadata": {}
},
"retrieved_context": [
{
"content": "<string>",
"source": "<string>",
"metadata": {}
}
],
"media": [
{
"type": "<string>",
"mime_type": "<string>",
"url": "<string>",
"base64": "<string>",
"metadata": {}
}
],
"device_findings": [
{
"count": 50000
}
]
}
'import requests
url = "https://api.promptguard.co/api/v1/guard"
payload = {
"messages": [
{
"role": "<string>",
"content": ""
}
],
"direction": "input",
"model": "<string>",
"context": {
"framework": "<string>",
"chain_name": "<string>",
"agent_id": "<string>",
"session_id": "<string>",
"tool_calls": [{}],
"metadata": {}
},
"retrieved_context": [
{
"content": "<string>",
"source": "<string>",
"metadata": {}
}
],
"media": [
{
"type": "<string>",
"mime_type": "<string>",
"url": "<string>",
"base64": "<string>",
"metadata": {}
}
],
"device_findings": [{ "count": 50000 }]
}
headers = {
"X-API-Key": "<api-key>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'X-API-Key': '<api-key>', 'Content-Type': 'application/json'},
body: JSON.stringify({
messages: [{role: '<string>', content: ''}],
direction: 'input',
model: '<string>',
context: {
framework: '<string>',
chain_name: '<string>',
agent_id: '<string>',
session_id: '<string>',
tool_calls: [{}],
metadata: {}
},
retrieved_context: [{content: '<string>', source: '<string>', metadata: {}}],
media: [
{
type: '<string>',
mime_type: '<string>',
url: '<string>',
base64: '<string>',
metadata: {}
}
],
device_findings: [{count: 50000}]
})
};
fetch('https://api.promptguard.co/api/v1/guard', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.promptguard.co/api/v1/guard",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'messages' => [
[
'role' => '<string>',
'content' => ''
]
],
'direction' => 'input',
'model' => '<string>',
'context' => [
'framework' => '<string>',
'chain_name' => '<string>',
'agent_id' => '<string>',
'session_id' => '<string>',
'tool_calls' => [
[
]
],
'metadata' => [
]
],
'retrieved_context' => [
[
'content' => '<string>',
'source' => '<string>',
'metadata' => [
]
]
],
'media' => [
[
'type' => '<string>',
'mime_type' => '<string>',
'url' => '<string>',
'base64' => '<string>',
'metadata' => [
]
]
],
'device_findings' => [
[
'count' => 50000
]
]
]),
CURLOPT_HTTPHEADER => [
"Content-Type: application/json",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.promptguard.co/api/v1/guard"
payload := strings.NewReader("{\n \"messages\": [\n {\n \"role\": \"<string>\",\n \"content\": \"\"\n }\n ],\n \"direction\": \"input\",\n \"model\": \"<string>\",\n \"context\": {\n \"framework\": \"<string>\",\n \"chain_name\": \"<string>\",\n \"agent_id\": \"<string>\",\n \"session_id\": \"<string>\",\n \"tool_calls\": [\n {}\n ],\n \"metadata\": {}\n },\n \"retrieved_context\": [\n {\n \"content\": \"<string>\",\n \"source\": \"<string>\",\n \"metadata\": {}\n }\n ],\n \"media\": [\n {\n \"type\": \"<string>\",\n \"mime_type\": \"<string>\",\n \"url\": \"<string>\",\n \"base64\": \"<string>\",\n \"metadata\": {}\n }\n ],\n \"device_findings\": [\n {\n \"count\": 50000\n }\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.promptguard.co/api/v1/guard")
.header("X-API-Key", "<api-key>")
.header("Content-Type", "application/json")
.body("{\n \"messages\": [\n {\n \"role\": \"<string>\",\n \"content\": \"\"\n }\n ],\n \"direction\": \"input\",\n \"model\": \"<string>\",\n \"context\": {\n \"framework\": \"<string>\",\n \"chain_name\": \"<string>\",\n \"agent_id\": \"<string>\",\n \"session_id\": \"<string>\",\n \"tool_calls\": [\n {}\n ],\n \"metadata\": {}\n },\n \"retrieved_context\": [\n {\n \"content\": \"<string>\",\n \"source\": \"<string>\",\n \"metadata\": {}\n }\n ],\n \"media\": [\n {\n \"type\": \"<string>\",\n \"mime_type\": \"<string>\",\n \"url\": \"<string>\",\n \"base64\": \"<string>\",\n \"metadata\": {}\n }\n ],\n \"device_findings\": [\n {\n \"count\": 50000\n }\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.promptguard.co/api/v1/guard")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"messages\": [\n {\n \"role\": \"<string>\",\n \"content\": \"\"\n }\n ],\n \"direction\": \"input\",\n \"model\": \"<string>\",\n \"context\": {\n \"framework\": \"<string>\",\n \"chain_name\": \"<string>\",\n \"agent_id\": \"<string>\",\n \"session_id\": \"<string>\",\n \"tool_calls\": [\n {}\n ],\n \"metadata\": {}\n },\n \"retrieved_context\": [\n {\n \"content\": \"<string>\",\n \"source\": \"<string>\",\n \"metadata\": {}\n }\n ],\n \"media\": [\n {\n \"type\": \"<string>\",\n \"mime_type\": \"<string>\",\n \"url\": \"<string>\",\n \"base64\": \"<string>\",\n \"metadata\": {}\n }\n ],\n \"device_findings\": [\n {\n \"count\": 50000\n }\n ]\n}"
response = http.request(request)
puts response.read_body{
"decision": "<string>",
"event_id": "<string>",
"confidence": 123,
"latency_ms": 123,
"weighted_score": 123,
"threat_type": "<string>",
"redacted_messages": [
{
"role": "<string>",
"content": ""
}
],
"threats": [
{
"type": "<string>",
"confidence": 123,
"details": "<string>",
"weighted_score": 123
}
],
"unscanned": [
{
"index": 123,
"reason": "<string>",
"detail": "<string>"
}
],
"unavailable": [
{
"detector": "<string>",
"reason": "<string>"
}
],
"entitlement": {
"plan": "<string>",
"seats": 123,
"scans_used": 123,
"scan_allowance": 123,
"mode": "<string>",
"as_of": "2023-11-07T05:31:56Z"
}
}{
"error": {
"code": "missing_api_key",
"message": "PromptGuard API key required. Please provide via X-API-Key header.",
"type": "authentication_error"
}
}{
"detail": [
{
"loc": [
"<string>"
],
"msg": "<string>",
"type": "<string>",
"input": "<unknown>",
"ctx": {}
}
]
}{
"error": {
"message": "<string>",
"type": "<string>",
"code": "<string>",
"current_plan": "<string>",
"requests_used": 123,
"requests_limit": 123,
"upgrade_url": "<string>",
"retry_after": 123
}
}Authorizations
PromptGuard API key for developer endpoints. Keys start with pg_live_ and are created in the dashboard.
Headers
Body
Request body for the guard endpoint.
Messages to scan (OpenAI-style message array)
1 - 512 elementsShow child attributes
Show child attributes
Scan direction: 'input' (pre-LLM) or 'output' (post-LLM)
^(input|output)$Model being used (for logging)
Optional framework context
Show child attributes
Show child attributes
RAG-retrieved documents to scan for knowledge poisoning. Each document is scanned individually; the first poisoned one blocks the request, and its position and source are returned in the event metadata so you know which document to drop. Scanning stops at that point, so a request with several poisoned documents reports the first. Optional; backwards-compatible.
32Show child attributes
Show child attributes
Media attachments to scan for steganographic payloads, adversarial patches, and font injection. Optional.
8Show child attributes
Show child attributes
What the calling Device's own masker caught before sending, as {category, count} pairs — never the values. category is one of the threat types this API returns (e.g. api_key_leak, pii_leak) and appears at most once; count is a positive integer. Any other key is rejected with 422. Recorded on this request's event, attributed to the Device, and shown in the fleet views; it does not change the decision. Optional; clients that omit it are unaffected.
37Show child attributes
Show child attributes
Response
Successful Response
Response from the guard endpoint.
Policy decision: 'allow', 'block', or 'redact'
Unique event identifier for tracking
Confidence score of the decision
Processing time in milliseconds
Aggregate decision-driving score (severity * confidence, clamped to [0, 1]) when a severity-carrying detector decided the verdict; null otherwise. Raw confidence stays in the confidence field.
Primary threat type detected
Redacted messages (only present when decision='redact'). Always the TEXT projection: a message sent as content blocks comes back as a string. Attachments are never rewritten — we do not re-encode a PDF with the secret removed, and returning one that looked redacted would be worse than returning none.
Show child attributes
Show child attributes
Detailed threat breakdown
Show child attributes
Show child attributes
Parts that reached us and produced nothing to scan. An allow with a non-empty unscanned is NOT 'this content is clean' — it is 'the text was clean and these parts were never read'. Reasons: url_only (we do not fetch caller-supplied URLs, that would be an SSRF primitive), file_id_unsupported, encrypted, no_text_extracted (a scanned/rasterised document), too_large, undecodable, unsupported_type, extractor_unavailable, unsupported_block, unsupported_tool_call (an entry in context.tool_calls in none of the shapes we can read — index is its position in that list).
Show child attributes
Show child attributes
Unavailable checks: detectors this scan would have run but the deployment cannot, because their backing service (an ML inference endpoint, an LLM judge, a bundled model) is not configured. An allow with a non-empty unavailable is NOT 'every check passed' — these detectors never looked. A detector that ran and found nothing is not listed, and neither is one the project's plan or guardrail settings leave out.
Show child attributes
Show child attributes
What this account is entitled to, at the instant this scan was answered. Null when the engine cannot say — an account with no subscription row, an admin key that bypasses the counter, or a row it could not read. Null is 'unknown', NOT 'unentitled': a client that reads it as a limit has invented a refusal the engine never made.
Show child attributes
Show child attributes