Agentic Data Plane

Connect Your App to AI Gateway

This guide shows how to connect your AI agent or application to the AI Gateway. You construct the proxy URL for a provider you have already created, authenticate (with the rpk ai CLI for local development, with OIDC client credentials for CI and application code, or with an API key for a long-lived client), and send your first request with the SDK of your choice.

The provider’s Connect tab in Agentic Data Plane generates this configuration for you: an API key step, setup instructions for popular clients, and code examples with the provider’s proxy URL already filled in. Copy from the tab to get started quickly, or follow this page for the full flow.

After completing this guide, you will be able to:

  • Construct the proxy URL for an LLM provider you have configured

  • Authenticate to AI Gateway with the rpk ai CLI for local development, with OIDC client credentials for CI and programmatic clients, or with an API key for a long-lived client

  • Send requests through the proxy URL with the SDK of your choice

Prerequisites

Proxy URL anatomy

Every provider you create in AI Gateway gets its own proxy URL:

<gateway-base>/llm/v1/providers/<provider-name>/<upstream-path>
  • <gateway-base>: The AI Gateway base URL for your Agentic Data Plane environment. Cluster-specific subdomain on clusters.rdpa.co (for example, https://aigw.<cluster-id>.clusters.rdpa.co). Copy the exact value from the Proxy URL field on any provider’s Connection card.

  • <provider-name>: The name you gave the provider when you created it, for example my-openai or prod-anthropic.

  • <upstream-path>: The upstream API path, relative to the provider’s base URL (for example, chat/completions for OpenAI, whose default base URL already ends in /v1, or v1/messages for Anthropic).

AI Gateway forwards the request to the upstream provider, attaches the configured credentials, and records the request for observability. Your application never sees the upstream API key.

The provider detail page generates ready-to-run snippets that already include the correct proxy URL and paths. When in doubt, copy from the Connect tab there.

Operations the proxy forwards

AI Gateway forwards a request only when its path and method name an operation the gateway recognizes. It forwards these:

  • Inference, in each provider’s own request shape and including streaming, on POST.

  • Embeddings, on POST.

  • Token counting, on POST.

  • Model listing, on GET.

Every other call gets 403 Forbidden with a message naming the method and path, and never reaches the upstream provider. A provider’s other APIs are not forwarded, including fine-tuning, file uploads, batches, assistants, reads of previously stored responses, model deletion, and organization administration.

Two things follow from how the proxy works. It calls the upstream with the credentials you configured on the provider, so a call outside this set would act on the whole upstream account rather than on the models you enabled. And a call outside this set carries no model name, which leaves guardrails, budgets, and the provider’s model list with nothing to check.

For the same reasons, the proxy refuses a path holding a . or .. segment, or a literal or encoded ?, #, or %, because each of these can make the upstream resolve a different path than the one the gateway classified. It also refuses a request that names no model, unless the request is a model listing, because a model the gateway can’t read can’t be checked against the provider’s model list. A Gemini Enterprise Agent Platform (formerly Vertex AI) provider is stricter still. It forwards POST model calls only and answers anything else with 400.

Refused calls are still recorded in the audit log, so you can see what a client attempted.

Use rpk ai for local development

The rpk ai command is the Redpanda AI CLI. Use it to manage AI Gateway resources (LLM providers, MCP servers, OAuth providers) and call MCP tools from the command line. rpk ai is self-contained. It has its own login and its own Agentic Data Plane environment selection, independent of any rpk cloud session.

  1. Install rpk ai:

    rpk ai install

    Update later with rpk ai upgrade. Remove with rpk ai uninstall.

    Upgrading rpk does not upgrade the plugin. The plugin is a separate binary that stays at its installed version, even across rpk upgrades, until you run rpk ai upgrade. New commands and flags in the reference marked "introduced in ai version X" require the plugin at that version or later.
  2. Sign in. This runs an OAuth device-authorization flow in your browser, caches credentials in ~/.rpai/credentials (readable only by you), then lists the Agentic Data Plane environments in your organization so you can select one:

    rpk ai auth login

    If the browser opens the wrong sign-in provider, or your email address belongs to more than one Redpanda Cloud organization, pass --email <address> so rpk ai looks up your sign-in provider before the browser opens, or --connection <name> to name the Auth0 connection directly. Both flags require the plugin at version 0.2.90 or later. If the browser keeps reusing an earlier session, add --no-browser and open the printed URL in a private window.

    After a plugin upgrade, rpk ai env list reports that no environments are configured until you run rpk ai auth login again. This is expected, because the login rewrites the config.

  3. Select the Agentic Data Plane environment whose AI Gateway you want to target. The rpk ai env use command accepts an environment name or ID and switches the active environment:

    rpk ai env list
    rpk ai env use <environment>

    Inspect the resolved environment and token state at any time with rpk ai env show and rpk ai auth status.

  4. Verify the connection:

    rpk ai llm-provider list

If the cached token has expired, rpk ai returns a 401. Rerun rpk ai auth login to refresh it.

rpk ai help, rpk ai version, and unknown subcommands run without prompting for authentication, so you can browse the CLI surface offline before signing in. Authentication is only required for commands that hit AI Gateway.

To target a specific AI Gateway URL for a single invocation (for example, a local gateway, or a staging environment the environments list does not include), pass --rpai-endpoint:

rpk ai --rpai-endpoint http://localhost:8090 llm-provider list

This overrides the selected environment’s AI Gateway URL for that one command, and the flag is not bound to an environment variable. For a manual or local gateway you use repeatedly, define it once as an environment instead:

rpk ai env add local --ai-gateway-url http://localhost:8090 --auth-mode none
rpk ai env use local

Environment variables

The rpk ai command honors the following environment variables:

Variable Purpose

RPAI_TOKEN

Static bearer token for the gateway. rpk ai normally manages its own token through rpk ai auth login. Set this (or pass --token) to override, for example in a headless or CI shell.

RPAI_CONFIG, RPAI_VERBOSE, RPAI_FORMAT

Map to --rpai-config, --rpai-verbose, --format (short flags -c, -v, -o). Long flag names are renamed under rpk ai to avoid colliding with the global rpk flags. There is no environment variable for environment selection (use --rpai-environment or rpk ai env use) or for the AI Gateway URL override (use the --rpai-endpoint flag).

Authenticate with OIDC client credentials (CI and programmatic)

For application code, CI runners, server-side processes, and headless agents, use the OAuth 2.0 client_credentials grant directly. This is the canonical authentication path for SDK-style usage. rpk ai is for command-line workflows, not for embedding in application code. AI Gateway issues the token itself. For a self-managed agent, copy the token endpoint from the Token endpoint row on the agent’s Setup tab.

Parameter Value

Token endpoint

<gateway-base>/oauth/idp/token

Discovery URL

<gateway-base>/.well-known/openid-configuration

Grant type

client_credentials

The token request takes no audience parameter.

  • cURL

  • Python (authlib)

  • Node.js (openid-client)

AUTH_TOKEN=$(curl -s --request POST \
    --url '<gateway-base>/oauth/idp/token' \
    --header 'content-type: application/x-www-form-urlencoded' \
    --data grant_type=client_credentials \
    --data client_id=<client-id> \
    --data client_secret=<client-secret> | jq -r .access_token)

Replace <gateway-base> with your AI Gateway base URL, and <client-id> and <client-secret> with your service account credentials.

from authlib.integrations.requests_client import OAuth2Session

token_endpoint = "<gateway-base>/oauth/idp/token"

client = OAuth2Session(
    client_id="<client-id>",
    client_secret="<client-secret>",
    token_endpoint=token_endpoint,
)

token = client.fetch_token(grant_type="client_credentials")

access_token = token["access_token"]

Passing token_endpoint to the OAuth2Session constructor lets authlib handle renewal automatically. For client_credentials grants, it fetches a new token rather than using a refresh token.

import * as client from 'openid-client';

const config = await client.discovery(
  new URL('<gateway-base>'),
  '<client-id>',
  '<client-secret>',
);

const tokens = await client.clientCredentialsGrant(config);
const accessToken = tokens.access_token;

This example uses openid-client version 6. Discovery reads the gateway’s /.well-known/openid-configuration document.

Token lifecycle management

Your client is responsible for refreshing tokens before they expire. OIDC access tokens have a limited TTL set by the identity provider, and AI Gateway doesn’t renew them automatically. Check the expires_in field in the token response for the exact duration.
  • Proactively refresh at ~80% of the token’s TTL to avoid failed requests.

  • authlib (Python) handles renewal automatically when you pass token_endpoint to OAuth2Session.

  • For other languages, cache the token and its expiry, then request a new token before the current one expires.

  • For SDK code, refresh OIDC client-credentials tokens through your client library (see the authlib example in Authenticate with OIDC client credentials (CI and programmatic)).

Authenticate with an API key

An API key is a long-lived credential that authenticates requests as the user who created it. Use one where a browser sign-in and a short-lived token are awkward: a desktop AI client that reads a key from its configuration, a long-running script, or a workstation tool you don’t want to re-authenticate every day. You don’t need to create a key for Claude Code, Codex, or Antigravity CLI. The rpk ai run command creates and renews one for the session. See How a session authenticates.

An API key carries your own identity and your own permissions, so every call it makes is attributed to you in the audit log and in transcripts. Use OIDC client credentials for an application or a CI job, where the caller should be a service account rather than a person.

Agentic Data Plane doesn’t revoke a user’s API keys automatically. When you deactivate or remove a user in your identity provider, manually delete all of that user’s API keys in Agentic Data Plane. Their keys remain valid until they expire or are deleted.

Create a key

  1. Open API keys in the sidebar, then click Create API key.

  2. Enter a name. Name the key after the client and the machine that holds it, such as Cursor on my laptop, so you can tell which key to delete later. Add a description if the name needs more context.

  3. Select an expiration: 1 day, 7 days, 30 days, 90 days, a date you pick, or Never. The default is 30 days. Set an expiration. A key with no expiration stays valid until someone deletes it.

  4. Click Create key.

  5. Copy the key and store it in your client’s secret store or password manager.

    The key is shown once. After you close the dialog you can’t read it again, because Agentic Data Plane stores only a hash of it. If you lose a key, delete it and create another.

A key’s expiration is fixed when you create it. To change when a key expires, create a new key and delete the old one. To rename a key or change its description, run rpk ai api-key update <name> with --display-name or --description. The API keys page doesn’t edit keys.

Send a key with a request

Every key starts with rpadp_. Send it in the X-Redpanda-Api-Key header:

export PROXY_URL="<your-gateway-base>/llm/v1/providers/<provider-name>"
export REDPANDA_ADP_API_KEY="<the-key-you-copied>"

curl "$PROXY_URL/chat/completions" \
  -H "X-Redpanda-Api-Key: $REDPANDA_ADP_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"model":"gpt-4o","messages":[{"role":"user","content":"Hello"}]}'

AI Gateway also accepts a key in Authorization: Bearer <key>, X-Api-Key, or X-Goog-Api-Key, which lets a client that only knows how to send a bearer token or a vendor-style API key header authenticate without extra configuration. The gateway recognizes a key in those headers by its rpadp_ prefix, so a key and an ordinary gateway token are never confused.

AI Gateway rejects two combinations with a 401:

  • A key sent together with the X-Redpanda-Cloud-Token header. Send one credential or the other, not both.

  • Two different keys in one request, for example one in Authorization and a different one in X-Api-Key.

An API key replaces the gateway credential only. For most providers, AI Gateway attaches the upstream provider credential itself, and the key can go in any of the headers this section lists.

A provider with Auth passthrough turned on, whether Anthropic, OpenAI, or OpenAI-compatible, requires an API key. Your client sends two separate credentials:

  • Your API key in X-Redpanda-Api-Key. It authenticates you to AI Gateway, not to the upstream provider.

  • Your own upstream credential, such as a Claude or ChatGPT subscription token, in Authorization.

AI Gateway rejects a passthrough request with HTTP 400 when it authenticated with a gateway token instead of an API key, or when the Authorization header is missing.

Review and delete keys

The API keys page lists each key by name, with the key’s prefix and its last four characters beneath it, so you can match a key you hold against its row. The other columns report when the key expires, when it was last used, and when and by whom it was created. An expired key stays in the list, marked Expired, until someone deletes it.

Search by name or creator to find a key. By default you see the keys you created. An administrator who can list every user’s keys can filter the list by creator.

To revoke a key, open its actions menu and click Delete. Deleting is permanent, and clients using the key stop working within a minute.

Permissions

Creating a key comes with the Sandboxed permission template and every higher template. Reading and deleting the keys you created, and renaming them with rpk ai api-key update, comes from the built-in Owner lifecycle policy, which applies to everyone, so no administrator has to grant a policy for each person.

Reading, renaming, or deleting another user’s keys is separate and administrative. It needs the dataplane_adp_apikey_get, _list, _update, and _delete permissions, which only the Full access template grants. See API key permissions.

Send requests with your SDK

The examples in this section assume you’ve set:

export PROXY_URL="<your-gateway-base>/llm/v1/providers/<provider-name>"
export AUTH_TOKEN="<oidc-access-token>"   # from the client_credentials flow above

To use an API key instead, set AUTH_TOKEN to the key. The OpenAI, Anthropic, OpenAI-compatible, Amazon Bedrock, and Gemini Enterprise Agent Platform examples work unchanged. In the Gemini example, and with the AWS SDK client in the Bedrock tip, send the key in an X-Redpanda-Api-Key header instead of X-Redpanda-Cloud-Token.

  • OpenAI SDK

  • Anthropic SDK

  • Google Gemini SDK

  • Amazon Bedrock

  • Gemini Enterprise Agent Platform

  • OpenAI-compatible

import os
from openai import OpenAI

client = OpenAI(
    base_url=os.environ["PROXY_URL"],       # .../llm/v1/providers/my-openai
    api_key=os.environ["AUTH_TOKEN"],        # OIDC access token
)

response = client.chat.completions.create(
    model="gpt-4o",                          # native OpenAI model ID
    messages=[{"role": "user", "content": "Hello from AI Gateway"}],
)
print(response.choices[0].message.content)

The OpenAI SDK appends chat/completions to the proxy URL, and AI Gateway forwards the call to OpenAI’s /v1/chat/completions. Use it with any OpenAI provider and, with a different base_url, with any OpenAI-compatible provider (vLLM, Ollama, LM Studio, Together, Groq, OpenRouter).

import os
from anthropic import Anthropic

client = Anthropic(
    base_url=os.environ["PROXY_URL"],       # .../llm/v1/providers/my-anthropic
    auth_token=os.environ["AUTH_TOKEN"],     # OIDC access token
)

message = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello from AI Gateway"}],
)
print(message.content[0].text)

