Gateway Mode
In gateway mode your application keeps its own SDK, request bodies and error handling. The only change is the base URL: it points at a ByteHide route instead of the provider, and the gateway inspects every request and response on the way through.
How It Works
- Your application sends the request to the route URL with a ByteHide credential.
- The gateway runs the route's policy on the request: it blocks it, masks sensitive data in place, or lets it through.
- The gateway forwards the request to the provider with the provider credential stored in the integration.
- The response is inspected on the way back, and the verdict is recorded in Incidences.
The request body is forwarded as it came: tool definitions, images, documents, cache breakpoints, model options and vendor extensions all survive. The gateway only changes what the policy decides, such as a masked value, which is written back exactly where it was read.
Route URLs
Every LLM route is served under your organization's gateway URL:
https://gateway.bytehide.com/YOUR_ORG_ID/YOUR_ROUTEhttps://gateway.bytehide.com/YOUR_ORG_ID/YOUR_ROUTEYou can copy it from the route's Setup button or from the Overview. The route answers on the provider's own paths, so each SDK keeps building the URLs it already builds:
| Your application calls today | Through ByteHide |
|---|---|
OpenAI POST /v1/chat/completions | POST {route}/v1/chat/completions (also {route}/chat/completions and {route}) |
Anthropic POST /v1/messages | POST {route}/v1/messages |
Anthropic POST /v1/messages/count_tokens | POST {route}/v1/messages/count_tokens |
Bedrock POST /model/{id}/converse and /invoke | POST {route}/model/{id}/converse and /invoke |
Bedrock converse-stream and invoke-with-response-stream | The same paths under {route} |
AgentCore POST /runtimes/{arn}/invocations | POST {route}/runtimes/{arn}/invocations |
Vertex AI ...:rawPredict and ...:streamRawPredict | POST {route}/v1/projects/.../models/{m}:rawPredict |
{route} is https://gateway.bytehide.com/YOUR_ORG_ID/YOUR_ROUTE.
AgentCore routes have no chat wire
A route that targets a Bedrock AgentCore Runtime does not answer on /chat/completions. Call it the way you call AgentCore today, with invoke_agent_runtime, as shown below.
Authentication
Send a consumer credential or the organization gateway key in whichever header your SDK already uses:
| Header | Sent by |
|---|---|
Authorization: Bearer ... | OpenAI SDKs, most HTTP clients |
x-api-key | Anthropic SDK |
api-key | Azure OpenAI SDK |
x-gateway-key | Any client |
| SigV4 access key ID | boto3 and the AWS SDKs |
The provider key never leaves ByteHide. Whatever key your SDK used to send to the provider is replaced by the ByteHide credential, and the gateway re-authenticates with the integration's own credential when it forwards the request.
SDK Examples
OpenAI (Python)
from openai import OpenAI
client = OpenAI(
base_url="https://gateway.bytehide.com/YOUR_ORG_ID/support-assistant",
api_key="gwc_live_YOUR_TOKEN",
)
response = client.chat.completions.create(
model="gpt-5.4",
messages=[{"role": "user", "content": "Where is my order 4417?"}],
)
print(response.choices[0].message.content)from openai import OpenAI
client = OpenAI(
base_url="https://gateway.bytehide.com/YOUR_ORG_ID/support-assistant",
api_key="gwc_live_YOUR_TOKEN",
)
response = client.chat.completions.create(
model="gpt-5.4",
messages=[{"role": "user", "content": "Where is my order 4417?"}],
)
print(response.choices[0].message.content)OpenAI (Node.js)
import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://gateway.bytehide.com/YOUR_ORG_ID/support-assistant',
apiKey: 'gwc_live_YOUR_TOKEN',
})
const response = await client.chat.completions.create({
model: 'gpt-5.4',
messages: [{ role: 'user', content: 'Where is my order 4417?' }],
})import OpenAI from 'openai'
const client = new OpenAI({
baseURL: 'https://gateway.bytehide.com/YOUR_ORG_ID/support-assistant',
apiKey: 'gwc_live_YOUR_TOKEN',
})
const response = await client.chat.completions.create({
model: 'gpt-5.4',
messages: [{ role: 'user', content: 'Where is my order 4417?' }],
})Anthropic (Python)
import anthropic
client = anthropic.Anthropic(
base_url="https://gateway.bytehide.com/YOUR_ORG_ID/claude-assistant",
api_key="gwc_live_YOUR_TOKEN",
)
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Summarize this support ticket."}],
)import anthropic
client = anthropic.Anthropic(
base_url="https://gateway.bytehide.com/YOUR_ORG_ID/claude-assistant",
api_key="gwc_live_YOUR_TOKEN",
)
message = client.messages.create(
model="claude-sonnet-4-6",
max_tokens=1024,
messages=[{"role": "user", "content": "Summarize this support ticket."}],
)AnthropicBedrock and AnthropicVertex work the same way against Bedrock and Vertex routes.
AWS Bedrock (boto3)
import boto3
client = boto3.client(
"bedrock-runtime",
region_name="us-east-1",
endpoint_url="https://gateway.bytehide.com/YOUR_ORG_ID/bedrock-assistant",
aws_access_key_id="gwc_live_YOUR_TOKEN",
aws_secret_access_key="unused",
)
response = client.converse(
modelId="YOUR_BEDROCK_MODEL_ID",
messages=[{"role": "user", "content": [{"text": "Draft a reply to this customer."}]}],
)import boto3
client = boto3.client(
"bedrock-runtime",
region_name="us-east-1",
endpoint_url="https://gateway.bytehide.com/YOUR_ORG_ID/bedrock-assistant",
aws_access_key_id="gwc_live_YOUR_TOKEN",
aws_secret_access_key="unused",
)
response = client.converse(
modelId="YOUR_BEDROCK_MODEL_ID",
messages=[{"role": "user", "content": [{"text": "Draft a reply to this customer."}]}],
)The ByteHide credential travels as the access key ID. The gateway discards your signature and signs the forwarded request with the integration's AWS credentials.
Bedrock AgentCore (boto3)
import json
import boto3
client = boto3.client(
"bedrock-agentcore",
region_name="us-east-1",
endpoint_url="https://gateway.bytehide.com/YOUR_ORG_ID/claims-agent",
aws_access_key_id="gwc_live_YOUR_TOKEN",
aws_secret_access_key="unused",
)
client.invoke_agent_runtime(
agentRuntimeArn="YOUR_AGENT_RUNTIME_ARN",
runtimeSessionId=session_id,
payload=json.dumps({"prompt": user_text}).encode(),
)import json
import boto3
client = boto3.client(
"bedrock-agentcore",
region_name="us-east-1",
endpoint_url="https://gateway.bytehide.com/YOUR_ORG_ID/claims-agent",
aws_access_key_id="gwc_live_YOUR_TOKEN",
aws_secret_access_key="unused",
)
client.invoke_agent_runtime(
agentRuntimeArn="YOUR_AGENT_RUNTIME_ARN",
runtimeSessionId=session_id,
payload=json.dumps({"prompt": user_text}).encode(),
)curl
curl https://gateway.bytehide.com/YOUR_ORG_ID/support-assistant/v1/chat/completions \
-H "Authorization: Bearer gwc_live_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.4",
"messages": [{"role": "user", "content": "Where is my order 4417?"}]
}'curl https://gateway.bytehide.com/YOUR_ORG_ID/support-assistant/v1/chat/completions \
-H "Authorization: Bearer gwc_live_YOUR_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"model": "gpt-5.4",
"messages": [{"role": "user", "content": "Where is my order 4417?"}]
}'Responses and Errors
Every answer comes back in the provider's own shapes, so your SDK raises its usual errors.
| Verdict | HTTP status | What your client receives |
|---|---|---|
| Pass | The provider's | The provider's answer, with any masking the policy applied |
| Block (default) | 403 | The provider's error shape, plus a ByteHide envelope with the category |
| Block, route set to Reply as assistant message | 200 | The block as the assistant's answer, in the provider's reply shape |
| Rate or token limit | 429 | A limit was reached. Slow down and retry |
| Guard unavailable | 503 | An outage, not a detection: the x-bytehide-fail-closed: true header is set and no rule is named |
A 503 is not a detection
When the guard cannot run, the gateway fails closed by default and answers 503 instead of letting traffic through uninspected. Treat it as an outage and retry; do not report it as an attack.
Streaming
Each route chooses how streaming answers are guarded, with its Streaming output guard setting:
| Option | Behavior |
|---|---|
| Buffered (default) | The gateway collects the full answer, applies masking and blocking, then sends it as a stream. Full protection, but no real-time tokens |
| Streaming | Tokens flow in real time, inspected frame by frame in the provider's own framing (SSE, or the AWS binary event stream for Bedrock). If the output policy trips mid-stream, the stream is cut and closed the way the protocol expects, so your client never hangs |
See Routes.
Optional Headers
None of these is required. They make the Incidences and Attackers screens answer who did this.
| Header | What it does |
|---|---|
x-user-id | The end user behind the call. Stored on the incidence and filterable. Send a pseudonymous ID rather than an email |
x-conversation-id | The conversation the call belongs to. Groups turns so a multi-turn attack shows as one story |
x-user-ip | The end user's IP address. Recorded (masked to a /24) and geolocated instead of your backend's address |
The response carries x-bytehide-request-id, the correlation ID of the request. Use it to match a client-side failure with its incidence.
Rate limits and the IP allowlist always act on the address that actually opened the connection, so a forged x-user-ip cannot bypass them. Firewall IP and range entries match either address: the connection's, or the x-user-ip your backend declares. A forged or missing value can only make the firewall block less, never more.
Next Steps
Quickstart
Set up an integration, policy, route and credential end to end
Incidences
See the verdict of every request