Are you sure you want to delete this access key?
sidebar_label | description |
---|---|
HTTP API | Configure HTTP/HTTPS endpoints for custom LLM integrations with dynamic request transforms, variable substitution, and multi-provider API compatibility |
Setting the provider ID to a URL sends an HTTP request to the endpoint. This provides a general-purpose way to use any HTTP endpoint for inference.
The provider configuration allows you to construct the HTTP request and extract the inference result from the response.
providers:
- id: https
config:
url: 'https://example.com/generate'
method: 'POST'
headers:
'Content-Type': 'application/json'
body:
myPrompt: '{{prompt}}'
transformResponse: 'json.output' # Extract the "output" field from the response
The placeholder variable {{prompt}}
will be replaced with the final prompt for the test case. You can also reference test variables as you construct the request:
providers:
- id: https
config:
url: 'https://example.com/generateTranslation'
body:
prompt: '{{prompt}}'
model: '{{model}}'
translate: '{{language}}'
tests:
- vars:
model: 'gpt-4.1-mini'
language: 'French'
body
can be a string or JSON object. If the body is a string, the Content-Type
header defaults to text/plain
unless specified otherwise. If the body is an object, then content type is automatically set to application/json
.
providers:
- id: https
config:
url: 'https://example.com/generateTranslation'
body:
model: '{{model}}'
translate: '{{language}}'
providers:
- id: https
config:
headers:
'Content-Type': 'application/x-www-form-urlencoded'
body: 'model={{model}}&translate={{language}}'
You can also send a raw HTTP request by specifying the request
property in the provider configuration. This allows you to have full control over the request, including headers and body.
Here's an example of how to use the raw HTTP request feature:
providers:
- id: https
config:
useHttps: true
request: |
POST /v1/completions HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer {{api_key}}
{
"model": "llama3.1-405b-base",
"prompt": "{{prompt}}",
"max_tokens": 100
}
transformResponse: 'json.content' # extract the "content" field from the response
In this example:
request
property contains a raw HTTP request, including the method, path, headers, and body.useHttps
property is set to true
, so the request will be sent over HTTPS.{{api_key}}
and {{prompt}}
within the raw request. These will be replaced with actual values when the request is sent.transformResponse
property is used to extract the desired information from the JSON response.You can also load the raw request from an external file using the file://
prefix:
providers:
- id: https
config:
request: file://path/to/request.txt
transformResponse: 'json.text'
This path is relative to the directory containing the Promptfoo config file.
Then create a file at path/to/request.txt
:
POST /api/generate HTTP/1.1
Host: example.com
Content-Type: application/json
{"prompt": "Tell me a joke"}
Nested objects are supported and should be passed to the dump
function.
providers:
- id: https
config:
url: 'https://example.com/generateTranslation'
body:
// highlight-start
messages: '{{messages | dump}}'
// highlight-end
model: '{{model}}'
translate: '{{language}}'
tests:
- vars:
// highlight-start
messages:
- role: 'user'
content: 'foobar'
- role: 'assistant'
content: 'baz'
// highlight-end
model: 'gpt-4.1-mini'
language: 'French'
Note that any valid JSON string within body
will be converted to a JSON object.
Query parameters can be specified in the provider config using the queryParams
field. These will be appended to the URL as GET parameters.
providers:
- id: https
config:
url: 'https://example.com/search'
// highlight-start
method: 'GET'
queryParams:
q: '{{prompt}}'
foo: 'bar'
// highlight-end
Both the provider id
and the url
field support Nunjucks templates. Variables in your test vars
will be rendered before sending the request.
providers:
- id: https://api.example.com/users/{{userId}}/profile
config:
method: 'GET'
If you are using promptfoo as a node library, you can provide the equivalent provider config:
{
// ...
providers: [{
id: 'https',
config: {
url: 'https://example.com/generate',
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: {
foo: '{{bar}}',
},
transformResponse: (json) => json.output,
}
}],
}
Request transform modifies your prompt after it is rendered but before it is sent to a provider API. This allows you to:
providers:
- id: https
config:
url: 'https://api.example.com/chat'
transformRequest: '{"message": "{{prompt}}"}'
body:
user_message: '{{prompt}}'
Use Nunjucks templates to transform the prompt:
transformRequest: '{"text": "{{prompt}}"}'
Define a function that transforms the prompt:
transformRequest: (prompt) => JSON.stringify({ text: prompt, timestamp: Date.now() });
Load a transform from an external file:
transformRequest: 'file://transforms/request.js'
Example transform file (transforms/request.js):
module.exports = (prompt) => {
return {
text: prompt,
metadata: {
timestamp: Date.now(),
version: '1.0',
},
};
};
You can also specify a specific function to use:
transformRequest: 'file://transforms/request.js:transformRequest'
The transformResponse
option allows you to extract and transform the API response. If no transformResponse
is specified, the provider will attempt to parse the response as JSON. If JSON parsing fails, it will return the raw text response.
You can override this behavior by specifying a transformResponse
in the provider config. The transformResponse
can be one of the following:
file://
) to a JavaScript moduleBy default, the entire response is returned as the output. If your API responds with a JSON object and you want to pick out a specific value, use the transformResponse
property to set a JavaScript snippet that manipulates the provided json
object.
For example, this transformResponse
configuration:
providers:
- id: https
config:
url: 'https://example.com/openai-compatible/chat/completions'
# ...
transformResponse: 'json.choices[0].message.content'
Extracts the message content from this response:
{
"id": "chatcmpl-abc123",
"object": "chat.completion",
"created": 1677858242,
"model": "gpt-4.1-mini",
"usage": {
"prompt_tokens": 13,
"completion_tokens": 7,
"total_tokens": 20
},
"choices": [
{
"message": {
"role": "assistant",
// highlight-start
"content": "\n\nThis is a test!"
// highlight-end
},
"logprobs": null,
"finish_reason": "stop",
"index": 0
}
]
}
If your API responds with a text response, you can use the transformResponse
property to set a JavaScript snippet that manipulates the provided text
object.
For example, this transformResponse
configuration:
providers:
- id: https
config:
url: 'https://example.com/api'
# ...
transformResponse: 'text.slice(11)'
Extracts the message content "hello world" from this response:
Assistant: hello world
You can use a string containing a JavaScript expression to extract data from the response:
providers:
- id: https
config:
url: 'https://example.com/api'
transformResponse: 'json.choices[0].message.content'
This expression will be evaluated with three variables available:
json
: The parsed JSON response (if the response is valid JSON)text
: The raw text responsecontext
: context.response
is of type FetchWithCacheResult
which includes:
data
: The response data (parsed as JSON if possible)cached
: Boolean indicating if response was from cachestatus
: HTTP status codestatusText
: HTTP status textheaders
: Response headers (if present)When using promptfoo as a Node.js library, you can provide a function as the response. You may return a string or an object of type ProviderResponse
.
parser:
{
providers: [{
id: 'https',
config: {
url: 'https://example.com/generate_response',
transformResponse: (json, text) => {
// Custom parsing logic that returns string
return json.choices[0].message.content;
},
}
},
{
id: 'https',
config: {
url: 'https://example.com/generate_with_tokens',
transformResponse: (json, text) => {
// Custom parsing logic that returns object
return {
output: json.output,
tokenUsage: {
prompt: json.usage.input_tokens,
completion: json.usage.output_tokens,
total: json.usage.input_tokens + json.usage.output_tokens,
}
}
},
}
}],
}
interface ProviderResponse {
cached?: boolean;
cost?: number;
error?: string;
logProbs?: number[];
metadata?: {
redteamFinalPrompt?: string;
[key: string]: any;
};
raw?: string | any;
output?: string | any;
tokenUsage?: TokenUsage;
isRefusal?: boolean;
sessionId?: string;
guardrails?: GuardrailResponse;
audio?: {
id?: string;
expiresAt?: number;
data?: string; // base64 encoded audio data
transcript?: string;
format?: string;
};
}
export type TokenUsage = z.infer<typeof TokenUsageSchema>;
export const TokenUsageSchema = BaseTokenUsageSchema.extend({
assertions: BaseTokenUsageSchema.optional(),
});
export const BaseTokenUsageSchema = z.object({
// Core token counts
prompt: z.number().optional(),
completion: z.number().optional(),
cached: z.number().optional(),
total: z.number().optional(),
// Request metadata
numRequests: z.number().optional(),
// Detailed completion information
completionDetails: CompletionTokenDetailsSchema.optional(),
});
You can use a JavaScript file as a response parser by specifying the file path with the file://
prefix. The file path is resolved relative to the directory containing the promptfoo configuration file.
providers:
- id: https
config:
url: 'https://example.com/api'
transformResponse: 'file://path/to/parser.js'
The parser file should export a function that takes three arguments (json
, text
, context
) and return the parsed output. Note that text and context are optional.
module.exports = (json, text) => {
return json.choices[0].message.content;
};
You can use the context
parameter to access response metadata and implement custom logic. For example, implementing guardrails checking:
module.exports = (json, text, context) => {
return {
output: json.choices[0].message.content,
guardrails: { flagged: context.response.headers['x-content-filtered'] === 'true' },
};
};
This allows you to access additional response metadata and implement custom logic based on response status codes, headers, or other properties.
You can also use a default export:
export default (json, text) => {
return json.choices[0].message.content;
};
You can also specify a function name to be imported from a file:
providers:
- id: https
config:
url: 'https://example.com/api'
transformResponse: 'file://path/to/parser.js:parseResponse'
This will import the function parseResponse
from the file path/to/parser.js
.
If your HTTP target has guardrails set up, you need to return an object with both output
and guardrails
fields from your transform. The guardrails
field should be a top-level field in your returned object and must conform to the GuardrailResponse interface. For example:
providers:
- id: https
config:
url: 'https://example.com/api'
transformResponse: |
{
output: json.choices[0].message.content,
guardrails: { flagged: context.response.headers['x-content-filtered'] === 'true' }
}
By default, the HTTP provider does not provide token usage statistics since it's designed for general HTTP APIs that may not return token information. However, you can enable optional token estimation to get approximate token counts for cost tracking and analysis. Token estimation is automatically enabled when running redteam scans so you can track approximate costs without additional configuration.
Token estimation uses a simple word-based counting method with configurable multipliers. This provides a rough approximation that's useful for basic cost estimation and usage tracking.
:::note Accuracy
Word-based estimation provides approximate token counts. For precise token counting, implement custom logic in your transformResponse
function using a proper tokenizer library.
:::
Token estimation is useful when:
Don't use token estimation when:
transformResponse
instead)Enable basic token estimation with default settings:
providers:
- id: https
config:
url: 'https://example.com/api'
body:
prompt: '{{prompt}}'
tokenEstimation:
enabled: true
This will use word-based estimation with a multiplier of 1.3 for both prompt and completion tokens.
Configure a custom multiplier for more accurate estimation based on your specific use case:
providers:
- id: https
config:
url: 'https://example.com/api'
body:
prompt: '{{prompt}}'
tokenEstimation:
enabled: true
multiplier: 1.5 # Adjust based on your content complexity
Multiplier Guidelines:
1.3
and adjust based on actual usageToken estimation works alongside response transforms. If your transformResponse
returns token usage information, the estimation will be skipped:
providers:
- id: https
config:
url: 'https://example.com/api'
tokenEstimation:
enabled: true # Will be ignored if transformResponse provides tokenUsage
transformResponse: |
{
output: json.choices[0].message.content,
tokenUsage: {
prompt: json.usage.prompt_tokens,
completion: json.usage.completion_tokens,
total: json.usage.total_tokens
}
}
For sophisticated token counting, implement it in your transformResponse
function:
providers:
- id: https
config:
url: 'https://example.com/api'
transformResponse: |
(json, text, context) => {
// Use a proper tokenizer library for accuracy
const promptTokens = customTokenizer.encode(context.vars.prompt).length;
const completionTokens = customTokenizer.encode(json.response).length;
return {
output: json.response,
tokenUsage: {
prompt: promptTokens,
completion: completionTokens,
total: promptTokens + completionTokens,
numRequests: 1
}
};
}
You can also load custom logic from a file:
providers:
- id: https
config:
url: 'https://example.com/api'
transformResponse: 'file://token-counter.js'
Example token-counter.js
:
// Using a tokenizer library like 'tiktoken' or 'gpt-tokenizer'
const { encode } = require('gpt-tokenizer');
module.exports = (json, text, context) => {
const promptText = context.vars.prompt || '';
const responseText = json.response || text;
return {
output: responseText,
tokenUsage: {
prompt: encode(promptText).length,
completion: encode(responseText).length,
total: encode(promptText).length + encode(responseText).length,
numRequests: 1,
},
};
};
Option | Type | Default | Description |
---|---|---|---|
enabled | boolean | false (true in redteam mode) | Enable or disable token estimation |
multiplier | number | 1.3 | Multiplier applied to word count (adjust for complexity) |
Here's a complete example for cost tracking with token estimation:
providers:
- id: https
config:
url: 'https://api.example.com/v1/generate'
method: POST
headers:
Authorization: 'Bearer {{env.API_KEY}}'
Content-Type: 'application/json'
body:
model: 'custom-model'
prompt: '{{prompt}}'
max_tokens: 100
tokenEstimation:
enabled: true
multiplier: 1.4 # Adjusted based on testing
transformResponse: |
{
output: json.generated_text,
cost: (json.usage?.total_tokens || 0) * 0.0001 // $0.0001 per token
}
When using an HTTP provider with multi-turn redteam attacks like GOAT and Crescendo, you may need to maintain session IDs between rounds. The HTTP provider will automatically extract the session ID from the response headers and store it in the vars
object.
A session parser is a javascript expression that should be used to extract the session ID from the response headers and returns it. All of the same formats of response parsers are supported.
The input to the session parser is an object data
with this interface:
{
headers?: Record<string, string> | null;
body?: Record<string, any> | null;
}
Simple header parser:
sessionParser: 'data.headers["set-cookie"]'
Example extracting the session from the body:
Example Response
{
"responses": [{ "sessionId": "abd-abc", "message": "Bad LLM" }]
}
Session Parser value:
sessionParser: 'data.body.responses[0]?.sessionId
The parser can take a string, file or function like the response parser.
Then you need to set the session ID in the vars
object for the next round:
providers:
- id: https
config:
url: 'https://example.com/api'
headers:
'Cookie': '{{sessionId}}'
You can use the {{sessionId}}
var anywhere in a header or body. Example:
providers:
- id: https
config:
url: 'https://example.com/api'
body:
'message': '{{prompt}}'
'sessionId': '{{sessionId}}'
Accessing the headers or body:
sessionParser: 'data.body.sessionId'
sessionParser: 'data.headers.["x-session-Id"]'
If you want the Promptfoo client to send a unique session or conversation ID with each test case, you can add a transformVars
option to your Promptfoo or redteam config. This is useful for multi-turn evals or multi-turn redteam attacks where the provider maintains a conversation state.
For example:
defaultTest:
options:
transformVars: '{ ...vars, sessionId: context.uuid }'
Now you can use the sessionId
variable in your HTTP target config:
providers:
- id: https
config:
url: 'https://example.com/api'
headers:
'x-promptfoo-session': '{{sessionId}}'
body:
user_message: '{{prompt}}'
The HTTP provider supports digital signature authentication with multiple certificate formats. This feature allows you to:
The current implementation uses asymmetric key cryptography (RSA by default), but the configuration is algorithm-agnostic. In either case, the private key is never sent to Promptfoo and will always be stored locally on your system either in your promptfooconfig.yaml
file or on a local path that the configuration file references.
providers:
- id: https
config:
url: 'https://api.example.com/v1'
method: 'POST'
headers:
'x-signature': '{{signature}}'
'x-timestamp': '{{signatureTimestamp}}'
signatureAuth:
type: 'pem'
privateKeyPath: '/path/to/private.key'
clientId: 'your-client-id'
The HTTP provider supports three certificate formats:
signatureAuth:
type: 'pem'
privateKeyPath: '/path/to/private.key' # Path to PEM private key file
# OR
privateKey: '-----BEGIN PRIVATE KEY-----\n...' # Direct key string
signatureAuth:
type: 'jks'
keystorePath: '/path/to/keystore.jks'
keystorePassword: 'your-keystore-password' # Optional: can use PROMPTFOO_JKS_PASSWORD env var
keyAlias: 'your-key-alias' # Optional: uses first available alias if not specified
signatureAuth:
type: 'pfx'
pfxPath: '/path/to/certificate.pfx'
pfxPassword: 'your-pfx-password' # Optional: can use PROMPTFOO_PFX_PASSWORD env var
# OR use separate certificate and key files
certPath: '/path/to/certificate.crt'
keyPath: '/path/to/private.key'
providers:
- id: https
config:
url: 'https://api.example.com/v1'
headers:
'x-signature': '{{signature}}'
'x-timestamp': '{{signatureTimestamp}}'
'x-client-id': 'your-client-id'
signatureAuth:
# Certificate type (pem, jks, or pfx)
type: 'pem'
# PEM options
privateKeyPath: '/path/to/private.key' # Path to key file
# privateKey: '-----BEGIN PRIVATE KEY-----\n...' # Or direct key string
# JKS options
# keystorePath: '/path/to/keystore.jks'
# keystorePassword: 'password' # Optional: can use PROMPTFOO_JKS_PASSWORD
# keyAlias: 'alias' # Optional: uses first available if not specified
# PFX options
# pfxPath: '/path/to/certificate.pfx'
# pfxPassword: 'password' # Optional: can use PROMPTFOO_PFX_PASSWORD
# certPath: '/path/to/certificate.crt' # Alternative to pfxPath
# keyPath: '/path/to/private.key' # Alternative to pfxPath
clientId: 'your-client-id'
# Optional fields with defaults shown
signatureValidityMs: 300000 # 5 minutes
signatureAlgorithm: 'SHA256'
signatureDataTemplate: '{{clientId}}{{timestamp}}\n' # \n is interpreted as a newline character
signatureRefreshBufferMs: 30000 # Optional: custom refresh buffer
:::note
You can use environment variables throughout your HTTP provider configuration using the {{env.VARIABLE_NAME}}
syntax.
:::
:::info Dependencies
jks-js
package: npm install jks-js
pem
package: npm install pem
:::When signature authentication is enabled, the following variables are available for use in headers or other templated fields:
signature
: The generated signature string (base64 encoded)signatureTimestamp
: The Unix timestamp when the signature was generatedOption | Type | Required | Default | Description |
---|---|---|---|---|
type | string | No | 'pem' | Certificate type: 'pem', 'jks', or 'pfx' |
privateKeyPath | string | No* | - | Path to the PEM private key file used for signing (PEM type only) |
privateKey | string | No* | - | PEM private key string (if not using privateKeyPath, PEM type only) |
keystorePath | string | No* | - | Path to the JKS keystore file (JKS type only) |
keystorePassword | string | No | - | JKS keystore password (JKS type only, can use PROMPTFOO_JKS_PASSWORD env var) |
keyAlias | string | No | First available alias | JKS key alias to use (JKS type only) |
pfxPath | string | No* | - | Path to the PFX certificate file (PFX type only) |
pfxPassword | string | No | - | PFX certificate password (PFX type only, can use PROMPTFOO_PFX_PASSWORD env var) |
certPath | string | No* | - | Path to the certificate file (PFX type only, alternative to pfxPath) |
keyPath | string | No* | - | Path to the private key file (PFX type only, alternative to pfxPath) |
clientId | string | Yes | - | Client identifier used in signature generation |
signatureValidityMs | number | No | 300000 | Validity period of the signature in milliseconds |
signatureAlgorithm | string | No | 'SHA256' | Signature algorithm to use (any supported by Node.js crypto) |
signatureDataTemplate | string | No | '{{clientId}}{{timestamp}}' | Template for formatting the data to be signed. Note: \n in the template will be interpreted as a newline character. |
signatureRefreshBufferMs | number | No | 10% of validityMs | Buffer time before expiry to refresh signature |
* Requirements depend on certificate type:
privateKeyPath
or privateKey
must be providedkeystorePath
must be providedpfxPath
or both certPath
and keyPath
must be providedThe HTTP provider automatically retries failed requests in the following scenarios:
By default, it will attempt up to 4 retries with exponential backoff. You can configure the maximum number of retries using the maxRetries
option:
providers:
- id: http
config:
url: https://api.example.com/v1/chat
maxRetries: 2 # Override default of 4 retries
Supported config options:
Option | Type | Description |
---|---|---|
url | string | The URL to send the HTTP request to. Supports Nunjucks templates. If not provided, the id of the provider will be used as the URL. |
request | string | A raw HTTP request to send. This will override the url , method , headers , body , and queryParams options. |
method | string | HTTP method (GET, POST, etc). Defaults to POST if body is provided, GET otherwise. |
headers | Record<string, string> | Key-value pairs of HTTP headers to include in the request. |
body | object | string | The request body. For POST requests, objects are automatically stringified as JSON. |
queryParams | Record<string, string> | Key-value pairs of query parameters to append to the URL. |
transformRequest | string | Function | A function, string template, or file path to transform the prompt before sending it to the API. |
transformResponse | string | Function | Transforms the API response using a JavaScript expression (e.g., 'json.result'), function, or file path (e.g., 'file://parser.js'). Replaces the deprecated responseParser field. |
tokenEstimation | object | Configuration for optional token usage estimation. See Token Estimation section above for details. |
maxRetries | number | Maximum number of retry attempts for failed requests. Defaults to 4. |
validateStatus | Function | A function that takes a status code and returns a boolean indicating if the response should be treated as successful. By default, accepts all status codes. |
signatureAuth | object | Configuration for digital signature authentication. See Signature Auth Options below. |
Option | Type | Required | Default | Description |
---|---|---|---|---|
type | string | No | 'pem' | Certificate type: 'pem', 'jks', or 'pfx' |
privateKeyPath | string | No* | - | Path to the PEM private key file used for signing (PEM type only) |
privateKey | string | No* | - | PEM private key string (if not using privateKeyPath, PEM type only) |
keystorePath | string | No* | - | Path to the JKS keystore file (JKS type only) |
keystorePassword | string | No | - | JKS keystore password (JKS type only, can use PROMPTFOO_JKS_PASSWORD env var) |
keyAlias | string | No | First available alias | JKS key alias to use (JKS type only) |
pfxPath | string | No* | - | Path to the PFX certificate file (PFX type only) |
pfxPassword | string | No | - | PFX certificate password (PFX type only, can use PROMPTFOO_PFX_PASSWORD env var) |
certPath | string | No* | - | Path to the certificate file (PFX type only, alternative to pfxPath) |
keyPath | string | No* | - | Path to the private key file (PFX type only, alternative to pfxPath) |
clientId | string | Yes | - | Client identifier used in signature generation |
signatureValidityMs | number | No | 300000 | Validity period of the signature in milliseconds |
signatureAlgorithm | string | No | 'SHA256' | Signature algorithm to use (any supported by Node.js crypto) |
signatureDataTemplate | string | No | '{{clientId}}{{timestamp}}' | Template for formatting the data to be signed. Note: \n in the template will be interpreted as a newline character. |
signatureRefreshBufferMs | number | No | 10% of validityMs | Buffer time before expiry to refresh signature |
* Requirements depend on certificate type:
privateKeyPath
or privateKey
must be providedkeystorePath
must be providedpfxPath
or both certPath
and keyPath
must be providedIn addition to a full URL, the provider id
field accepts http
or https
as values.
Use the generator below to create an HTTP provider configuration based on your endpoint:
import { HttpConfigGenerator } from '@site/src/components/HttpConfigGenerator';
The HTTP provider throws errors for:
validateStatus
is set)By default, all response status codes are accepted. You can customize this using the validateStatus
option:
providers:
- id: https
config:
url: 'https://example.com/api'
# Function-based validation
validateStatus: (status) => status < 500 # Accept any status below 500
# Or string-based expression
validateStatus: 'status >= 200 && status <= 299' # Accept only 2xx responses
# Or load from file
validateStatus: 'file://validators/status.js' # Load default export
validateStatus: 'file://validators/status.js:validateStatus' # Load specific function
Example validator file (validators/status.js
):
export default (status) => status < 500;
// Or named export
export function validateStatus(status) {
return status < 500;
}
The provider automatically retries certain errors (like rate limits) based on maxRetries
, while other errors are thrown immediately.
Press p or to see the previous file or, n or to see the next file
Are you sure you want to delete this access key?
Are you sure you want to delete this access key?
Are you sure you want to delete this access key?
Are you sure you want to delete this access key?