PalveronPalveronDocs

Verify

The core endpoint for AI governance — send a prompt, receive a decision.

Every governance decision starts here. Send a prompt; Palveron runs it through policies, NGE, and (optionally) LLM-assist; you get back a decision and a fully-attributed trace.

POST /api/v1/verify

curl -X POST https://gateway.palveron.com/api/v1/verify \
  -H "Authorization: Bearer pv_live_..." \
  -H "Content-Type: application/json" \
  -d '{
    "prompt": "My social security number is 123-45-6789.",
    "metadata": { "agent_id": "ckagent..." },
    "context": { "tool_name": "customer-support-ui" }
  }'

Request

FieldRequiredDescription
prompt✅The text to evaluate
metadata—Free-form JSON object. metadata.agent_id attributes the request to a registered agent (omit it and the trace is recorded without agent attribution). metadata.source: "browser" marks Browser-Guard traffic.
attachments—Array of multi-modal payloads (see Multi-modal)
context—Request-origin context: tool_name, MCP server, chain depth, source system — passed through to traces
structured_input—Optional structured field decomposition for tools with routing metadata (e.g. email:send). When present and the tool is known to the field-role registry, the response carries modified_fields.
extracted_text—Legacy: pre-extracted text from images/PDFs. Prefer attachments.

Agent attribution lives in metadata.agent_id — there is no top-level agent_id field. A top-level agent_id is silently ignored and the trace stays agent-anonymous.

Response

{
  "decision": "BLOCKED",
  "output": "",
  "reason": "PII detected: SSN",
  "trace_id": "cktrace...",
  "integrity_hash": "sha256:...",
  "sequence_num": 1042,
  "flare_status": "PENDING",
  "should_anchor": true,
  "content_type": "text"
}
FieldDescription
decisionThe governance verdict (see below)
outputThe content to forward to your LLM. On MODIFIED this is the redacted/edited prompt; on PASSED the original.
reasonHuman-readable explanation (present when a policy fired). When the gateway masked personal data, the sentence in the language of the request reads "Personal data was detected in this request and protected before it was passed on.", followed by "Redacted: {categories}." and "Anonymized: {categories}." where they apply; {categories} are the names with the number of distinct values, such as "Person name (2)".
reason_codeOnly when present: machine-readable code for reason. For sending with a justification in the browser: justification_unavailable (see below).
trace_idTamper-evident trace record id — use it in the Trace Explorer, exports, and POST /api/v1/verify-proof
integrity_hashSHA-256 integrity hash of the trace
sequence_numMonotonic per-project trace sequence number
flare_statusBlockchain anchoring status of the trace (e.g. PENDING), when Flare is active
should_anchorWhether this trace qualifies for blockchain anchoring
content_typeDetected content type (text, image, audio, document, …)
findingsOnly on multi-modal requests: findings per attachment
enforcement_actionOnly on Browser-Guard requests: browser enforcement action (BLOCK, WARN, JUSTIFY, REDIRECT)
action_configOnly with enforcement_action: its configuration (e.g. redirect target)
error_classOnly on fail-closed/engine-error verdicts: machine-readable failure class (e.g. llm_4xx)
modified_fieldsOnly on structured_input requests: per-field result map (ROUTING preserved, CONTENT masked)
justificationOnly in answers to Browser Guard, when the prompt can be sent with a justification in the browser: what may go in clear text and what stays masked (see below)

Optional fields are omitted from the JSON entirely when not applicable.

Per-policy NGE scores, detected entities, and match details are not part of the verify response — they are recorded on the trace. Retrieve them via the Traces API or the Trace Explorer.

