What we store, and what we don't

The honesty brand's natural flex: the exact field list, not a promise.

We sit inline on your traffic, so a privacy claim you cannot check is worth nothing. So here is the precise, body-free column list our metering path writes, lifted from the code that writes it, plus exactly how the gateway behaves when it faults. Read it, then verify it with one curl.

What we store

One body-free row per request. These columns, nothing else.

This is the whole metered record. It is metadata about the call, never the call's content. We never store prompt or response bodies, and we never train on your traffic.

request_events · the body-free metering rowsource: internal/metering/writer.go
Identifiers we mintRandom ids we generate. None is derived from your content.
request_idRandom id for this request (also returned as x-request-id).
trace_idRandom correlation id (also returned as x-recovea-trace-id).
Your own attribution, resolved from your keyYour workspace structure, not your traffic. The tags are strings you send, never request content.
customer_idWhich workspace (resolved from the rcv_ key you authenticated with).
project_idWhich project, when the key has one. NULL otherwise.
team_idWhich team, derived in SQL from the key. NULL otherwise.
rcv_key_idWhich of your Recovea keys made the call.
route_idThe route (path + first-seen model) this call belongs to.
featureBody-free tag you set with X-Recovea-Feature. NULL when absent.
end_user_idBody-free tag you set with X-Recovea-Customer. NULL when absent.
Model + token countsCounts only, read from the provider's usage field. NULL when the provider reports none, never a fabricated zero.
baseline_modelThe model you asked for.
realized_modelThe model that actually served.
input_tokensusage.prompt_tokens.
output_tokensusage.completion_tokens.
cached_tokensusage.prompt_tokens_details.cached_tokens.
reasoning_tokensusage.completion_tokens_details.reasoning_tokens.
What ran, and how it turned outFlags about our own optimization layer and the request outcome.
levers_firedWhich optimizations ran, with their outcome (hit / miss / skip / error).
cache_hit · cache_typeWhether an exact-cache hit served, and which kind.
dedup_singleflightWhether identical concurrent requests coalesced onto one upstream call.
finish_reasonThe provider's terminal reason (stop, length, tool_calls).
successAttested or cleanly-derived outcome. NULL when ambiguous, never a fabricated false.
fell_back_to_baselineWhether fail-open fired and the request served untouched.
statusThe HTTP status class (success / error).
Timing + surfaceHow long it took and where it was served.
latency_total_msEnd-to-end time.
gateway_overhead_msOur added time, alarmed separately.
providerThe serving provider (openai / anthropic / openrouter).
envlive or test.
tsWhen the request happened.

What never lands in that row

  • Prompt messages or system prompts
  • Response or completion text
  • Tool-call arguments or tool outputs
  • Embedding input text
  • Your provider key in plaintext (encrypted at rest; decrypted in memory per request; never logged)
  • A dollar cost on the row (baseline_cost / realized_cost are always NULL here; USD is priced later at rollup)

The one scoped exception, stated up front

Quality evaluation is the only place a response body is ever reassembled, and it is opt-in. When you turn on shadow evaluation, a copy of the model output is held in memory to score non-inferiority off the hot path. It is never written to the metering row above, and it is off by default. We would rather name our own exception than have you find it.

Your provider key is encrypted at rest (AES-256-GCM), decrypted only in memory for the lifetime of one request, and never logged or written to the database.

Fail-open, and the asymmetry

If we are down or slow, your request goes straight to your provider.

A fault in any Recovea-added optimization or metering layer never becomes your 5xx. The request falls through to your provider, on your own key, on the baseline model. We publish no uptime percentage and no SLA. This is an architectural property, not a number we ask you to trust.

your app
recovea gateway
authfail-closed
spend capfail-closed-hybrid
levers + meterfail-open
fault in levers / meter -> fall through to provider, baseline model, untouched
your provideryour key
layer · what happens when it faultsdirection
Optimization levers (cache, dedup, routing, slim)
Any error, panic, or timeout inside our layer is recorded and the request continues to your provider on the baseline model, untouched.
fail-open
Metering / ledger write
Fully async, off the hot path. A database outage or a full buffer drops the event (counted), and never fails or slows your request.
fail-open
Upstream provider unreachable
We return the provider's own server-error shape (502), increment gateway_fail_open_total, and page ourselves. Your bytes are never silently swallowed.
passthrough
Auth (rcv_ key resolution) + tenant isolation
Missing or wrong key returns 401. If the auth store itself is down, you get an honest 503, never a served-unauthenticated request and never a misleading invalid key.
fail-closed
Hard spend cap (your kill-switch)
No enabled cap means nothing to enforce, so we serve. With an enabled cap whose state we cannot confirm, we run one bounded check and block (402 budget_unverifiable) rather than leak spend past a wall we cannot see. Blast radius: only workspaces that set a cap.
fail-closed-hybrid

Said in the same breath: fail-open does not unbuckle your seatbelt

The layers that ADD value (optimization, metering) fail open. The layers that PROTECT you (auth, tenant isolation, your spend cap) fail closed. You cannot trigger a fault to make your cap evaporate: an unconfirmable cap blocks rather than leaks, and auth refuses rather than serves. Visibility may briefly gap during an outage; enforcement does not. Every fall-through increments an internal counter (gateway_fail_open_total) that pages us, so we watch our own fallbacks instead of hoping they are rare.

Don't trust us. Check us.

Three things on this page are falsifiable, not asserted.

The field list

The columns above are the whole metering row. Run a call, request a copy of what we recorded for it by its x-request-id, and you will get back exactly these fields and no body.

The per-request receipt

Every billed request returns a recovea-receipt-v1 you can re-derive yourself, client-side, with no server trust, hash-chained into the ledger as recovea-chain-v1.

The passthrough

Confirm the endpoint is live and that switching is a one-line base_url change you can revert instantly, with no account. Run the curl quickstart.

On the roadmap, not shipped, and we will not pretend otherwise

  • PlannedSelf-host in your own VPC, where payloads never leave your network. Planned for the enterprise roadmap, not available today.
  • PlannedPer-tenant KMS envelope encryption with a customer-supplied key that revokes our access. Planned; today the key is encrypted at rest in our secrets store.
  • PlannedAn open-source receipt verifier so you can re-derive a receipt with our code, not just our word. Planned.
  • PlannedSOC 2 is on the roadmap. We are not certified yet and we say so.

Security contact and disclosure policy: /.well-known/security.txt. The broader posture lives on Security & trust.

Read the field list. Run the curl. Then decide.

No prompt bodies stored, no training on your traffic, fail-open by design, and a spend cap that fails closed. All of it checkable.