The Anthropic SDK hits v1/messages on the proxy, which AI Gateway forwards to Anthropic. If the provider has Auth passthrough turned on, the Authorization header carries your own Anthropic credential, which AI Gateway forwards unchanged. Authenticate to AI Gateway with an API key in the X-Redpanda-Api-Key header. A gateway token doesn’t work with passthrough.

import os
from google import genai
from google.genai import types

client = genai.Client(
    api_key=os.environ["AUTH_TOKEN"],        # the SDK requires a key
    http_options=types.HttpOptions(
        base_url=f"{os.environ['PROXY_URL']}/v1beta",  # .../llm/v1/providers/my-google/v1beta
        headers={"X-Redpanda-Cloud-Token": os.environ["AUTH_TOKEN"]},
    ),
)

response = client.models.generate_content(
    model="gemini-2.0-flash",
    contents="Hello from AI Gateway",
)
print(response.text)

Send a gateway token in the X-Redpanda-Cloud-Token header. The Google SDKs send api_key as x-goog-api-key, which AI Gateway reads only for an API key. With an API key you can pass it as api_key, or send it in X-Redpanda-Api-Key instead of X-Redpanda-Cloud-Token. AI Gateway sets x-goog-api-key to the provider’s stored key when it calls Google.

Bedrock is different. AI Gateway performs SigV4 signing server-side, using the credentials on the provider. Your client only needs to call the proxy URL with an OIDC access token.

