Guardrails

Package: agentrouter.guardrails.v1 Service: GuardrailsService

Endpoints

List guardrails

Changed in v0.7.0 (new features): A project now reads the organization's guardrails alongside its own, and every guardrail, pattern and provider reports the level it sits at. The organization's rules are enforced on the project, so they belong in what it reads; a project still cannot edit them, and a write aimed at one is refused.

What it does: Returns guardrails matching the specified filters.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
pagenoCursor-pagination inputs.
searchnoOptional substring search on guardrail name.
typesnoFilter to guardrails of these types.
exclude_typesnoExclude guardrails of these types.
statusesnoFilter to guardrails in these statuses.
severitiesnoFilter to guardrails with these severity levels.
categoriesnoFilter to guardrails in these categories.
include_templatesnoWhen true, include platform template rows (is_template=true).
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.

Response fields:

FieldRequiredDescription
guardrailsnoGuardrails matching the request filters, one page's worth.
pagenoCursor-pagination outputs.
totalnoTotal count of matching guardrails across all pages.
{"signatures":{"go":"c.Guardrails().ListGuardrails(ctx, \u0026guardrailsv1.ListGuardrailsRequest{...})","python":"client.guardrails.listguardrails(...)","typescript":"client.guardrails.listguardrails({...})","cli":"tare api guardrails list","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails list","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails","slug":"list-guardrails"}

Get a guardrail

Changed in v0.7.0 (new features): A project now reads the organization's guardrails alongside its own, and every guardrail, pattern and provider reports the level it sits at. The organization's rules are enforced on the project, so they belong in what it reads; a project still cannot edit them, and a write aimed at one is refused.

What it does: Returns a single guardrail by id.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
idyesRequired: uuid of the guardrail to fetch.
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.

Response fields:

FieldRequiredDescription
guardrailnoThe requested guardrail.
{"signatures":{"go":"c.Guardrails().GetGuardrail(ctx, \u0026guardrailsv1.GetGuardrailRequest{...})","python":"client.guardrails.getguardrail(...)","typescript":"client.guardrails.getguardrail({...})","cli":"tare api guardrails get \u003cid\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H...\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails get \u003cid\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H...\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails/{id}","slug":"get-a-guardrail"}

Create a guardrail

Changed in v0.7.0 (behaviour changes): A project owner now creates, edits and deletes the guardrails and guardrail rules of a project they own, without an organization-wide guardrails credential. The shared pattern library and the external guardrail providers stay administrator-only, and a project owner still reaches no other project's guardrails.

Changed in v0.7.0 (new features): You can now author a guardrail, a guardrail rule, a guardrail provider and a pattern for a whole organization rather than one project at a time. Send scope GUARDRAIL_SCOPE_ORGANIZATION with no project_id, and every project of that customer is served the rule. A request that names a project behaves exactly as it did, and a request that names both is refused, so a caller that forgot the project cannot write into every project by accident.

What it does: Creates a new guardrail.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
guardrailyesRequired: guardrail to create. id must be absent or empty (server-assigned).
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.

Response fields:

FieldRequiredDescription
guardrailnoThe created guardrail with server-assigned fields populated.
{"signatures":{"go":"c.Guardrails().CreateGuardrail(ctx, \u0026guardrailsv1.CreateGuardrailRequest{...})","python":"client.guardrails.createguardrail(...)","typescript":"client.guardrails.createguardrail({...})","cli":"tare api guardrails create --guardrail $GUARDRAIL","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"guardrail\": {},\n    \"scope\": \"...\"\n  }'"},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails create --guardrail $GUARDRAIL","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"guardrail\": {},\n    \"scope\": \"...\"\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails","slug":"create-a-guardrail"}

Update a guardrail

Changed in v0.7.0 (behaviour changes): A project owner now creates, edits and deletes the guardrails and guardrail rules of a project they own, without an organization-wide guardrails credential. The shared pattern library and the external guardrail providers stay administrator-only, and a project owner still reaches no other project's guardrails.

Changed in v0.7.0 (new features): You can now author a guardrail, a guardrail rule, a guardrail provider and a pattern for a whole organization rather than one project at a time. Send scope GUARDRAIL_SCOPE_ORGANIZATION with no project_id, and every project of that customer is served the rule. A request that names a project behaves exactly as it did, and a request that names both is refused, so a caller that forgot the project cannot write into every project by accident.

What it does: Updates mutable guardrail fields. Omitted/empty scalar string fields leave the existing value untouched. Enum fields set to *_UNSPECIFIED leave the existing value untouched. Absent message fields (triggered_response) leave the existing value untouched; a present triggered_response replaces it wholesale. The guardrail id must be set inside the guardrail field. Returns PERMISSION_DENIED when the target guardrail is a template (is_template=true); templates are instantiated via CreateGuardrailFromTemplate, never mutated.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
guardrailyesRequired: guardrail to update. guardrail.id must be set. Partial update semantics: omitted/empty scalar string fields leave the existing value untouched; enum fields set to *_UNSPECIFIED leave the existing value untouched; absent message fields (triggered_response) leave the existing value untouched.
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.

Response fields:

FieldRequiredDescription
guardrailnoThe updated guardrail with server-assigned fields populated.
{"signatures":{"go":"c.Guardrails().UpdateGuardrail(ctx, \u0026guardrailsv1.UpdateGuardrailRequest{...})","python":"client.guardrails.updateguardrail(...)","typescript":"client.guardrails.updateguardrail({...})","cli":"tare api guardrails update --guardrail $GUARDRAIL","curl":"curl -X PATCH \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H...\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"guardrail\": {},\n    \"scope\": \"...\"\n  }'"},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails update --guardrail $GUARDRAIL","curl":"curl -X PATCH \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H...\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"guardrail\": {},\n    \"scope\": \"...\"\n  }'"},"persona":"Admin","httpVerb":"PATCH","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails/{guardrail.id}","slug":"update-a-guardrail"}

Delete a guardrail

Changed in v0.7.0 (behaviour changes): A project owner now creates, edits and deletes the guardrails and guardrail rules of a project they own, without an organization-wide guardrails credential. The shared pattern library and the external guardrail providers stay administrator-only, and a project owner still reaches no other project's guardrails.

What it does: Soft-deletes a guardrail (sets deleted_at; the row disappears from List/Get). Child rules of the soft-deleted guardrail are no longer served. Historical trigger records referencing this guardrail REMAIN queryable. Returns PERMISSION_DENIED when the target guardrail is a template (is_template=true); templates are instantiated via CreateGuardrailFromTemplate, never mutated.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
idyesRequired: uuid of the guardrail to delete.
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.
{"signatures":{"go":"c.Guardrails().DeleteGuardrail(ctx, \u0026guardrailsv1.DeleteGuardrailRequest{...})","python":"client.guardrails.deleteguardrail(...)","typescript":"client.guardrails.deleteguardrail({...})","cli":"tare api guardrails delete \u003cid\u003e","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H...\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails delete \u003cid\u003e","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H...\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"DELETE","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails/{id}","slug":"delete-a-guardrail"}

Set guardrail status

Changed in v0.7.0 (behaviour changes): A project owner now creates, edits and deletes the guardrails and guardrail rules of a project they own, without an organization-wide guardrails credential. The shared pattern library and the external guardrail providers stay administrator-only, and a project owner still reaches no other project's guardrails.

What it does: Updates the status and/or enabled flag of a guardrail. At least one of status/enabled must be provided; otherwise INVALID_ARGUMENT. Returns PERMISSION_DENIED when the target guardrail is a template (is_template=true); templates are instantiated via CreateGuardrailFromTemplate, never mutated.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
idyesRequired: uuid of the guardrail to update.
statusnoNew operational status for the guardrail. GUARDRAIL_STATUS_UNSPECIFIED leaves the stored status unchanged.
enablednoNew enabled state. When absent the enabled field is left unchanged.
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.

Response fields:

FieldRequiredDescription
guardrailnoThe guardrail with the updated status.
{"signatures":{"go":"c.Guardrails().SetGuardrailStatus(ctx, \u0026guardrailsv1.SetGuardrailStatusRequest{...})","python":"client.guardrails.setguardrailstatus(...)","typescript":"client.guardrails.setguardrailstatus({...})","cli":"tare api guardrails set-status --id $ID","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H.../status\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"...\",\n    \"enabled\": false,\n    \"scope\": \"...\"\n  }'"},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails set-status --id $ID","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H.../status\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"status\": \"...\",\n    \"enabled\": false,\n    \"scope\": \"...\"\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails/{id}/status","slug":"set-guardrail-status"}

Create a guardrail from a template

Changed in v0.7.0 (behaviour changes): A project owner now creates, edits and deletes the guardrails and guardrail rules of a project they own, without an organization-wide guardrails credential. The shared pattern library and the external guardrail providers stay administrator-only, and a project owner still reaches no other project's guardrails.

What it does: Instantiates a new guardrail from a platform template row (is_template=true). The resulting guardrail is editable and scoped to (customer_id, project_id). Returns NOT_FOUND when template_id does not reference an existing template guardrail (non-template ids are treated as not found).

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
template_idyesRequired: id of the template guardrail (is_template=true) to clone.
nameyesRequired: display name for the new guardrail.
descriptionnoOptional description for the new guardrail.
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.

Response fields:

FieldRequiredDescription
guardrailnoThe newly created guardrail instantiated from the template.
{"signatures":{"go":"c.Guardrails().CreateGuardrailFromTemplate(ctx, \u0026guardrailsv1.CreateGuardrailFromTemplateRequest{...})","python":"client.guardrails.createguardrailfromtemplate(...)","typescript":"client.guardrails.createguardrailfromtemplate({...})","cli":"tare api guardrails create-from-template --template-id $TEMPLATE_ID --name $NAME","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/from-template\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": \"...\",\n    \"name\": \"...\",\n    \"description\": \"...\",\n    \"scope\": \"...\"\n  }'"},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails create-from-template --template-id $TEMPLATE_ID --name $NAME","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/from-template\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"template_id\": \"...\",\n    \"name\": \"...\",\n    \"description\": \"...\",\n    \"scope\": \"...\"\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails/from-template","slug":"create-a-guardrail-from-a-template"}

Get guardrail statistics

What it does: Returns aggregate statistics for guardrails in scope.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.

Response fields:

FieldRequiredDescription
totalnoTotal number of guardrails in scope.
activenoNumber of guardrails with status ACTIVE.
by_typenoCount of guardrails per GuardrailType. Keys are the stored lowercase domain values (e.g. security, compliance), not enum value names.
by_severitynoCount of guardrails per GuardrailSeverity. Keys are the stored lowercase domain values (e.g. critical, high), not enum value names.
by_categorynoCount of guardrails per GuardrailCategory. Keys are the stored lowercase domain values (e.g. data-privacy, financial), not enum value names.
{"signatures":{"go":"c.Guardrails().GetGuardrailStats(ctx, \u0026guardrailsv1.GetGuardrailStatsRequest{...})","python":"client.guardrails.getguardrailstats(...)","typescript":"client.guardrails.getguardrailstats({...})","cli":"tare api guardrails stats","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/stats\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails stats","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/stats\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails/stats","slug":"get-guardrail-statistics"}

List guardrail rules

Changed in v0.7.0 (new features): Every guardrail rule now carries a reference, a short read-only id such as GR-0142 that the database mints once and never rewrites. The guardrail rule APIs and each trigger evaluation return it beside the display_name, and the data plane receives it in its runtime configuration, so a platform operator reading a trigger has one id to quote. Existing rules were numbered in creation order, and a blocked caller's refusal body does not carry the reference yet.

What it does: Returns rules for the specified guardrail.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
pagenoCursor-pagination inputs.
guardrail_idyesRequired: parent guardrail id.
searchnoOptional substring search on rule name.
activenoWhen set, filter to active or inactive rules only.
execute_onnoFilter to rules with these execute_on values.
provider_idsnoFilter to rules referencing these provider ids.
check_typesnoFilter to rules with these check_type values.
actionsnoFilter to rules with these action values.
modesnoFilter to rules with these mode values.
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.

Response fields:

FieldRequiredDescription
rulesnoRules matching the request filters, one page's worth.
pagenoCursor-pagination outputs.
totalnoTotal count of matching rules across all pages.
{"signatures":{"go":"c.Guardrails().ListGuardrailRules(ctx, \u0026guardrailsv1.ListGuardrailRulesRequest{...})","python":"client.guardrails.listguardrailrules(...)","typescript":"client.guardrails.listguardrailrules({...})","cli":"tare api guardrails rules list --guardrail-id $GUARDRAIL_ID","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H.../rules\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails rules list --guardrail-id $GUARDRAIL_ID","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H.../rules\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails/{guardrail_id}/rules","slug":"list-guardrail-rules"}

Get a guardrail rule

Changed in v0.7.0 (new features): Every guardrail rule now carries a reference, a short read-only id such as GR-0142 that the database mints once and never rewrites. The guardrail rule APIs and each trigger evaluation return it beside the display_name, and the data plane receives it in its runtime configuration, so a platform operator reading a trigger has one id to quote. Existing rules were numbered in creation order, and a blocked caller's refusal body does not carry the reference yet.

What it does: Returns a single rule by id. Returns NOT_FOUND when the rule does not exist under the given guardrail_id.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
guardrail_idyesRequired: parent guardrail id.
idyesRequired: uuid of the rule to fetch.
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.

Response fields:

FieldRequiredDescription
rulenoThe requested guardrail rule.
{"signatures":{"go":"c.Guardrails().GetGuardrailRule(ctx, \u0026guardrailsv1.GetGuardrailRuleRequest{...})","python":"client.guardrails.getguardrailrule(...)","typescript":"client.guardrails.getguardrailrule({...})","cli":"tare api guardrails rules get --guardrail-id $GUARDRAIL_ID \u003cid\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H.../rules/01H...\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails rules get --guardrail-id $GUARDRAIL_ID \u003cid\u003e","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H.../rules/01H...\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails/{guardrail_id}/rules/{id}","slug":"get-a-guardrail-rule"}

Create a guardrail rule

Changed in v0.11.0 (new features): Guardrail providers can be Amazon Bedrock Guardrails (kind bedrockguardrails): set region, optional endpoint and timeout, store a static AWS key pair with aws_credentials, and bind each rule to a guardrail ID and version. A rule bound to DRAFT returns a warning. Rule writes are refused while a data plane serving the rule's project, or for an organization-scope rule any data plane in the organization, runs a build older than 0.11.0 or has not reported a readable build.

Changed in v0.7.0 (behaviour changes): A project owner now creates, edits and deletes the guardrails and guardrail rules of a project they own, without an organization-wide guardrails credential. The shared pattern library and the external guardrail providers stay administrator-only, and a project owner still reaches no other project's guardrails.

Changed in v0.7.0 (new features): You can now author a guardrail, a guardrail rule, a guardrail provider and a pattern for a whole organization rather than one project at a time. Send scope GUARDRAIL_SCOPE_ORGANIZATION with no project_id, and every project of that customer is served the rule. A request that names a project behaves exactly as it did, and a request that names both is refused, so a caller that forgot the project cannot write into every project by accident.

Changed in v0.7.0 (new features): Every guardrail rule now carries a reference, a short read-only id such as GR-0142 that the database mints once and never rewrites. The guardrail rule APIs and each trigger evaluation return it beside the display_name, and the data plane receives it in its runtime configuration, so a platform operator reading a trigger has one id to quote. Existing rules were numbered in creation order, and a blocked caller's refusal body does not carry the reference yet.

Changed in v0.5.0 (behaviour changes): Creating or updating an externalservice guardrail provider now rejects, with INVALID_ARGUMENT, a config the data plane could not use: an unknown key or non-string value, a service_url that is not an absolute http or https URL or names a link-local or metadata address, a blank endpoint or one without a leading slash, a blank timeout or one outside 0-30s, or a ca_cert_pem over 8 KiB, over four certificates or holding anything else. Its rules need a raw.threshold above 0 and at most 1.

Changed in v0.5.0 (behaviour changes): Creating or updating a guardrail rule with the externalService configuration variant now fails with INVALID_ARGUMENT for every provider kind. A stored rule that still carries the variant stays readable and editable as long as the write does not send it back.

Changed in v0.5.0 (deprecations): The externalService variant of a guardrail rule's configuration is deprecated: nothing reads it, and its headers were never sent as HTTP headers. Connection settings and the credential live on the provider; per-check parameters go in the rule's raw configuration, where threshold is the one reserved key.

Changed in v0.4.0 (behaviour changes): Editing a guardrail rule that is bound to a provider outside its own project is now refused. The rule keeps enforcing and reading it is unaffected, but any update that touches its provider, check type or configuration fails until the rule is repointed at a provider its own project owns -- which can be done in the same update call. Only rules the guardrail-provider ownership migration reports as still bound outside their project are affected; it names the count in a notice reading "N non-template rule(s) remain bound outside their own project".

Changed in v0.4.0 (security updates): A guardrail rule can now be bound only to a provider its own project owns. A caller who knew another project's provider id could previously point a rule at it and send that project's traffic through another project's provider and credential, because the binding check asked only whether the provider existed and was usable, never who owned it. The refusal reads the same whether the id names another project's provider, a catalog template or nothing at all, so it cannot be used to discover which provider ids exist. Rules that already reference a provider in their own project are unaffected. No CVE; requires a caller authorized to write guardrails in some project. Rules written before this release are NOT rewritten, and any that the upgrade left bound outside their own project keep enforcing -- and keep exporting the other project's provider configuration, its endpoint and credential reference included, into the referencing project's data-plane configuration. The upgrade reports how many such rules remain: the guardrail-provider ownership migration raises a notice reading "N non-template rule(s) remain bound outside their own project". Repoint each of them at a provider its own project owns; until you do, the next edit to the rule's provider, check type or configuration is refused.

What it does: Adds a new rule to a guardrail. Returns PERMISSION_DENIED when the target guardrail is a template (is_template=true); templates are instantiated via CreateGuardrailFromTemplate, never mutated. A rule on a bedrockguardrails provider is refused with FAILED_PRECONDITION while a data plane serving the rule's scope reports a build older than 0.11.0, a build that cannot be read, or no build. The response's warnings carry non-fatal advisories, such as a binding to a guardrail's DRAFT.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
guardrail_idyesRequired: parent guardrail id (must match rule.guardrail_id when both are set).
ruleyesRequired: rule to create. id must be absent or empty (server-assigned).
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.

Response fields:

FieldRequiredDescription
rulenoThe created rule with server-assigned fields populated.
warningsnoNon-fatal advisories about the rule as stored, such as a Bedrock guardrail bound to its DRAFT version. Empty when there is nothing to report.
{"signatures":{"go":"c.Guardrails().CreateGuardrailRule(ctx, \u0026guardrailsv1.CreateGuardrailRuleRequest{...})","python":"client.guardrails.createguardrailrule(...)","typescript":"client.guardrails.createguardrailrule({...})","cli":"tare api guardrails rules create --guardrail-id $GUARDRAIL_ID --rule $RULE","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H.../rules\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"rule\": {},\n    \"scope\": \"...\"\n  }'"},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails rules create --guardrail-id $GUARDRAIL_ID --rule $RULE","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H.../rules\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"rule\": {},\n    \"scope\": \"...\"\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails/{guardrail_id}/rules","slug":"create-a-guardrail-rule"}

Update a guardrail rule

Changed in v0.11.0 (new features): Guardrail providers can be Amazon Bedrock Guardrails (kind bedrockguardrails): set region, optional endpoint and timeout, store a static AWS key pair with aws_credentials, and bind each rule to a guardrail ID and version. A rule bound to DRAFT returns a warning. Rule writes are refused while a data plane serving the rule's project, or for an organization-scope rule any data plane in the organization, runs a build older than 0.11.0 or has not reported a readable build.

Changed in v0.7.0 (behaviour changes): A project owner now creates, edits and deletes the guardrails and guardrail rules of a project they own, without an organization-wide guardrails credential. The shared pattern library and the external guardrail providers stay administrator-only, and a project owner still reaches no other project's guardrails.

Changed in v0.7.0 (new features): You can now author a guardrail, a guardrail rule, a guardrail provider and a pattern for a whole organization rather than one project at a time. Send scope GUARDRAIL_SCOPE_ORGANIZATION with no project_id, and every project of that customer is served the rule. A request that names a project behaves exactly as it did, and a request that names both is refused, so a caller that forgot the project cannot write into every project by accident.

Changed in v0.7.0 (new features): Every guardrail rule now carries a reference, a short read-only id such as GR-0142 that the database mints once and never rewrites. The guardrail rule APIs and each trigger evaluation return it beside the display_name, and the data plane receives it in its runtime configuration, so a platform operator reading a trigger has one id to quote. Existing rules were numbered in creation order, and a blocked caller's refusal body does not carry the reference yet.

Changed in v0.5.0 (behaviour changes): Creating or updating an externalservice guardrail provider now rejects, with INVALID_ARGUMENT, a config the data plane could not use: an unknown key or non-string value, a service_url that is not an absolute http or https URL or names a link-local or metadata address, a blank endpoint or one without a leading slash, a blank timeout or one outside 0-30s, or a ca_cert_pem over 8 KiB, over four certificates or holding anything else. Its rules need a raw.threshold above 0 and at most 1.

Changed in v0.5.0 (behaviour changes): Creating or updating a guardrail rule with the externalService configuration variant now fails with INVALID_ARGUMENT for every provider kind. A stored rule that still carries the variant stays readable and editable as long as the write does not send it back.

Changed in v0.5.0 (deprecations): The externalService variant of a guardrail rule's configuration is deprecated: nothing reads it, and its headers were never sent as HTTP headers. Connection settings and the credential live on the provider; per-check parameters go in the rule's raw configuration, where threshold is the one reserved key.

Changed in v0.4.0 (behaviour changes): Editing a guardrail rule that is bound to a provider outside its own project is now refused. The rule keeps enforcing and reading it is unaffected, but any update that touches its provider, check type or configuration fails until the rule is repointed at a provider its own project owns -- which can be done in the same update call. Only rules the guardrail-provider ownership migration reports as still bound outside their project are affected; it names the count in a notice reading "N non-template rule(s) remain bound outside their own project".

Changed in v0.4.0 (security updates): A guardrail rule can now be bound only to a provider its own project owns. A caller who knew another project's provider id could previously point a rule at it and send that project's traffic through another project's provider and credential, because the binding check asked only whether the provider existed and was usable, never who owned it. The refusal reads the same whether the id names another project's provider, a catalog template or nothing at all, so it cannot be used to discover which provider ids exist. Rules that already reference a provider in their own project are unaffected. No CVE; requires a caller authorized to write guardrails in some project. Rules written before this release are NOT rewritten, and any that the upgrade left bound outside their own project keep enforcing -- and keep exporting the other project's provider configuration, its endpoint and credential reference included, into the referencing project's data-plane configuration. The upgrade reports how many such rules remain: the guardrail-provider ownership migration raises a notice reading "N non-template rule(s) remain bound outside their own project". Repoint each of them at a provider its own project owns; until you do, the next edit to the rule's provider, check type or configuration is refused.

What it does: Updates mutable rule fields. Omitted/empty scalar string fields leave the existing value untouched. Enum fields set to *_UNSPECIFIED leave the existing value untouched. Absent message fields (triggered_response, configuration) leave the existing value untouched; a present configuration Struct REPLACES the stored configuration wholesale (no deep merge). The rule id must be set inside the rule field. Returns PERMISSION_DENIED when the target rule's parent guardrail is a template (is_template=true); templates are instantiated via CreateGuardrailFromTemplate, never mutated. An update that changes the provider or the configuration of a rule on a bedrockguardrails provider, or turns such a rule on, is refused with FAILED_PRECONDITION while a data plane serving the rule's scope reports a build older than 0.11.0, a build that cannot be read, or no build. The response's warnings carry non-fatal advisories, such as a binding to a guardrail's DRAFT.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
guardrail_idyesRequired: parent guardrail id (must match rule.guardrail_id when both are set).
ruleyesRequired: rule to update. rule.id must be set. Partial update semantics: omitted/empty scalar string fields leave the existing value untouched; enum fields set to *_UNSPECIFIED leave the existing value untouched; absent message fields (triggered_response, configuration) leave the existing value untouched; a present configuration Struct REPLACES the stored configuration wholesale (no deep merge).
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.

Response fields:

FieldRequiredDescription
rulenoThe updated rule with server-assigned fields populated.
warningsnoNon-fatal advisories about the rule as stored, such as a Bedrock guardrail bound to its DRAFT version. Empty when there is nothing to report.
{"signatures":{"go":"c.Guardrails().UpdateGuardrailRule(ctx, \u0026guardrailsv1.UpdateGuardrailRuleRequest{...})","python":"client.guardrails.updateguardrailrule(...)","typescript":"client.guardrails.updateguardrailrule({...})","cli":"tare api guardrails rules update --guardrail-id $GUARDRAIL_ID --rule $RULE","curl":"curl -X PATCH \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H.../rules/01H...\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"rule\": {},\n    \"scope\": \"...\"\n  }'"},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails rules update --guardrail-id $GUARDRAIL_ID --rule $RULE","curl":"curl -X PATCH \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H.../rules/01H...\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"rule\": {},\n    \"scope\": \"...\"\n  }'"},"persona":"Admin","httpVerb":"PATCH","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails/{guardrail_id}/rules/{rule.id}","slug":"update-a-guardrail-rule"}

Delete a guardrail rule

Changed in v0.7.0 (behaviour changes): A project owner now creates, edits and deletes the guardrails and guardrail rules of a project they own, without an organization-wide guardrails credential. The shared pattern library and the external guardrail providers stay administrator-only, and a project owner still reaches no other project's guardrails.

What it does: Soft-deletes a rule (sets deleted_at; the row disappears from List/Get). Historical trigger records referencing this rule REMAIN queryable. Returns NOT_FOUND when the rule does not exist under the given guardrail_id. Returns PERMISSION_DENIED when the target rule's parent guardrail is a template (is_template=true); templates are instantiated via CreateGuardrailFromTemplate, never mutated.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; defaults to caller's session project when empty.
guardrail_idyesRequired: parent guardrail id.
idyesRequired: uuid of the rule to delete.
scopenoThe level this request names (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT, so a client written before #9666 behaves exactly as it did. GUARDRAIL_SCOPE_ORGANIZATION requires project_id to be empty. An HTTP/JSON caller uses the binding with no /projects/{project_id} segment, because the project path requires that segment, and names the scope explicitly on top of it: scope=GUARDRAIL_SCOPE_ORGANIZATION as a query parameter on a GET or DELETE, and as a body field on a POST or PATCH. The binding fills customer_id only, so a request on it that leaves scope unset is refused as a project request naming no project.
{"signatures":{"go":"c.Guardrails().DeleteGuardrailRule(ctx, \u0026guardrailsv1.DeleteGuardrailRuleRequest{...})","python":"client.guardrails.deleteguardrailrule(...)","typescript":"client.guardrails.deleteguardrailrule({...})","cli":"tare api guardrails rules delete --guardrail-id $GUARDRAIL_ID \u003cid\u003e","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H.../rules/01H...\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails rules delete --guardrail-id $GUARDRAIL_ID \u003cid\u003e","curl":"curl -X DELETE \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails/01H.../rules/01H...\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"DELETE","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails/{guardrail_id}/rules/{id}","slug":"delete-a-guardrail-rule"}

Get a project's effective guardrail stack

Changed in v0.7.0 (new features): A project's effective guardrail stack now reads as one list carrying both levels. A rule set at the organization and a rule set at the project that check the same thing at the same stage are marked as duplicating each other rather than one being merged away, because the data plane evaluates both. Rules that are present and switched off stay on the list, marked unenforced.

What it does: Returns every rule a project is served under, at both levels, in one list (tetrateio/fraser#9666). The two levels are not merged. A rule set at the organization and a rule set at the project that check the same thing at the same stage are two rules and the data plane evaluates both, so each is marked as duplicating the other rather than one of them being folded away. A data plane's own network controls are absent from the answer. A data plane carries no guardrails, and what it does carry is mirrored read-only.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idyesRequired: the project whose stack this is. The stack is a project reading: the organization's own rules are the outer half of it, and asking for the organization alone is ListGuardrails at that scope.

Response fields:

FieldRequiredDescription
rulesnoEvery rule the project is served under, organization rules first and each level ordered by guardrail and then by rule, so two reads of an unchanged stack render identically.
organization_rulesnoHow many of them were set at each level, so a console can label the two halves without counting the list itself.
project_rulesnoundocumented
duplicated_rulesnoHow many rules have at least one duplicate on the stack.
{"signatures":{"go":"c.Guardrails().GetEffectiveStack(ctx, \u0026guardrailsv1.GetEffectiveStackRequest{...})","python":"client.guardrails.geteffectivestack(...)","typescript":"client.guardrails.geteffectivestack({...})","cli":"tare api guardrails effective-stack --project-id $PROJECT_ID","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails:effectiveStack\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails effective-stack --project-id $PROJECT_ID","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../projects/proj_01H.../guardrails:effectiveStack\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/customers/{customer_id}/projects/{project_id}/guardrails:effectiveStack","slug":"get-a-projects-effective-guardrail-stack"}

Count what a guardrail apply reconverges

Changed in v0.7.0 (new features): A guardrail apply can now state what it reconverges before you confirm it: how many projects it changes, how many distinct data planes re-pull, and how many of those projects are served nowhere.

What it does: Counts what an apply reconverges, before the operator confirms it (tetrateio/fraser#9666). An organization-level apply names no project and reaches every one of them, so it is counted in projects and in data planes: every data plane labelled with any of those projects re-pulls at once.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; empty at the organization.
scopenoThe level the apply is at (tetrateio/fraser#9666). Unspecified reads as GUARDRAIL_SCOPE_PROJECT.

Response fields:

FieldRequiredDescription
blast_radiusnoundocumented
{"signatures":{"go":"c.Guardrails().GetBlastRadius(ctx, \u0026guardrailsv1.GetBlastRadiusRequest{...})","python":"client.guardrails.getblastradius(...)","typescript":"client.guardrails.getblastradius({...})","cli":"tare api guardrails blast-radius","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../guardrails:blastRadius\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails blast-radius","curl":"curl \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../guardrails:blastRadius\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\""},"persona":"Admin","httpVerb":"GET","httpPath":"/v1/customers/{customer_id}/guardrails:blastRadius","slug":"count-what-a-guardrail-apply-reconverges"}

Test a candidate rule against a project's stack

Changed in v0.7.0 (new features): A candidate guardrail rule can now be tested against a project's stack before it takes traffic. The answer names the checks it duplicates, whether it blocks what another rule only redacts, whether its provider is one its scope may bind and is switched on, whether the patterns it names resolve, and whether a data plane would evaluate it at all. Nothing is stored and no traffic is sent.

What it does: Evaluates a candidate rule against a project's effective stack and returns what it would run into, without storing it and without sending any traffic (tetrateio/fraser#9666). What it answers is where the rule lands: which checks on the stack it duplicates, whether its provider is one its scope may bind and is switched on, whether the patterns it names resolve, and whether a data plane would evaluate it at all. That turns a rollout into a countdown rather than leaving an operator to find out from a refused request.

Request fields:

FieldRequiredDescription
customer_idnoOwning customer scope; defaults to caller's session customer when empty.
project_idnoOwning project scope; empty at the organization.
scopenoThe level the candidate rule would be set at.
ruleyesRequired: the candidate rule. It is not stored, and an id on it is ignored except to leave the rule it names out of the stack it is tested against, so editing a rule does not report it as duplicating itself. The cli field annotation is what makes protoc-gen-ia-cli emit a protojson flag for it. A REQUIRED message field carrying no such annotation is skipped, so the command would take every scalar flag, send no rule at all and be refused for the one field it exists to carry.
guardrail_idnoThe guardrail the rule would join. Optional: a candidate for a guardrail that does not exist yet is tested as though its guardrail were enabled.
against_project_idnoWhich project's stack to test against. Required at the organization, where the candidate reaches every project and the answer differs per project; ignored at project scope, where project_id already names it.

Response fields:

FieldRequiredDescription
findingsnoWhat the candidate would run into, ordered by the enum. An empty list is the claim that it lands on a stack it does not collide with.
duplicatesnoThe rules already on the stack that evaluate the candidate's check at its stage. Both would run.
unresolved_pattern_namesnoPattern names the candidate references that no pattern row in its scope defines.
would_be_enforcednoWhether a data plane would evaluate the candidate at all: its guardrail is enabled, the rule is active and in enforce mode, and its provider is one this scope may bind and is switched on.
stack_rules_afternoHow many rules the project's stack carries with the candidate on it, so a console can state the before and after without re-reading the stack.
{"signatures":{"go":"c.Guardrails().TestRuleAgainstStack(ctx, \u0026guardrailsv1.TestRuleAgainstStackRequest{...})","python":"client.guardrails.testruleagainststack(...)","typescript":"client.guardrails.testruleagainststack({...})","cli":"tare api guardrails rules test-against-stack --rule $RULE","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../guardrails/rules:testAgainstStack\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"project_id\": \"...\",\n    \"scope\": \"...\",\n    \"rule\": {},\n    \"guardrail_id\": \"...\",\n    \"against_project_id\": \"...\"\n  }'"},"examples":{"go":"// No Go SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","python":"# No Python SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","typescript":"// No TypeScript SDK wrapper for GuardrailsService yet -- use the CLI or curl example below.","cli":"tare api guardrails rules test-against-stack --rule $RULE","curl":"curl -X POST \"${AGENTROUTER_BASE_URL}/v1/customers/cust_01H.../guardrails/rules:testAgainstStack\" \\\n  -H \"Authorization: Bearer ak-${AGENTROUTER_API_KEY}\" \\\n  -H \"Content-Type: application/json\" \\\n  -d '{\n    \"project_id\": \"...\",\n    \"scope\": \"...\",\n    \"rule\": {},\n    \"guardrail_id\": \"...\",\n    \"against_project_id\": \"...\"\n  }'"},"persona":"Admin","httpVerb":"POST","httpPath":"/v1/customers/{customer_id}/guardrails/rules:testAgainstStack","slug":"test-a-candidate-rule-against-a-projects-stack"}