Decisions

  • PASSED — Request allowed, original prompt in output.
  • BLOCKED — Request rejected. reason explains why.
  • MODIFIED — Request allowed with edits. output contains the redacted version.
  • FLAGGED — Request allowed; a LOG_ONLY policy fired and the violation is recorded in the trace. If the flagged prompt carries detected personal data or credentials, they are masked before being passed on and the verdict is relabelled ANONYMIZED (or REDACTED for credentials); the triggering rule stays recorded as an observed policy on the trace. The response field observed_policies lists these observed policies (always present, [] when there is none).
  • PENDING_APPROVAL — Request queued for a human approver. Track the outcome via GET /api/v1/approvals/status?trace_id=....
  • POLICY_CHANGE — Request allowed; it triggered a governance/policy-change event rather than a content verdict.

Sending with a justification in the browser

When the gateway detected personal data in a prompt from Browser Guard, employees can send the prompt with a justification. Only categories that allow a justification go in clear text: person name, organization, location, email address and phone number. IBAN, credit card, social security and tax numbers, ID numbers, credentials and every other category stay masked. In this case the answer carries the field justification:

FieldMeaning
stateavailable: the browser may send with a justification. unavailable: the prompt could not be fully checked for personal data; sending with a justification is not possible.
outputOnly for available: the text that is sent with a justification. Justifiable categories appear in clear text, all others masked as in the output of the answer.
releasedThe justifiable categories that go in clear text, each with label, name and count
still_maskedThe categories that stay masked and cannot be released, each with label, name and count

label is the identifier of the category (such as IBAN), name its word in the language of the request (such as "Person name"), count the number of distinct values. The clear text of a masked detail never appears in the answer, and the field is not stored.

With state: "unavailable", enforcement_action is BLOCK, reason_code is justification_unavailable, and reason reads: "This prompt could not be fully checked for personal data. Sending with a justification is therefore not possible, and the prompt was not sent. Please try again later." The verdict in decision stays the gateway's verdict.

What was actually sent after the choice is stated in the record of the request (see the Traces API).

HTTP status codes

The HTTP status code mirrors the decision field, so load balancers, observability tools, and HTTP-aware clients (e.g. MCP / OpenClaw consumers) get a faithful signal without having to parse the JSON body.

StatusDecisionMeaning
200 OKPASSED / MODIFIED / FLAGGED / POLICY_CHANGERequest allowed. Forward output to your LLM.
202 AcceptedPENDING_APPROVALHuman approval required. The trace is persisted; the approval is tracked via trace_id.
403 ForbiddenBLOCKEDRequest denied by policy or capability model. Do not retry — inspect reason.
429 Too Many Requestsno decisionTier rate-limit hit. Honour the Retry-After header. Body is the rate-limit error, not a verify response.
400 Bad Requestno decisionMalformed request (e.g. missing prompt). error.code is validation_error.
503 Service Unavailableno bodyThe gateway could not durably record the governance decision (DB and DLQ unavailable). Retry with backoff.

The JSON body is the source of truth. A response with a decision field is a verify response regardless of status (200 / 202 / 403). Clients should branch on decision, not on response.ok. Reserve HTTP-status branching for 429 (rate limits) and 5xx (transport errors).

Resilience headers

Every governance response carries three headers:

HeaderValues
X-Palveron-Statushealthy | degraded — platform health rollup
X-Palveron-Trace-Persisteddb | dlq | none — where this request's trace landed
X-Palveron-Degraded-SubsystemsComma-separated list; omitted entirely when healthy (header absence is the fast "all good" probe)

Errors

Distinct from a governance BLOCKED (which is a 403 with a verify-shaped body), transport/validation errors return a structured error object:

{ "error": { "code": "validation_error", "message": "Prompt is required", "request_id": "req-..." } }

Messages are localized (Accept-Language); branch on error.code (for example validation_error), and quote error.request_id in support requests.

Tip: distinguish a transport error from a governance block by checking for the decision field. If it's present, the response is a verify outcome (parse the body). If it's absent, treat the status code as a transport/auth error.

Retry behavior

POST /verify is not idempotent — a retry creates a second trace. Retry only on transient failures (timeouts, 429 honouring Retry-After, 5xx) with exponential backoff and jitter, and treat 403/400 as final.

On this page