Appearance
Endpoint: Routing Policies
Manage routing policies that control how Veriprompt selects AI providers.
Endpoints
| Method | Path | Description |
|---|---|---|
| GET | /api/v1/routing/policies | List all policies |
| POST | /api/v1/routing/policies | Create a policy |
| GET | /api/v1/routing/policies/:id | Get a policy |
| PATCH | /api/v1/routing/policies/:id | Update a policy |
| DELETE | /api/v1/routing/policies/:id | Delete a policy |
| POST | /api/v1/routing/policies/:id/simulate | Simulate a policy |
Authentication
All endpoints require a valid API key or session token. Session-authenticated users also need the matching routing policy permission:
routing.policies.viewrouting.policies.createrouting.policies.editrouting.policies.delete
http
Authorization: Bearer YOUR_API_KEYList Policies
http
GET /api/v1/routing/policiesResponse
json
{
"success": true,
"policies": [
{
"id": "clxyz123...",
"name": "US Only Policy",
"description": "Route to US providers only",
"scope": "company",
"isDefault": true,
"policyJson": {
"version": "routingPolicyV1",
"strategy": "BALANCED",
"geoFenceRules": {
"allowedCountries": ["US"]
}
},
"createdAt": "2025-01-15T10:00:00Z",
"updatedAt": "2025-01-15T10:00:00Z"
}
],
"companyName": "Acme Corp"
}Create Policy
http
POST /api/v1/routing/policies
Content-Type: application/jsonRequest Body
json
{
"name": "Low Latency EU",
"description": "Optimized for EU users",
"scope": "company",
"isDefault": false,
"policyJson": {
"version": "routingPolicyV1",
"strategy": "LOWEST_LATENCY",
"sanitization": {
"mode": "manual",
"allowedTriggers": ["prompt", "api", "user_action"],
"sensitivityProfiles": ["personal_information"]
},
"hardConstraints": {
"minAvailability": 0.95,
"maxLatencyMs": 3000
},
"preferences": {
"weights": {
"latency": 0.5,
"cost": 0.2,
"quality": 0.2,
"stability": 0.1
}
},
"geoFenceRules": {
"allowedRegions": ["EU"],
"requireCompliance": ["GDPR"]
},
"providerGroups": [],
"fallback": {
"enabled": true,
"maxRetries": 3,
"retryDelayMs": 1000
}
}
}Parameters
| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Policy name (unique per company) |
description | string | No | Human-readable description |
scope | string | Yes | company, user, or prompt |
isDefault | boolean | No | Set as default for scope |
policyJson | object | Yes | Policy configuration |
Policy JSON Schema
typescript
interface PolicyJson {
version: "routingPolicyV1";
name?: string;
scope?: string;
strategy: "BALANCED" | "LOWEST_COST" | "LOWEST_LATENCY" | "HIGHEST_QUALITY" | "ROUND_ROBIN";
sanitization?: {
mode: "disabled" | "automatic" | "manual";
allowedTriggers?: Array<"prompt" | "api" | "user_action">;
sensitivityProfiles?: string[];
};
hardConstraints?: {
minAvailability?: number; // 0-1, default 0.95
maxLatencyMs?: number; // milliseconds
maxCostPerToken?: number; // cost per token
};
preferences?: {
weights?: {
latency?: number; // 0-1, default 0.25
cost?: number; // 0-1, default 0.25
quality?: number; // 0-1, default 0.25
stability?: number; // 0-1, default 0.25
};
};
geoFenceRules?: {
allowedRegions?: string[]; // ["US", "EU", "UK", "APAC"]
blockedRegions?: string[];
allowedCountries?: string[]; // ["US", "DE", "UK"]
blockedCountries?: string[];
requireCompliance?: string[]; // ["GDPR", "SOC2", "HIPAA"]
};
providerGroups?: string[]; // Array of provider group IDs
fallback?: {
enabled?: boolean; // default true
maxRetries?: number; // default 3
retryDelayMs?: number; // default 1000
};
}Sanitization Modes
disabled: the gateway never sanitizes for this policy.automatic: every execution under the policy is sanitized before provider dispatch.manual: sanitization only runs when the request explicitly triggers it and the trigger source is allowed.
Geofencing Validation (Strict)
On POST /api/v1/routing/policies and PATCH /api/v1/routing/policies/:id, geofencing arrays are validated against the company's active provider configuration baseline (ProviderModelConfig):
geoFenceRules.allowedCountriesgeoFenceRules.blockedCountriesgeoFenceRules.allowedRegionsgeoFenceRules.blockedRegions
Baseline fields:
- Countries from
locationCountry - Regions from
locationNetwork
If invalid values are submitted, the API returns 400.
Validation Error Example
json
{
"error": "Invalid geofencing values. Countries/regions must match active provider configuration values for this company.",
"details": [
{
"field": "allowedCountries",
"invalidValues": ["GERMANY"],
"allowedSample": ["US", "DE", "FR"],
"allowedCount": 3
}
]
}Response
json
{
"success": true,
"policy": {
"id": "clxyz456...",
"name": "Low Latency EU",
"description": "Optimized for EU users",
"scope": "company",
"isDefault": false,
"policyJson": { ... },
"createdAt": "2025-01-15T10:30:00Z",
"updatedAt": "2025-01-15T10:30:00Z"
}
}Get Policy
http
GET /api/v1/routing/policies/:idResponse
json
{
"success": true,
"policy": {
"id": "clxyz456...",
"name": "Low Latency EU",
...
}
}Update Policy
http
PATCH /api/v1/routing/policies/:id
Content-Type: application/jsonRequest Body
Only include fields you want to update:
json
{
"name": "Low Latency EU v2",
"policyJson": {
"strategy": "LOWEST_LATENCY",
"hardConstraints": {
"maxLatencyMs": 2000
}
}
}Response
json
{
"success": true,
"policy": { ... }
}Delete Policy
http
DELETE /api/v1/routing/policies/:idResponse
json
{
"success": true,
"message": "Policy deleted"
}Simulate Policy
Test how a policy would route requests without executing them.
http
POST /api/v1/routing/policies/:id/simulateResponse
json
{
"success": true,
"policy": {
"id": "clxyz456...",
"name": "Low Latency EU"
},
"simulation": {
"primary": {
"provider": "anthropic",
"model": "claude-3-sonnet",
"estimatedLatency": 450,
"estimatedCost": 0.000015,
"qualityScore": 0.92,
"availability": 0.998,
"compositeScore": 0.87,
"reasoning": "Best match for latency-optimized EU routing"
},
"fallbacks": [
{
"provider": "openai",
"model": "gpt-4-turbo",
"estimatedLatency": 620,
"estimatedCost": 0.00003,
"qualityScore": 0.95,
"availability": 0.996,
"compositeScore": 0.82
}
],
"totalCandidates": 8,
"filteredOut": 3
},
"metadata": {
"simulatedAt": "2025-01-15T10:45:00Z",
"basedOnWindow": "24h"
}
}Task-aware simulation (optional request body)
Send a JSON body to see how the policy ranks for a task, with and without task-aware routing. An empty body behaves exactly as before. This endpoint (like /api/v1/routing/rank) is used by the policy editor and authenticates with the signed-in session; cookies.txt below stands for that session.
| Field | Type | Description |
|---|---|---|
content | string (max 20,000 characters) | Sample request text, classified with the rule-based classifier only. No model is called, so a simulation costs nothing |
taskCodes | string[] or comma-separated string | Explicit task codes (profile source EXPLICIT). Up to 20, each up to 64 characters |
taskFit | "policy" (default), "off", "on" | policy follows the stored policy; off ranks without task fit; on is a what-if as if the policy had task fit enabled. Still subject to the company's package and the confidence threshold |
bash
curl -X POST https://app.veriprompt.tech/api/v1/routing/policies/POLICY_ID/simulate \
-b cookies.txt \
-H "Content-Type: application/json" \
-d '{ "content": "Review this contract for liability clauses", "taskFit": "on" }'When content or taskCodes is sent, the response gains two top-level fields next to simulation (both null otherwise):
json
{
"success": true,
"simulation": { "...": "unchanged" },
"taskProfile": {
"codes": ["legal", "contract"],
"dimensions": ["domain_legal", "long_context"],
"confidence": 0.4,
"source": "CLASSIFIED",
"applied": false,
"classifierVersion": "regex-v1",
"notes": [ { "key": "taskFitExplainNoteRegex", "vars": {} } ]
},
"taskFit": {
"mode": "on",
"policyEnabled": false,
"applied": false,
"reason": "below-min-confidence",
"summary": { "applied": false, "source": "CLASSIFIED", "codes": ["legal", "contract"], "dimensions": ["domain_legal", "long_context"], "confidence": 0.4 },
"ranking": [ { "rank": 1, "provider": "anthropic", "model": "example-model", "compositeScore": 0.87, "qualityScore": 0.7, "taskFit": null, "explanations": [] } ],
"taskFitOff": [ { "rank": 1, "provider": "anthropic", "model": "example-model", "compositeScore": 0.87, "qualityScore": 0.7, "taskFit": null, "explanations": [] } ],
"rankChanges": [],
"explanations": [ { "key": "taskFitExplainLowConfidence", "vars": { "confidence": 40 } } ]
}
}rankingis the order the runtime would produce undermode;taskFitOffis the same candidate set without task fit;rankChangeslists{ provider, model, from, to }for every candidate whose position differs (empty when not applied).applied: falsecarries areason:policy-disabled,profile-not-applied,below-min-confidence,not-entitled(the company's package does not include task-fit routing) ormode-off.- Each ranked candidate has
taskFitwithlegacyQualityScore,blendedQualityScore,perDimensionandprovenance(sourceisinternal,external,manualortier;benchmarks,asOfandnwhere applicable), andexplanationsas{ key, vars }pairs. - A
taskCodesortaskFitvalue of the wrong type returns400. - Permission is the same as for any simulation; the policy must belong to your company.
Rank providers
http
POST /api/v1/routing/rankRanks providers for a policy. It accepts the same optional taskCodes and content fields as the simulation, and returns taskProfile and taskFit ({ applied, reason?, ...summary, explanations }) next to the existing result. Each ranked candidate carries its evidence provenance under taskFit when task fit applied. Both fields are null when no task fields were sent.
Policy JSON: preferences.taskFit
json
{
"preferences": {
"weights": { "quality": 0.4, "cost": 0.3, "latency": 0.2, "stability": 0.1 },
"taskFit": {
"enabled": true,
"classifier": "gateway",
"externalEvidence": "allow",
"minInternalSamples": 30,
"staleAfterDays": 120,
"minConfidence": 0.5,
"dimensionWeights": { "domain_legal": 2 }
}
}
}| Field | Type | Default | Description |
|---|---|---|---|
enabled | boolean | false | Master switch. Saving true requires the Task-Fit Routing package feature, otherwise 403 with code FEATURE_NOT_ENTITLED |
classifier | "off", "regex", "gateway" | "gateway" | How the task is detected when not stated. gateway may send one short classification request through your own gateway path |
externalEvidence | "allow", "deny" | "allow" | Whether third-party benchmarks may influence ranking |
minInternalSamples | integer 1 to 100000 | 30 | Own evidence outranks external evidence from this many samples |
staleAfterDays | integer 1 to 3650 | 120 | Older benchmark rows carry no weight |
minConfidence | number 0 to 1 | 0.5 | A detected task below this is recorded but not applied |
dimensionWeights | object | none | Per capability weight 0 to 10: coding, tool_use, reasoning, math, instruction_following, factuality, long_context, multilingual_de, writing, domain_legal, domain_medical, domain_finance, vision, general. Unknown keys are dropped |
Out-of-range numbers are clamped rather than rejected. See Task-Aware Routing.
Strategy Values
| Value | Description |
|---|---|
BALANCED | Equal weight to all factors |
LOWEST_COST | Prioritize lowest cost providers |
LOWEST_LATENCY | Prioritize fastest providers |
HIGHEST_QUALITY | Prioritize highest quality output |
ROUND_ROBIN | Rotate between available providers |
Geofencing Codes
Regions
US, EU, UK, APAC, LATAM, ME, AF
Countries
US, CA, UK, DE, FR, NL, IE, JP, AU, SG, IN, BR
Compliance Standards
GDPR, SOC2, HIPAA, ISO27001, PCI_DSS
Example: Create US-Only HIPAA Policy
bash
curl https://app.veriprompt.tech/api/v1/routing/policies \
-H "Authorization: Bearer YOUR_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "US Healthcare",
"description": "HIPAA-compliant US-only routing",
"scope": "company",
"isDefault": false,
"policyJson": {
"version": "routingPolicyV1",
"strategy": "HIGHEST_QUALITY",
"hardConstraints": {
"minAvailability": 0.99
},
"geoFenceRules": {
"allowedCountries": ["US"],
"requireCompliance": ["HIPAA", "SOC2"]
},
"fallback": {
"enabled": true,
"maxRetries": 3
}
}
}'Error Responses
400 Bad Request
json
{
"error": "Invalid policy",
"details": [
{ "path": ["name"], "message": "Required" }
]
}401 Unauthorized
json
{
"error": "Unauthorized"
}404 Not Found
json
{
"error": "Policy not found"
}409 Conflict
json
{
"error": "A policy with this name already exists"
}