Transparent Proxy Path Attribution
For: Developers configuring AI SDK base URLs to route through Axemere Gateway.
Developer Guides | Developer Integration | Identity & Attribution
In transparent proxy mode your AI SDK is pointed at the gateway by changing one environment variable or config parameter. No application code changes are required. This guide covers how to encode workload_id, project_id, account_id, and customer_id directly in that base URL so attribution flows automatically on every request: no per-request headers, no middleware.
Table of Contents
- URL format
- Label reference
- Character constraints
- SDK examples
- Common patterns
- Priority waterfall
- Error responses
- Limitations
- See also
URL format
/proxy/{provider}[/w/{workload_id}][/p/{project_id}][/a/{account_id}][/c/{customer_id}]/{upstream_path}
The labeled segments are optional and order-independent. All of the following are equivalent and valid:
# Workload + project (typical deployment) http://gateway:7080/proxy/anthropic/w/wl-chatbot-prod/p/proj-q3/ # Workload + customer (SaaS) http://gateway:7080/proxy/anthropic/w/wl-saas-backend/c/cust-tenant-abc/ # All four fields http://gateway:7080/proxy/anthropic/w/wl-chatbot/p/proj-abc/a/acct-eng/c/cust-xyz/ # Order-independent — same result as above http://gateway:7080/proxy/anthropic/c/cust-xyz/w/wl-chatbot/a/acct-eng/p/proj-abc/ # No attribution fields — existing form, unchanged http://gateway:7080/proxy/anthropic/
The gateway strips the prefix and all labeled segments before forwarding. The upstream AI provider sees only the clean API path (/v1/messages, /v1/chat/completions, etc.); it never sees the gateway path or attribution fields.
Label reference
| Prefix | Field | Validated against registry? |
|---|---|---|
w/ | workload_id | Yes: 403 if workload not registered |
p/ | project_id | No: attribution only |
a/ | account_id | No: attribution only |
c/ | customer_id | No: attribution only |
org_id is not path-encodable; it is always set by the gateway operator via MVGC_ORG_ID or managed mode config.
labels are not path-encodable; a map cannot be encoded as a single path segment. Set labels via default_attribution.labels on the workload, or use explicit mode for per-request label granularity.
Character constraints
Path-encoded values must satisfy:
- Allowed characters: alphanumeric,
-,_,. - Prohibited:
/,?,#,%(unencoded), space - Maximum length: 128 characters per value
UUIDs, kebab-case-slugs, and snake_case_ids all satisfy these constraints.
Note on
customer_id: These values appear in HTTP access logs, CDN logs, and distributed traces. Use opaque identifiers (UUIDs, hashed IDs) rather than human-readable business names if log exposure is a concern.
SDK examples
Anthropic Python SDK
import anthropic client = anthropic.Anthropic( base_url="http://gateway:7080/proxy/anthropic/w/wl-chatbot-prod/p/proj-q3", api_key="any-value", # real key is held by the gateway ) # All requests from this client are attributed to wl-chatbot-prod / proj-q3 automatically message = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, messages=[{"role": "user", "content": "Hello"}], )
OpenAI Python SDK
Note —
/v1in base_url: The OpenAI Python SDK appends endpoint paths such as/chat/completions(no/v1prefix) tobase_url. Include/v1in the base_url after your attribution labels so the upstream path forwarded toapi.openai.comis/v1/chat/completions. The Anthropic SDK includes/v1in its own endpoint paths, so no/v1is needed in the Anthropic base_url.
from openai import OpenAI client = OpenAI( base_url="http://gateway:7080/proxy/openai/w/wl-analytics/p/proj-data/v1", api_key="any-value", ) response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "Summarize this data"}], )
Any SDK via environment variable
Most AI SDKs respect a *_BASE_URL environment variable. Set it once at deployment time, no application code changes:
# Anthropic — SDK appends /v1/messages; no /v1 needed in base URL export ANTHROPIC_BASE_URL=http://gateway:7080/proxy/anthropic/w/wl-chatbot-prod/p/proj-q3 # OpenAI — SDK appends /chat/completions; /v1 must be in the base URL export OPENAI_BASE_URL=http://gateway:7080/proxy/openai/w/wl-analytics/p/proj-data/v1 # Cohere export CO_API_URL=http://gateway:7080/proxy/cohere/w/wl-embed-service/p/proj-search # Google Gemini export GOOGLE_API_BASE_URL=http://gateway:7080/proxy/gemini/w/wl-vision/p/proj-classify
Common patterns
Multiple workloads, one gateway
Run several services through a single gateway instance. Each service sets its own base URL; the gateway routes requests to the right policy context automatically.
# Service A: customer chatbot ANTHROPIC_BASE_URL=http://gateway:7080/proxy/anthropic/w/wl-chatbot-prod/p/proj-chatbot # Service B: internal analytics pipeline (OpenAI — /v1 required in base URL) OPENAI_BASE_URL=http://gateway:7080/proxy/openai/w/wl-analytics/p/proj-analytics/v1 # Service C: CI summarizer (staging) ANTHROPIC_BASE_URL=http://gateway:7080/proxy/anthropic/w/wl-ci-summarizer/p/proj-ci
Each service's requests are governed by its own workload policy. Spend reports show per-workload and per-project breakdowns with no application code involvement.
Per-customer SaaS
Construct one client instance per customer, encoding customer_id in the base URL. No middleware, no per-request header injection, no changes to the code that calls client.messages.create(...).
import anthropic def make_client(customer_id: str) -> anthropic.Anthropic: """Gateway-backed Anthropic client scoped to a specific customer.""" return anthropic.Anthropic( base_url=f"http://gateway:7080/proxy/anthropic/w/wl-saas-backend/c/{customer_id}", api_key="any-value", ) # Each customer gets their own client — attribution is automatic acme_client = make_client("cust-acme-corp") globex_client = make_client("cust-globex-ind") # These calls are attributed to different customer_ids automatically acme_client.messages.create(...) globex_client.messages.create(...)
Spend reports group by customer_id, giving you per-customer cost breakdowns without any server-side attribution logic.
Multi-workload SaaS
Combine workload_id and customer_id in the path when you have multiple services and multiple customers.
def make_client(service: str, customer_id: str) -> anthropic.Anthropic: workload = f"wl-{service}-prod" return anthropic.Anthropic( base_url=f"http://gateway:7080/proxy/anthropic/w/{workload}/c/{customer_id}", api_key="any-value", ) chat_client_acme = make_client("chat", "cust-acme") search_client_acme = make_client("search", "cust-acme") chat_client_globex = make_client("chat", "cust-globex")
Each combination applies the policy for that workload (which may differ between services) and attributes cost to that customer.
Agent framework: per-workflow cost tracking
Encode project_id with the workflow identifier so the audit log and spend reports can show total cost per workflow run.
import anthropic def make_workflow_client(workflow_id: str) -> anthropic.Anthropic: """Client for a single workflow run — all steps share the same project_id.""" return anthropic.Anthropic( base_url=f"http://gateway:7080/proxy/anthropic/w/wl-agents/p/proj-wf-{workflow_id}", api_key="any-value", ) client = make_workflow_client("run-20240407-abc123") # All calls within this workflow are attributed to proj-wf-run-20240407-abc123 plan = client.messages.create(model="claude-3-5-sonnet-20241022", ...) code = client.messages.create(model="claude-3-5-sonnet-20241022", ...) review = client.messages.create(model="claude-3-5-sonnet-20241022", ...)
Spend reports show total cost per workflow. Budget caps on project_id let you set per-run spending limits.
Per-step label granularity: If you need to distinguish planner cost from coder cost in the audit log (e.g. via
labels: {agent_role: planner}), path encoding cannot do this; labels are not path-encodable. Use explicit mode for per-step attribution. Path encoding gives you per-workflow aggregation; explicit mode gives you per-step breakdown.
Per-request override via header
Path-encoded values set the deployment-level baseline. Any X-MVGC-* header on an individual request overrides the path value for that request only; useful for testing, debugging, or middleware that needs to add per-request context on top of a static base URL.
import anthropic import httpx # Client has workload + project encoded in base URL client = anthropic.Anthropic( base_url="http://gateway:7080/proxy/anthropic/w/wl-chatbot-prod/p/proj-q3", api_key="any-value", http_client=httpx.Client( headers={ # Override project_id for this specific request "X-MVGC-Project-ID": "proj-special-initiative", } ), )
Important: Overriding
X-MVGC-Workload-IDvia header changes more than just one field; it loads a completely different workload config, which means a different policy bundle and differentdefault_attribution. Use workload header overrides intentionally.
The full priority order for each field is:
X-MVGC-* header > path segment > workload default_attribution > gateway config default
Priority waterfall
| Field | Priority 1 | Priority 2 | Priority 3 | Priority 4 |
|---|---|---|---|---|
workload_id | X-MVGC-Workload-ID header | w/ path segment | MVGC_DEFAULT_WORKLOAD_ID config | — (400 if empty) |
project_id | X-MVGC-Project-ID header | p/ path segment | workload default_attribution.project_id | empty |
account_id | X-MVGC-Account-ID header | a/ path segment | workload default_attribution.account_id | empty |
customer_id | X-MVGC-Customer-ID header | c/ path segment | workload default_attribution.customer_id | empty |
Error responses
| Condition | HTTP status | Error message |
|---|---|---|
| Unknown provider | 400 | unknown provider "xyz"; supported: anthropic, openai, ... |
| Label prefix present but no value | 400 | proxy path attribute w has no value |
| Value contains invalid characters | 400 | proxy path attribute c contains invalid characters |
| Value exceeds 128 characters | 400 | proxy path attribute p exceeds maximum length |
workload_id not registered | 403 | existing workload not found response |
org_id or workload_id not resolvable | 400 | proxy requires org_id and workload_id; set via request header, base URL path, or gateway config |
Limitations
labelsare not path-encodable (map type). Set viadefault_attribution.labelson the workload, or use explicit mode for per-request label values.org_idis not path-encodable; it is operator-set via gateway config and cannot be overridden by callers.- Full MITM proxy mode does not support path encoding. In CONNECT tunneling the gateway sees only the destination host, not the URL path. Attribution in MITM mode is limited to gateway config and workload defaults.
- Percent-encoded values (e.g.
wl-chat%2Fprod) are not supported. IDs must be path-safe without encoding; use-or_as separators.
See also
- Identity & Attribution: full field reference and all ingress modes
- Workloads: registering workloads and configuring
default_attribution - Developer Integration Guide: explicit mode, streaming, delegation tokens
- Glossary