Ruby

The official openai gem takes both settings in its constructor. You change base_url and api_key, and the rest of your code — models, messages, tools, streaming — is untouched. The provider key stays in the vault; this process only carries the Vulnetix API key.

Install

bundle add openai

Configure

require "openai"

org = ENV.fetch("VULNETIX_ORG_UUID")

client = OpenAI::Client.new(
  base_url: "https://guardrails.vulnetix.com/openai/#{org}/v1",
  api_key: ENV.fetch("VULNETIX_API_KEY") # Vulnetix key, not an OpenAI key
)

chat = client.chat.completions.create(
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "Hello" }]
)

puts chat.choices[0].message.content

Swap openai in the path for any other provider slug and pass that provider’s own model string — the gateway forwards model verbatim.

Streaming

Unchanged. The gateway relays the provider’s SSE stream.

stream = client.chat.completions.stream_raw(
  model: "gpt-4o-mini",
  messages: [{ role: "user", content: "Hello" }]
)

stream.each do |chunk|
  print chunk.choices[0].delta.content
end

Handling a guardrail block

A request your policy refuses never reaches the provider. It returns an OpenAI-shaped 403, which the gem raises as OpenAI::Errors::PermissionDeniedError.

begin
  chat = client.chat.completions.create(
    model: "gpt-4o-mini",
    messages: [{ role: "user", content: prompt }]
  )
rescue OpenAI::Errors::PermissionDeniedError => e # HTTP 403
  error = e.body.is_a?(Hash) ? (e.body["error"] || e.body) : {}
  code  = error["code"]         # request_blocked, model_denied, ...
  rule  = error["blocked_by"]   # the rule that stopped it

  raise "AI firewall blocked this request (rule: #{rule})" if code == "request_blocked"
  raise
end

code is what tells your organisation refused this apart from the provider refused this:

codeMeaning
request_blockedA content guardrail matched. blocked_by names the rule; violations lists every rule that matched.
provider_deniedPolicy does not allow this provider.
model_denied / model_not_allowedPolicy does not allow this model.
provider_key_missingNo provider key in the vault for this provider.

Every HTTP failure is a subclass of OpenAI::Errors::APIError, so rescue that if you want a single handler; PermissionDeniedError is its 403 case.

Note Redaction is silent — nothing is raised. A pii_redact rule rewrites the matched spans in your prompt to the literal [REDACTED] and forwards the request. You get an ordinary completion back, about redacted text.

Tool / function calling

Tools are forwarded untouched — the tools you pass and the tool calls you get back are the provider’s own. Guardrails inspect the request you send, which includes the tool-result messages you append before the next call.

Gotchas

  • api_key: is your Vulnetix key. An OpenAI key here is the most common 401.
  • There are two widely used gems with similar names. This page uses the official openai gem (OpenAI::Client.new(api_key:, base_url:)). If your project uses the community ruby-openai gem instead, the equivalent knobs are OpenAI::Client.new(access_token:, uri_base:) — same two settings under different names. That gem appends the API version itself, so give uri_base the URL without /v1; what matters either way is that the request lands on …/{orgUuid}/v1/chat/completions.
  • base_url: must include the /v1 suffix.