import os, httpx

# Bedrock 4.6+ Anthropic models require an inference profile (us./eu./global.).
# Replace with the inference profile your provider exposes.
response = httpx.post(
    f"{os.environ['PROXY_URL']}/model/us.anthropic.claude-sonnet-4-6/invoke",
    headers={"Authorization": f"Bearer {os.environ['AUTH_TOKEN']}"},
    json={
        "anthropic_version": "bedrock-2023-05-31",
        "messages": [{"role": "user", "content": "Hello"}],
        "max_tokens": 1024,
    },
)
print(response.json())

See Inference profiles and IAM for inference-profile selection guidance.

Bedrock’s Converse API works the same way. Send to /model/{MODEL_ID}/converse with a Converse-shaped body. You can also use the AWS SDK’s bedrockruntime client with its BaseEndpoint set to the proxy URL. AI Gateway re-signs the request server-side with the provider’s credentials, so your client never sees AWS keys. The SDK’s own SigV4 signature uses the Authorization header, so send your gateway token in the X-Redpanda-Cloud-Token header.

AI Gateway signs each upstream call with the service account key stored on the provider, so your client sends only the gateway credential. Call Google’s native API path, with the provider’s project ID and location in it. A Claude model takes publishers/anthropic and :rawPredict:

import os, httpx

