SDK Reference: Confidential HTTP Client
The Confidential HTTP Client lets you make privacy-preserving requests to external APIs from your workflow. Unlike the regular HTTPClient, the request executes inside a secure enclave, secrets are injected via templates, and responses can be optionally encrypted.
- For use cases and a conceptual overview, see The Confidential HTTP Capability
- Guide: Making Confidential Requests
Quick reference
| Method | Description |
|---|---|
sendRequest (high-level) | Recommended. Makes a confidential HTTP request with built-in consensus and typing. |
sendRequest (low-level) | Makes a confidential HTTP request manually inside a runInNodeMode block. |
Core types
ConfidentialHTTPRequest / ConfidentialHTTPRequestJson
The top-level request type that combines an HTTP request with Vault DON secrets and encryption settings.
Field | Type | Description |
|---|---|---|
request | HTTPRequest | HTTPRequestJson | The HTTP request to execute inside the enclave. See HTTPRequest. |
vaultDonSecrets | SecretIdentifier[] | SecretIdentifierJson[] | List of secrets to fetch from the Vault DON and make available in the enclave. See SecretIdentifier. |
encryptOutput | boolean | If true, encrypts the response body before it leaves the enclave. See Response encryption. Default: false. |
HTTPRequest / HTTPRequestJson
Defines the HTTP request that will be executed inside the enclave.
Field | Type | Description |
|---|---|---|
url | string | The URL of the API endpoint. |
method | string | The HTTP method (e.g., "GET", "POST"). |
bodyString | string (optional) | The request body as a string template. Use this for secret injection with {{.secretName}} placeholders. |
bodyBytes | Uint8Array | string (optional) | The request body as raw bytes (base64-encoded in JSON format). |
multiHeaders | { [key: string]: HeaderValues } (optional) | Request headers. Supports multiple values per key and template syntax for secret injection. |
templatePublicValues | { [key: string]: string } (optional) | Public (non-secret) values used to fill template placeholders in the body and headers. |
customRootCaCertPem | Uint8Array | string (optional) | Optional custom root CA certificate (PEM format) for verifying the external server's TLS certificate. |
timeout | Duration | DurationJson (optional) | Optional request timeout (e.g., "5s"). |
HTTPResponse / HTTPResponseJson
The response returned from the enclave after the HTTP request completes.
Field | Type | Description |
|---|---|---|
statusCode | number | The HTTP status code. |
body | Uint8Array | string (base64) | The response body. If encryptOutput is true, this contains the encrypted body (see Response encryption). |
multiHeaders | { [key: string]: HeaderValues } | The HTTP response headers. |
SecretIdentifier / SecretIdentifierJson
Identifies a secret stored in the Vault DON.
| Field | Type | Description |
|---|---|---|
key | string | The logical name of the secret. Must match the template placeholder (e.g., "myApiKey" matches {{.myApiKey}}). |
namespace | string | The secret namespace. |
owner | string (optional) | Optional. The owner address for the secret. |
HeaderValues / HeaderValuesJson
Represents multiple values for a single HTTP header key.
Field | Type | Description |
|---|---|---|
values | string[] | The header values. Supports template syntax for secret injection (e.g., "Basic {{.myToken}}"). |
ConfidentialHTTPSendRequester
The object passed to your fetch function when using the high-level sendRequest pattern. It wraps the low-level client and provides a single method:
| Method | Description |
|---|---|
sendRequest(input: ConfidentialHTTPRequest | ConfidentialHTTPRequestJson): { result: () => HTTPResponse } | Sends a confidential HTTP request and returns the response. |
You don't create this object yourself — it is provided by the SDK when your function is called.
Making requests
sendRequest()
Recommended. The high-level sendRequest method handles runInNodeMode wrapping and consensus for you. You provide a function that receives a ConfidentialHTTPSendRequester and your arguments, and the SDK takes care of the rest.
Signature:
sendRequest<TArgs extends unknown[], TOutput>(
runtime: Runtime<unknown>,
fn: (sendRequester: ConfidentialHTTPSendRequester, ...args: TArgs) => TOutput,
consensusAggregation: ConsensusAggregation<TOutput, true>,
unwrapOptions?: UnwrapOptions<TOutput>,
): (...args: TArgs) => { result: () => TOutput }
Parameters:
runtime: TheRuntimeinstance from your trigger handler.fn: A function that receives aConfidentialHTTPSendRequesterplus any additional arguments you pass. Use thesendRequesterto make the confidential request and return the parsed result.consensusAggregation: The consensus strategy (e.g.,consensusIdenticalAggregation(),consensusMedianAggregation()).unwrapOptions(optional): Advanced option for controlling how complex (non-primitive) output types are unwrapped during consensus. Not needed for most use cases.
Returns:
A curried function that accepts ...args and returns an object with a .result() method.
Example:
import {
ConfidentialHTTPClient,
consensusIdenticalAggregation,
ok,
json,
type ConfidentialHTTPSendRequester,
type Runtime,
} from "@chainlink/cre-sdk"
type Config = { url: string; owner: string }
type APIResult = { data: string }
// 1. Define your fetch function
const fetchData = (sendRequester: ConfidentialHTTPSendRequester, config: Config): APIResult => {
const response = sendRequester
.sendRequest({
request: {
url: config.url,
method: "GET",
multiHeaders: {
Authorization: { values: ["Basic {{.apiKey}}"] },
},
},
vaultDonSecrets: [{ key: "apiKey", owner: config.owner }],
})
.result()
if (!ok(response)) {
throw new Error(`Request failed: ${response.statusCode}`)
}
return json(response) as APIResult
}
// 2. Call sendRequest in your trigger handler
const onCronTrigger = (runtime: Runtime<Config>): string => {
const confHTTPClient = new ConfidentialHTTPClient()
const result = confHTTPClient
.sendRequest(runtime, fetchData, consensusIdenticalAggregation<APIResult>())(runtime.config)
.result()
return result.data
}
Low-level sendRequest()
The low-level overload gives you direct access to the client within a runtime.runInNodeMode() block. For Confidential HTTP, this offers minimal practical advantages over the high-level pattern because the API call already executes as a single request inside the enclave. Use this only if you have a specific reason to manage the runInNodeMode wrapping manually.
Signature:
sendRequest(
runtime: NodeRuntime<unknown>,
input: ConfidentialHTTPRequest | ConfidentialHTTPRequestJson
): { result: () => HTTPResponse }
Parameters:
runtime: ANodeRuntimeinstance provided byruntime.runInNodeMode().input: AConfidentialHTTPRequestorConfidentialHTTPRequestJsonobject containing the request, secrets, and encryption settings.
Returns:
An object with a .result() method that blocks until the request completes and returns the HTTPResponse.
Example:
import {
ConfidentialHTTPClient,
consensusIdenticalAggregation,
type Runtime,
type NodeRuntime,
} from "@chainlink/cre-sdk"
type Config = { url: string }
const fetchData = (nodeRuntime: NodeRuntime<Config>): string => {
const client = new ConfidentialHTTPClient()
const resp = client
.sendRequest(nodeRuntime, {
request: {
url: nodeRuntime.config.url,
method: "GET",
multiHeaders: {
Authorization: { values: ["Basic {{.apiKey}}"] },
},
},
vaultDonSecrets: [{ key: "apiKey" }],
encryptOutput: false,
})
.result()
if (resp.statusCode !== 200) {
throw new Error(`Request failed with status: ${resp.statusCode}`)
}
const bodyText = new TextDecoder().decode(resp.body)
return bodyText
}
// In your workflow
const onCronTrigger = (runtime: Runtime<Config>): string => {
const result = runtime.runInNodeMode(fetchData, consensusIdenticalAggregation<string>())().result()
runtime.log(`Result: ${result}`)
return result
}
Template syntax
Secrets are injected into the request body and headers using Go template syntax: {{.secretName}}. The placeholder name must match the key field in the corresponding SecretIdentifier.
Body template:
bodyString: '{"apiKey": "{{.myApiKey}}", "method": "{{.method}}", "params": []}',
Header template:
multiHeaders: {
"Authorization": { values: ["Basic {{.myCredential}}"] },
},
templatePublicValues (optional)
Every {{.placeholder}} in your body or headers is resolved inside the enclave. By default, placeholders are filled with secrets from vaultDonSecrets. But sometimes you have a placeholder value that isn't secret — for example, an RPC method name or a public parameter. That's what templatePublicValues is for: it lets you inject non-secret values into the same template.
This is purely a convenience. You could always hardcode the value directly in the body string instead:
// These two are equivalent:
// Option 1: hardcoded in the body string
bodyString: '{"method": "eth_blockNumber", "auth": "{{.apiKey}}"}'
// Option 2: using templatePublicValues
bodyString: '{"method": "{{.method}}", "auth": "{{.apiKey}}"}'
templatePublicValues: {
method: "eth_blockNumber"
}
templatePublicValues is useful when you want to keep the template generic and pass in dynamic values (e.g., from config) without string concatenation.
Example with both secret and public values:
request: {
url: config.url,
method: "POST",
bodyString: '{"method": "{{.rpcMethod}}", "auth": "{{.apiKey}}"}',
templatePublicValues: {
rpcMethod: config.rpcMethod, // dynamic value from config, not a secret
},
},
vaultDonSecrets: [{ key: "apiKey", owner: config.owner }], // secret, from Vault DON
In this example, {{.rpcMethod}} is resolved from templatePublicValues (a dynamic, non-secret value from your workflow config) and {{.apiKey}} is resolved from the Vault DON (a secret). Both are resolved inside the enclave.
Response encryption
The encryptOutput field controls whether the response body is encrypted before leaving the enclave.
| encryptOutput | Secret key provided | Behavior |
|---|---|---|
false (default) | — | Response returned unencrypted. |
true | san_marino_aes_gcm_encryption_key in vaultDonSecrets | Response AES-GCM encrypted with your symmetric key. |
true | No key provided | Response TDH2 encrypted with the Vault DON master public key. |
AES-GCM encryption is the recommended approach. Store a 256-bit (32-byte) AES key as a Vault DON secret with the identifier san_marino_aes_gcm_encryption_key, then decrypt the response in your own backend.
The encrypted response body is structured as nonce || ciphertext || tag.
For a complete example with response encryption, see the Making Confidential Requests guide.