Guide

Audit Proxy

Capture a full audit log of every AI call — across any provider — with a one-line change to your existing code. Your provider keys stay yours; Flint never stores them.

How it works

Route your SDK through the Flint proxy by changing baseURL. Every request is forwarded verbatim to the real provider using the key you pass in x-provider-key. That key is used for the outbound call and immediately discarded — it is never written to a database, log, or any storage layer.

# Request flow
Your app
│ Authorization: Bearer <FLINT_API_KEY>
│ x-provider-key: <YOUR_PROVIDER_KEY>
api.flintlogic.com/v1/proxy/{provider}/{path}
│ key forwarded → provider
│ response returned unchanged
│ audit record written async
Your app + audit log entry
Your provider key is never stored. Only a hint of the last 4 characters (e.g. ...k3f2) is saved in the audit record for reference.

Setup

1. Get a Flint API key

Go to Settings → API Keys → Create key. Keys look like flnt_live_....

2. Change one line in your SDK

import OpenAI from 'openai';

const openai = new OpenAI({
  apiKey:         process.env.OPENAI_API_KEY,   // your key — forwarded, never stored
  baseURL:        'https://api.flintlogic.com/v1/proxy/openai',
  defaultHeaders: {
    'Authorization':  `Bearer ${process.env.FLINT_API_KEY}`,
    'x-provider-key': process.env.OPENAI_API_KEY,
  },
});

// Everything else stays exactly the same
const response = await openai.chat.completions.create({
  model:    'gpt-4o',
  messages: [{ role: 'user', content: 'Summarise this contract in 3 bullets.' }],
});

Client-side mode

If you'd prefer the AI call to stay entirely on your infrastructure — no traffic routed through Flint — make the call directly and report the result afterward via POST /audit/report. Flint receives only the audit data; the provider response never transits Flint's servers.

# Client-side flow
Your app → AI provider (direct, no proxy)
Your app → POST /audit/report (fire-and-forget)
│ Flint extracts tokens + cost
audit record written
import OpenAI from 'openai';

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

async function chat(messages) {
  const start    = Date.now();
  const response = await openai.chat.completions.create({ model: 'gpt-4o', messages });
  const latencyMs = Date.now() - start;

  // Fire-and-forget — does not block your response.
  // Include 'messages' to enable prompt capture (Settings → Data & Privacy).
  fetch('https://api.flintlogic.com/v1/audit/report', {
    method:  'POST',
    headers: {
      'Authorization': `Bearer ${process.env.FLINT_API_KEY}`,
      'Content-Type':  'application/json',
    },
    body: JSON.stringify({
      provider: 'openai', endpoint: '/v1/chat/completions',
      response, latencyMs, status: 200,
      messages,  // required for prompt capture
    }),
  }).catch(() => {});

  return response;
}

Report payload

FieldRequiredDescription
providerYesopenai, anthropic, or gemini
endpointYesProvider API path, e.g. /v1/chat/completions
responseYesRaw provider response object — tokens and model extracted from this
latencyMsNoClient-measured call duration in milliseconds
statusNoHTTP status code returned by the provider (default 200)
messagesNoArray of {role, content} — required for prompt capture to work in client-side mode
systemNoAnthropic system prompt string — included in prompt capture when provided
Client-reported records appear alongside proxy records in your audit log and dashboard. They include a source: "client" field to distinguish the two modes.

Prompt capture

Enable Settings → Data & Privacy → Prompt capture to store prompt previews, SHA-256 hashes, and full prompt/completion content for each request. Off by default — only enable if your prompts contain no PII or confidential data.

ModeWhat you need to do
ProxyNothing — the proxy extracts the prompt automatically from the forwarded request body.
Client-sideInclude messages (and system for Anthropic) in your /v1/audit/report payload. Without messages, the report is still recorded but prompt data is skipped.

When capture is on, each audit record gains additional fields. A 100-character preview and hash are always stored in the audit record. The full prompt and completion are stored in S3 and retrievable via the API.