# <project-id> and <location> must match the provider's settings.
response = httpx.post(
    f"{os.environ['PROXY_URL']}/v1/projects/<project-id>/locations/<location>"
    "/publishers/anthropic/models/claude-sonnet-4-6:rawPredict",
    headers={"Authorization": f"Bearer {os.environ['AUTH_TOKEN']}"},
    json={
        "anthropic_version": "vertex-2023-10-16",
        "messages": [{"role": "user", "content": "Hello"}],
        "max_tokens": 1024,
    },
)
print(response.json())

A Gemini model takes publishers/google and :generateContent, with a Gemini-shaped body:

response = httpx.post(
    f"{os.environ['PROXY_URL']}/v1/projects/<project-id>/locations/<location>"
    "/publishers/google/models/gemini-3.6-flash:generateContent",
    headers={"Authorization": f"Bearer {os.environ['AUTH_TOKEN']}"},
    json={"contents": [{"role": "user", "parts": [{"text": "Hello"}]}]},
)
print(response.json())

A path with a different location fails with 400 and vertex_location_mismatch. See Gemini Enterprise Agent Platform troubleshooting.

Use the OpenAI SDK with the proxy URL of the OpenAI-compatible provider and whatever model identifier the upstream exposes. If the provider’s upstream base URL has no version segment, add /v1 to the proxy URL, as the provider’s Connect tab does:

