Documentation
OBVELO is a pseudonymisation gateway between your application and your model provider. It replaces every personal value with a token, forwards the request, and restores the real values in the response. Nothing from request content is stored.
Quickstart
- Create an account at https://app.obvelo.com — the free plan needs no card — and create a key in your panel.
- Point your client's base URL at the gateway and add your OBVELO key as a header. Your provider key stays where it is.
- Send requests as usual. The response comes back with the real values in place.
python
from openai import OpenAI client = OpenAI( base_url="https://api.obvelo.com/v1", api_key=os.environ["OPENAI_API_KEY"], default_headers={"x-api-key": os.environ["OBVELO_KEY"]}, )
typescript
const client = new OpenAI({ baseURL: "https://api.obvelo.com/v1", apiKey: process.env.OPENAI_API_KEY, defaultHeaders: { "x-api-key": process.env.OBVELO_KEY }, });
Authentication
Every request carries two credentials, and they go to different places. Your OBVELO key travels in x-api-key and is checked by the gateway. Your model provider's credential — Authorization, api-key for Azure, x-goog-api-key for Gemini — passes through to the provider unchanged. We never call a model on our own contract.
x-api-key: $OBVELO_KEY Authorization: Bearer $OPENAI_API_KEY
A key represents a system, not a person — give each application its own key, and each key its own rule profile.
Anthropic's own SDK already sends its key in x-api-key. A separate header name for the OBVELO key on that surface is being added; until it ships, talk to us before wiring the Anthropic SDK through the gateway.
Endpoints
Keep the request shape you already use. Streaming is supported, and every surface returns the same masking result.
| path | shape |
|---|---|
| /v1/chat/completions | OpenAI Chat, Mistral, compatible gateways |
| /v1/responses | OpenAI Responses |
| /v1/messages | Anthropic Messages |
| /v1beta/models/{model}:generateContent | Gemini |
An account is served in the chat completions shape by default. The Responses, Messages and Gemini shapes are served to accounts that set their own provider address, which is part of the own-provider entitlement — see Limits.
How masking works
Each detected value is replaced by a token: its category, a namespace that belongs to your session, and a number. One person keeps one token across the message, tool calls and attachments — in every inflected form — so the model can still follow who did what.
Anna Kowalska → [Person AG26KCY5GDJM76NS73FA6RKFNP-1] 44051401359 → [PESEL AG26KCY5GDJM76NS73FA6RKFNP-1] Kowalska → [Person AG26KCY5GDJM76NS73FA6RKFNP-1]
The namespace is what keeps two sessions from both holding a [Person 1] — the defect that puts one customer's name into another customer's letter. Detection combines 475 rules across 18 categories with a name model. Structural values — thread ids, cursors, record ids — are never masked, so agent loops keep working. This is pseudonymisation: the mapping travels with the request and stays with you.
SDKs
A TypeScript and a Python SDK, streaming included. Both wrap the OpenAI client; Python also wraps the Anthropic client, and TypeScript wraps LangChain chat models. You can also call the endpoints directly with the headers above.
The SDK only restores values. Masking always happens on the server, so every fix reaches every client at once — a restore bug shows a token, a mask bug would show a person.
The SDKs are not on public package registries yet. Ask us at office@obvelo.com and we will send you the current build.
Agents and MCP
Tool-call arguments leave masked and are restored before they reach your system — a search for a tokenised name still finds the right record.
The MCP bridge sits in front of your own MCP server and applies your account's tool allowlist before any call goes out; a tool that is not on the list is not even shown to the model. Agents are part of every paid plan.
Attachments
Files inside the request are masked with the same engine and token state as the surrounding text, then rebuilt so they still open — including authors, comments, reviewer lists, slide masters, chart labels and named ranges. The format is recognised from the content, never from the file name.
masked
docx · xlsx · pptx · csv · txt
not masked — reported
pdf · scans · provider file ids
When a file cannot be masked, the response says so and why. What happens next is your account's policy: block the request, flag it and pass it on (the default), pass it silently, or mask what can be masked. Changing it from the default is done on request today.
Custom lists and formats
Send your own dictionary with the request: people, organisations, labelled values of your own, and words that must never be masked. Up to 20 000 entries, used for that request and stored nowhere; a longer list is cut at the limit, and the response says so. One SDK call turns a CRM or ERP export into a dictionary.
Custom formats — such as internal client codes — are available from the Starter plan.
Rule profiles
Each key carries its own profile: jurisdictions and a selection of rules. The profile belongs to the credential, not to a request header, so a caller cannot switch it — a profile a caller can name is a profile any caller can claim.
Limits
Plans are billed by characters. The hard cap is on by default — traffic stops rather than producing an unexpected invoice. Detection quality is identical on every plan. No plan limits the number of people on an account.
| plan | characters / mo | req / min | keys |
|---|---|---|---|
| free | 1 M | 60 | 1 |
| starter | 10 M | 300 | 3 |
| standard | 50 M | 600 | 3 |
| pro | 200 M | 3 000 | unlimited |
| enterprise | 1 B | 12 000 | unlimited |
Data handling
The data plane stores nothing from request content; the control plane stores the account, the plan and usage in numbers, and never sees content. Hosting and every sub-processor are in the EU or EEA.
If the control plane is unavailable, the gateway keeps serving existing customers from its last snapshot; only new registrations wait.
Ready to try it?