Connect a runtime
Fetch effective NovaGuard policies and usage state, enforce them before provider calls, and report allowed or blocked usage events.
NovaGuard supplies policy definitions, usage counters, and an admission API for cost and rate limits. Your gateway requests admission before a provider call, evaluates inline content policies, and reports the final outcome.
NovaGuard does not sit inline with provider traffic. A saved policy has no effect until a connected runtime fetches it, enforces it before the provider call, and reports the outcome.
Before you start
You need:
- the organization ID that owns the project
- the project ID that owns the provider traffic
- an organization API key with
guardrails:readandguardrails:ingest - at least one enabled organization or project policy
Store the API key in a server-side secret and send it as a Bearer token. Do not put it in browser code, source control, screenshots, or logs.
export NOVEUM_API_KEY="<organization-api-key>"
export NOVEUM_ORGANIZATION_ID="<organization-id>"
export NOVEUM_PROJECT_ID="<project-id>"All examples use https://noveum.ai as the API origin.
Integration flow
ETag.Use the admission API for atomic cost and rate reservations. Read-only state is useful for inspection and dry runs; it does not reserve capacity against concurrent requests.
Reserve capacity before a provider call
Send POST /api/v1/projects/:projectId/policies/admit with a stable requestId, model, optional provider, estimatedInputTokens, maximumOutputTokens, and estimatedCostUsd. Reserve the maximum expected output rather than an optimistic average.
The response returns allowed: false with the blocking policy and observed/projected limit, or allowed: true with a reservationId, expiry, and policy version. Retry the same request ID after an ambiguous transport failure. Admission checks cost caps and rate limits; evaluate content policies separately in the gateway.
After an allowed call, send actual inputTokens, outputTokens, and costUsd to POST /api/v1/projects/:projectId/policies/reservations/:reservationId/complete. Supply a stable eventId to correlate the completion. Release unused capacity through the reservation's /abandon or /cancel endpoint when the provider call will not proceed. Do not independently charge the same call again through usage ingestion.
A 503 GUARDRAIL_ADMISSION_UNAVAILABLE response means the service could not check admission. Apply the policy's fail-closed behavior instead of interpreting the failure as permission to proceed.
Fetch effective policies
For project traffic, fetch the merged organization and project policy set:
curl --include \
"https://noveum.ai/api/v1/projects/$NOVEUM_PROJECT_ID/policies/effective" \
--header "Authorization: Bearer $NOVEUM_API_KEY"The response is an array of enabled policies. Each object includes its policyId, type, config, failClosed, priority, and source. source is either org or project.
The project endpoint applies the same merge rules shown in the dashboard: a project policy replaces an organization policy with the same name, then the result is ordered by priority. Disabled and deleted policies are omitted.
These related read routes are available when an integration needs a scope-specific view:
| Route | Result |
|---|---|
GET /api/v1/projects/:projectId/policies | Saved policies defined directly on the project |
GET /api/v1/projects/:projectId/policies/effective | Enabled organization and project policies effective for that project |
GET /api/v1/organizations/:organizationId/policies | Saved policies defined directly on the organization |
GET /api/v1/organizations/:organizationId/policies/effective | Enabled organization policies |
Use the project effective route for enforcement instead of merging scopes in the client.
Fetch usage state
Project-scoped and organization-scoped policies use different counters. Fetch both states:
curl --include \
"https://noveum.ai/api/v1/projects/$NOVEUM_PROJECT_ID/policies/state" \
--header "Authorization: Bearer $NOVEUM_API_KEY"
curl --include \
"https://noveum.ai/api/v1/organizations/$NOVEUM_ORGANIZATION_ID/policies/state" \
--header "Authorization: Bearer $NOVEUM_API_KEY"The response contains:
| Field | Meaning |
|---|---|
cost | Current USD totals for rolling 1-day, 7-day, and 30-day windows and the calendar month, plus current calendar-month perModel totals |
rate | Current request and token totals for 1-minute, 1-hour, and 1-day periods |
asOf | Time at which the state was calculated |
ttlSeconds | How long the runtime may treat the state as current |
stale | Whether Noveum had to return fallback state that may be behind the live counters |
Match each effective policy to the state for its source: use organization state for a policy with source: "org" and project state for one with source: "project". Reporting one allowed event to the project usage endpoint updates both scopes, so do not submit a second organization event.
State counters are built from ALLOWED usage reports. A BLOCKED report records the decision and can trigger an owner alert, but does not add provider cost or token usage.
Cache and refresh safely
The effective response and both state responses include:
Cache-Control: private, max-age=30
ETag: "<response-hash>"Keep the ETag separately for each of the three responses. On the next refresh, send it in If-None-Match:
curl --include \
"https://noveum.ai/api/v1/projects/$NOVEUM_PROJECT_ID/policies/effective" \
--header "Authorization: Bearer $NOVEUM_API_KEY" \
--header 'If-None-Match: "<previous-response-hash>"'A 304 Not Modified response has no body; continue using the last validated payload. Do not poll either route more often than its 30-second cache lifetime.
Treat either state response with stale: true as degraded input and make that condition visible in runtime logs or metrics. For a policy already known to the runtime, if no usable state exists for its scope after the cached copy expires, honor that policy's failClosed setting:
true- block the provider call while state is unavailablefalse- allow the provider call while state is unavailable
Define an explicit startup behavior for the separate case where the runtime has never fetched a policy set and therefore has no failClosed value to apply.
Enforce before the provider call
Evaluate inline policies in the gateway and use admission for counter-based limits before sending traffic to the provider:
- For
COST_CAP, admission reserves estimated cost against the applicable organization and project windows. - For
RATE_LIMIT, admission reserves the pending request and its input plus maximum output tokens against each configured window. action: "BLOCK"means the runtime must skip the provider call when the threshold is reached.
Use the same cached policy and state snapshot for one decision so a refresh cannot change inputs halfway through evaluation. Log the request correlation ID, policy ID, state asOf, and outcome, but never log the API key or sensitive prompt content.
Report an allowed event
For integrations using usage reporting without admission, send actual usage after an allowed provider call finishes. Admission-based integrations complete their reservation instead:
curl "https://noveum.ai/api/v1/projects/$NOVEUM_PROJECT_ID/policies/usage" \
--request POST \
--header "Authorization: Bearer $NOVEUM_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"eventId": "request-01J8ABC123:allowed",
"timestamp": "2026-07-31T10:00:00.000Z",
"model": "gpt-4.1-mini",
"inputTokens": 850,
"outputTokens": 145,
"requestCount": 1,
"costUsd": 0.0018,
"outcome": "ALLOWED"
}'costUsd is required for an allowed event. inputTokens and outputTokens default to 0, requestCount defaults to 1, and outcome defaults to ALLOWED when omitted. Supply the provider's final values whenever they are available so later decisions use accurate counters.
Report a blocked event
When a policy blocks the request, do not call the provider. Report the blocked decision with the planned model and the policy information used by the runtime:
curl "https://noveum.ai/api/v1/projects/$NOVEUM_PROJECT_ID/policies/usage" \
--request POST \
--header "Authorization: Bearer $NOVEUM_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"eventId": "request-01J8ABC124:blocked",
"timestamp": "2026-07-31T10:00:01.000Z",
"model": "gpt-4.1-mini",
"requestCount": 1,
"outcome": "BLOCKED",
"blockedBy": "RATE_LIMIT",
"policyId": "<policy-id-from-effective-response>",
"reason": "requests_1m threshold reached"
}'blockedBy identifies the policy type, such as COST_CAP, RATE_LIMIT, or the applicable content-rule type. policyId and reason are optional, but including them makes the reported event easier to correlate with the runtime log. A blocked event defaults to zero cost and zero tokens.
Reporting a blocked event records what the runtime already decided. The endpoint does not make or reverse the decision.
Batch, acknowledgements, and retries
The request body can be one event or an array of up to 500 events. A valid request returns 202 Accepted:
{
"success": true,
"accepted": 2,
"persisted": 2,
"blocked": 1
}accepted is the number of validated events in the request. persisted can be lower when a previously seen eventId is skipped. blocked is the number of events whose outcome was BLOCKED.
Use a stable, unique eventId for each decision. If a delivery times out after sending, retry it with the same eventId; do not mint a new ID for the retry. Duplicate protection is short-lived - currently one hour - so it is a retry safeguard, not permanent idempotency.
The usage endpoint is limited to 6,000 requests per minute per API key. A 429 response includes Retry-After; wait for that interval, then retry with the original eventId values. Batch client-buffered events where practical, and back off on network errors and 5xx responses.
Verify the integration
Use Violations, the project policy /violations read endpoint, and the runtime decision log to verify blocked decisions. Verify a non-production integration with the admission or usage response:
- Create a generous enabled policy and confirm that the project
effectiveresponse contains itspolicyIdand expectedsource. - Confirm that subsequent conditional fetches return
304when the payload is unchanged. - Send an allowed test request and capture the runtime decision log plus the
202acknowledgement. - Temporarily set a safe test boundary that blocks one request, then confirm the provider was not called and the blocked event received a
202acknowledgement. - When the alert throttle permits delivery, confirm that an organization owner receives the blocked-event email and that its scope matches the test project.
- Restore the generous boundary after the test.
Keep the runtime decision log and acknowledgement together under the same request correlation ID. The owner email is supporting evidence for a blocked event, not the primary enforcement record.