import os
from openai import OpenAI

client = OpenAI(
    base_url=os.environ["PROXY_URL"],       # .../llm/v1/providers/my-vllm
    api_key=os.environ["AUTH_TOKEN"],
)

response = client.chat.completions.create(
    model="meta-llama/Llama-3.3-70B-Instruct",  # as exposed by your upstream
    messages=[{"role": "user", "content": "Hello"}],
)

The provider’s Connect tab also has per-client setup guides, for Antigravity CLI, Claude Code, Codex, Cline, Cursor, and OpenCode, and code examples for HTTP, provider SDKs, and agent frameworks. Which guides appear depends on the provider type and its models. See the Connect tab.

Group a session’s requests with a conversation ID

Agentic Data Plane assembles an agent’s Transcripts tab from OpenTelemetry spans your app exports itself, not from the gateway calls alone. To populate it, stream instrumented spans to the environment’s OTLP endpoint and put the same identifier on every span in the session as the gen_ai.conversation.id attribute. For a self-managed agent, the Setup tab generates this instrumentation. See Set up a self-managed agent. For the span contract, ingestion limits, and validation checks, see Self-Managed Agent Telemetry Reference. See what your agent did explains how transcripts read this attribute.

Also send the X-Redpanda-Genai-Conversation header on every gateway request in the session, set to the same session or thread ID you put on the spans. The gateway stamps that conversation ID on its own record of each call. The header doesn’t affect authentication or whether requests succeed, and it is not a substitute for instrumenting your app. A transcript is built only from the spans your app exports.

Set it through your SDK’s default-headers mechanism so it rides along with both the LLM call and each MCP tool call:

import os
from openai import OpenAI

client = OpenAI(
    base_url=os.environ["PROXY_URL"],
    api_key=os.environ["AUTH_TOKEN"],
    default_headers={"X-Redpanda-Genai-Conversation": session_id},
)