# Fetch the full prompt + completion stored in S3 for a specific request
curl "https://api.flintlogic.com/v1/audit/prompt/<requestId>" \
  -H "Authorization: Bearer $FLINT_API_KEY"

# Response
{
  "prompt":     "Summarise this contract in 3 bullets.\n\nContract text...",
  "completion": "1. Payment terms are net-30.\n2. ...",
  "provider":   "openai",
  "model":      "gpt-4o"
}
After toggling prompt capture on, allow up to 60 seconds for the change to propagate to all active proxy instances. Changes are not instantaneous due to a short settings cache.

Proxy base URLs

ProviderSet as baseURL
OpenAIhttps://api.flintlogic.com/v1/proxy/openai
Anthropichttps://api.flintlogic.com/v1/proxy/anthropic
Gemini (GenAI SDK)https://api.flintlogic.com/v1/proxy/gemini
Gemini (OpenAI SDK)https://api.flintlogic.com/v1/proxy/gemini/v1beta/openai

Querying your audit log

Retrieve paginated audit records for your org. You can also view them in the Audit tab of this dashboard.

# All calls in May 2025
curl "https://api.flintlogic.com/v1/audit?from=2025-05-01&to=2025-05-31" \
  -H "Authorization: Bearer $FLINT_API_KEY"

# Only OpenAI calls, newest 50
curl "https://api.flintlogic.com/v1/audit?provider=openai&limit=50" \
  -H "Authorization: Bearer $FLINT_API_KEY"

# Paginate with cursor
curl "https://api.flintlogic.com/v1/audit?cursor=eyJwayI6..." \
  -H "Authorization: Bearer $FLINT_API_KEY"

Filter parameters

ParameterTypeDescription
fromISO 8601Start of time range, e.g. 2025-01-01
toISO 8601End of time range
providerstringopenai, anthropic, or gemini
modelstringModel ID, e.g. gpt-4o
limitnumberRecords per page, max 500 (default 100)
cursorstringPagination token from previous response

Response

{
  "records": [
    {
      "requestId":    "abc-123",
      "provider":     "openai",
      "model":        "gpt-4o",
      "endpoint":     "/v1/chat/completions",
      "inputTokens":  512,
      "outputTokens": 128,
      "totalTokens":  640,
      "costUsd":      0.00832,
      "latencyMs":    843,
      "status":       200,
      "keyHint":      "...k3f2",
      "createdAt":    "2025-05-04T14:22:01.000Z"
    }
  ],
  "nextCursor": "eyJwayI6...",
  "count": 1
}

What's captured

FieldDescription
requestIdUnique ID for the call
provideropenai, anthropic, or gemini
modelModel ID extracted from the provider response
endpointProvider API path, e.g. /v1/chat/completions
inputTokensPrompt tokens consumed
outputTokensCompletion tokens generated
totalTokensSum of input + output
costUsdEstimated USD cost based on published pricing
latencyMsTime from proxy receipt to provider response
statusHTTP status returned by the provider
keyHintLast 4 characters of the key used, e.g. ...k3f2
createdAtISO 8601 timestamp
promptPreview(prompt capture) First 100 characters of the prompt
promptHash(prompt capture) SHA-256 hash of the full prompt — use for deduplication
completionPreview(prompt capture) First 100 characters of the completion, proxy mode only
promptS3Key(prompt capture) S3 object key — fetch full content via GET /v1/audit/prompt/:requestId
Audit records are retained for 90 days then automatically deleted. Contact support to arrange extended retention.

Error reference

Error codeStatusMeaning
unknown_provider400Provider in the URL is not supported
missing_provider_key400x-provider-key header is absent
upstream_error502Provider returned an error or timed out
Provider errors (4xx/5xx from OpenAI, Anthropic, etc.) are passed through unchanged. Your existing error handling will continue to work without modification.