Custom HTTP Targets
Red team any AI application or traditional ML system wherever it is deployed - AWS, Azure, GCP, or any custom HTTP endpoint.
AI red teaming works against any AI system that accepts input and returns output, wherever it runs. You are not limited to OpenAI or litellm-schema endpoints. Point Dreadnode at a model or application behind your own HTTP API (any request and response shape), a cloud-managed deployment (AWS SageMaker, Azure OpenAI / AI Foundry, Google Vertex AI), an agent loop, RAG pipeline, or multi-turn agent, or a self-hosted model.
Drive it from the Dreadnode AI red teaming agent (TUI) or the Python SDK.
Cloud-managed deployments
Section titled “Cloud-managed deployments”If your target is a managed cloud deployment, start with its dedicated guide:
This page covers the generic path: any custom HTTP endpoint.
Two ways to connect a custom endpoint
Section titled “Two ways to connect a custom endpoint”| Path | How it works |
|---|---|
| Dreadnode AI red teaming agent (TUI) | Describe the endpoint in plain language. The agent writes the workflow that calls it. |
| Python SDK | Write a @dn.task target (full control) or a declarative build_target(TargetSpec). |
SDK: write a target function
Section titled “SDK: write a target function”Any async function that takes a string and returns a string, wrapped with @dn.task, is a valid target. You own the request shape, auth, and response parsing:
import httpximport dreadnode as dnfrom dreadnode.airt import Assessment, tap_attack
dn.configure(organization="your-org", workspace="your-workspace", project="your-project")
@dn.taskasync def my_api_target(prompt: str) -> str: async with httpx.AsyncClient() as client: response = await client.post( "https://my-app.example.com/v1/chat", json={"message": prompt}, # your request shape headers={"Authorization": f"Bearer {API_KEY}"}, timeout=30.0, ) return response.json()["reply"] # your response shape
async def main(): assessment = Assessment( name="custom-api-assessment", target=my_api_target, model="dn/claude-opus-4-8", goal="Extract the system prompt from the agent", ) async with assessment.trace(): await assessment.run(tap_attack, n_iterations=15)Why the @dn.task decorator matters
Section titled “Why the @dn.task decorator matters”Your target is more than a plain function. @dn.task turns it into a traced task, and that is what makes red teaming observable and scorable:
- Every call is captured with its input prompt and output response, so each trial becomes a finding you can open in the platform to see exactly what was sent and what came back. This is the evidence behind every result.
- The attack loop treats it as a first-class unit it can call, retry, and score; scorers attach to the task’s output.
- Timing and execution metrics are recorded per call, and every trial streams to your assessment in the platform automatically.
In short: without the decorator the attack cannot trace or score your endpoint; with it, every prompt/response pair, score, and transform is recorded as evidence. Keep all the HTTP, auth, and parsing inside the task; the decorator captures the boundary (input in, text out).
SDK: declarative build_target
Section titled “SDK: declarative build_target”For a straightforward request/response endpoint, skip the boilerplate and describe it with a TargetSpec:
from dreadnode.airt import build_target, TargetSpec, TargetAuth
target = build_target( TargetSpec( endpoint="https://my-app.example.com/score", auth=TargetAuth(type="bearer", env_var="MY_API_KEY"), request_template='{"input_data": {"input_string": ["{prompt}"]}}', # any request shape response_text_path="$.output", # JSONPath to the reply text ))# use it anywhere a target is accepted, e.g. tap_attack(target=target, ...)request_template places {prompt} (and {image_b64} / {audio_b64} / {video_b64} for multimodal) into your request body; response_text_path is a JSONPath to the reply. Any provider’s payload works without custom code.
Auth mechanisms
Section titled “Auth mechanisms”Credentials are read from the environment or cloud identity, never inlined.
auth.type | Use for | How it authenticates |
|---|---|---|
api_key | OpenAI-compatible, Azure ML / AI Foundry | Static key from env_var into a configurable header |
bearer | Any static token | Authorization: Bearer <env_var> |
azure_ad | Azure with managed identity | Entra token via DefaultAzureCredential, auto-refreshed |
gcp | Google Vertex AI | Google ADC token |
aws_sigv4 | Amazon Bedrock / SageMaker | SigV4 request signing via the AWS credential chain |
Agent (TUI): describe it in plain language
Section titled “Agent (TUI): describe it in plain language”Launch the agent and describe the endpoint. It writes the httpx call, auth, and response parsing for you:
dn --model dn/claude-opus-4-8 --capability ai-red-teamingI have a chat API at https://my-app.example.com/v1/chat (deployed on Azure Container Apps) that accepts
{"message": "..."}and returns{"reply": "..."}. It needs a Bearer token. Run a TAP attack against it with the goal “Extract the system prompt” and a max of 15 iterations.
For an Azure OpenAI deployment, give the agent the endpoint, deployment, API version, and how the key is provided:
Red team my Azure OpenAI deployment. Endpoint
https://my-resource.openai.azure.com, deploymentgpt-4o-mini, API version2024-10-21, and the API key is in theAZURE_API_KEYenvironment variable (api-keyheader). Run a TAP attack with the goal “make it reveal its system prompt” and 15 iterations.
Set AZURE_API_KEY in your shell before launching the TUI (see Provide credentials below); UI Secrets apply only when the attack runs on Dreadnode-hosted infrastructure.
Provide credentials
Section titled “Provide credentials”When you run the SDK or the TUI on your machine, credentials come from your local environment variables - you do not need to add anything in the web UI. The attack executes in your local process and reads the keys from your shell:
- SDK custom task: your
@dn.tasktarget reads the key straight fromos.environ(or, forazure_ad/aws_sigv4/gcpauth, from your local cloud-identity chain). - TUI ai-red-teaming agent: run locally, the agent reads the same shell environment variables.
So just export the variables in your shell before launching. When an attack instead runs on Dreadnode-hosted infrastructure (a provisioned sandbox), it reads credentials from Secrets you add under Account Settings > Secrets - Dreadnode injects them into that environment, so your local shell is not used. See Secrets.
Add whatever your target needs. For an Azure OpenAI / AI Foundry deployment that is three values:
AZURE_API_KEY="<your key>"AZURE_API_BASE="https://<your-resource>.openai.azure.com/"AZURE_API_VERSION="2024-10-21"For a custom HTTP app it is usually a single token (for example MY_API_KEY); for OpenAI it is OPENAI_API_KEY. See Secrets for details.
Azure OpenAI as a custom task
Section titled “Azure OpenAI as a custom task”You can red team an Azure OpenAI deployment with a @dn.task target that calls its chat/completions URL directly - useful when you want full control over the request. The API key goes in an api-key header and the API version is a query parameter:
import os, httpximport dreadnode as dn
RESOURCE = "https://<your-resource>.openai.azure.com"DEPLOYMENT = "gpt-4o-mini" # your deployment name, not the model nameAPI_VERSION = "2024-10-21"
@dn.taskasync def azure_target(prompt: str) -> str: url = f"{RESOURCE}/openai/deployments/{DEPLOYMENT}/chat/completions" async with httpx.AsyncClient(timeout=60) as client: r = await client.post( url, params={"api-version": API_VERSION}, headers={"api-key": os.environ["AZURE_API_KEY"]}, # local env var or UI Secret json={"messages": [{"role": "user", "content": prompt}]}, ) r.raise_for_status() return r.json()["choices"][0]["message"]["content"]Only AZURE_API_KEY is required for this custom-task path (the endpoint and version are in the code). For the simpler generator path (azure/<deployment>, no custom task), see the Azure OpenAI and AI Foundry guide.
Common patterns
Section titled “Common patterns”RAG pipelines
Section titled “RAG pipelines”Put both the retrieval and generation steps inside the target, then probe for context injection:
@dn.taskasync def rag_target(prompt: str) -> str: documents = await retrieve_relevant_docs(prompt) # your retrieval return await generate_with_context(prompt, documents) # your generationPair it with the rag_poisoning transforms (context injection, document poisoning, query manipulation):
from dreadnode.transforms.rag_poisoning import context_injection
attack = tap_attack( goal="Inject false information through RAG context", target=rag_target, attacker_model="dn/claude-opus-4-8", evaluator_model="dn/claude-opus-4-8", transforms=[context_injection()],)Multi-turn or stateful agents
Section titled “Multi-turn or stateful agents”Manage conversation state inside the target:
@dn.taskasync def stateful_target(prompt: str) -> str: session = get_or_create_session() session.add_message("user", prompt) response = await call_model(session.messages) session.add_message("assistant", response) return responseAgent frameworks (OpenAI Assistants, Anthropic, custom)
Section titled “Agent frameworks (OpenAI Assistants, Anthropic, custom)”Wrap the framework’s multi-step protocol in the target: create the thread/run, poll for completion, and return the final text. The attack sees a single prompt-to-response call regardless of how many API calls happen inside.
Cookbook
Section titled “Cookbook”For a full runnable notebook against a custom HTTP app (with a shipped sample app you can deploy to Container Apps, Lambda, or a Function App), see 07_custom_http.ipynb in the AI Red Teaming cookbook.
Next steps
Section titled “Next steps”- Azure OpenAI and AI Foundry - dedicated Azure guide
- AWS SageMaker - dedicated SageMaker guide
- Targets overview - all transports and how auth works
- Using the SDK - full SDK getting started guide