Replace session_id with the session or thread identifier your framework already tracks, and stamp the same value on the session’s MCP tool calls.

Streaming responses

Streaming passes through unchanged. Use the SDK’s native streaming API. The proxy forwards the stream byte-for-byte.

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Write a short poem"}],
    stream=True,
)

for chunk in response:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

Handle errors

AI Gateway returns standard HTTP status codes. The upstream provider’s error body passes through, so your existing SDK error handling works:

Status Meaning

400

Bad request. Invalid parameters or malformed JSON.

401

Authentication failed. The token or API key is invalid, expired, or deleted, or wasn’t sent in a header AI Gateway reads. A gateway token goes in Authorization: Bearer or X-Redpanda-Cloud-Token. An API key goes in X-Redpanda-Api-Key or one of the other headers in Authenticate with an API key.

403

Forbidden. Nothing grants the caller this action, the provider is disabled, the requested model isn’t enabled on the provider (model_not_allowed), or the call isn’t an operation the proxy forwards. See Operations the proxy forwards.

404

Provider not found. Verify the provider name in the URL.

429

Rate limited. Either the upstream provider is rate limiting you, or the agent’s budget is exhausted. A budget block uses the provider’s own rate-limit error format, and its message names the budget and when it resets. See Set Up Budgets. Respect Retry-After if present.

5xx

Upstream or gateway error. Retry with exponential backoff.

Best practices

  • Use environment variables for the proxy URL and token. Never hard-code them.

  • Refresh OIDC tokens through your client library so refresh is invisible to your SDK code (authlib for Python, openid-client for Node.js, and so on).

  • Implement retry with exponential backoff for 5xx and timeout conditions.

  • Respect Retry-After on 429 responses.

  • Rotate service account credentials on a schedule your organization accepts.

  • Observe usage in Redpanda Agentic Data Plane on each provider’s detail page.

Troubleshooting

These are the most common errors when sending requests through the proxy URL, and how to resolve them.

401 Unauthorized

  • If you’re using rpk ai: Rerun rpk ai auth login to refresh the credentials. Token expiry surfaces as a 401.

  • If you’re using OAuth client credentials: Check the token hasn’t expired and refresh it. Confirm you requested it from <gateway-base>/oauth/idp/token and that the Authorization header is formatted Bearer <token>.

  • If you’re using an API key: Check the key hasn’t expired or been deleted on the API keys page. A 401 can also mean the request carried the key alongside an X-Redpanda-Cloud-Token header, or carried two different keys. Send one credential. See Authenticate with an API key.

  • For Gemini: Ensure a gateway token is sent in the X-Redpanda-Cloud-Token header. AI Gateway reads x-goog-api-key only for an API key.

  • For a provider with Auth passthrough: Ensure the client sends an API key in X-Redpanda-Api-Key and a valid upstream credential in Authorization, and doesn’t also send X-Redpanda-Cloud-Token. A passthrough request that authenticates with a gateway token fails with HTTP 400 instead. See Authenticate with an API key.

404 Not Found

  • Re-check the provider name in the proxy URL. The segment after /providers/ must match the provider’s name exactly.

403 Forbidden

  • The caller may lack the required access. Ask an admin for an access policy granting Action::"LLMProvider.invoke", plus Action::"LLMProvider.get" if the caller also reads provider configuration. For an agent’s service account credentials, that policy has to name the agent itself, which is what the agent’s Permissions tab writes. For a person signed in with rpk ai, it names that user, and for an API key, the user who created the key. No built-in role short of Admin grants these. See LLM provider permissions.

  • The provider may be disabled. Check the badge next to the provider’s name on its detail page.

  • If the response is model_not_allowed, either the requested model isn’t enabled on the provider, or the request named no model at all. Enable the model on the provider’s Models tab, request a model that is enabled, or set the model so it can be checked.

  • If the message says the method and path aren’t an operation this gateway forwards, the call is outside the set the proxy carries. Call the provider’s other APIs directly instead of through the proxy URL. See Operations the proxy forwards.

Connection timeout or reset

  • Verify the proxy URL is correct (copy directly from the provider’s Connection card).

  • Check that the provider isn’t pointing at a private base URL your client can’t reach (OpenAI-compatible providers only).

  • Check the upstream provider’s status page.