.NET / C#

The official OpenAI NuGet package takes an endpoint and a credential. You set the endpoint to the gateway and the credential to your Vulnetix API key; every call you already make stays the same. The provider key lives in the vault, not in your application settings.

Install

dotnet add package OpenAI

Configure

using System.ClientModel;
using OpenAI;
using OpenAI.Chat;

string org = Environment.GetEnvironmentVariable("VULNETIX_ORG_UUID")!;

ChatClient client = new(
    model: "gpt-4o-mini",
    credential: new ApiKeyCredential(Environment.GetEnvironmentVariable("VULNETIX_API_KEY")!),
    options: new OpenAIClientOptions
    {
        Endpoint = new Uri($"https://guardrails.vulnetix.com/openai/{org}/v1")
    });

ChatCompletion completion = client.CompleteChat("Hello");
Console.WriteLine(completion.Content[0].Text);

The credential is your Vulnetix API key, not an OpenAI key. Swap openai in the path for any other provider slug and pass that provider’s own model string — the gateway forwards it verbatim.

Streaming

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

AsyncCollectionResult<StreamingChatCompletionUpdate> updates =
    client.CompleteChatStreamingAsync("Hello");

await foreach (StreamingChatCompletionUpdate update in updates)
{
    if (update.ContentUpdate.Count > 0)
        Console.Write(update.ContentUpdate[0].Text);
}

Handling a guardrail block

A request your policy refuses never reaches the provider. It returns an OpenAI-shaped 403, which the SDK throws as System.ClientModel.ClientResultException. The typed exception does not model Vulnetix’s extra fields, so read them from the raw response.

using System.ClientModel;
using System.Text.Json;

try
{
    ChatCompletion completion = client.CompleteChat(prompt);
}
catch (ClientResultException e) when (e.Status == 403)
{
    using JsonDocument doc = JsonDocument.Parse(e.GetRawResponse()!.Content);
    JsonElement error = doc.RootElement.GetProperty("error");

    string code = error.GetProperty("code").GetString()!;          // request_blocked, ...
    string? rule = error.TryGetProperty("blocked_by", out var b) ? b.GetString() : null;

    throw new InvalidOperationException(
        $"AI firewall refused this request: {code} (rule: {rule ?? "n/a"})", e);
}

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.
Note Redaction is silent — nothing is thrown. A pii_redact rule rewrites the matched spans in your prompt to the literal [REDACTED] and forwards the request. You get an ordinary ChatCompletion back, about redacted text.

Tool / function calling

ChatTool definitions and the tool calls you get back are forwarded untouched. Guardrails inspect the request you send — including the ToolChatMessage results you add to the message list before the next call — so a tool that returns a secret produces a request the firewall can block or redact.

Gotchas

  • The ApiKeyCredential holds your Vulnetix key. A provider key here is the most common 401.
  • Endpoint must include the /v1 suffix.
  • ClientResultException.Status is the HTTP status; GetRawResponse().Content is the untouched body, which is the only place blocked_by and violations survive.
  • Azure OpenAI’s AzureOpenAIClient is a different client with its own endpoint conventions — use the plain OpenAI package shown here when pointing at the gateway.