# HTTP Webhook Destination Config Reference


> Complete Rudder CLI reference for the HTTP Webhook destination config keys, authentication, request mapping, batching, and secrets.



[HTTP Webhook](https://www.rudderstack.com/docs/destinations/http-webhook/setup-guide/) sends each event to an HTTP endpoint you own. You choose the method, body format, authentication, and — when the default passthrough doesn't suit the endpoint — how RudderStack maps the event onto the outgoing request.

In an HTTP Webhook destination spec:

- `type: http`
- `definition_version: 1`

## Sample configuration

```yaml
version: rudder/v1
kind: destination
metadata:
  name: orders-webhook-prod
spec:
  id: orders-webhook-prod
  display_name: Orders Webhook Production
  type: http
  definition_version: 1
  enabled: true
  config:
    api_url: https://api.example.com/v1/events
    method: POST
    format: JSON

    auth: apiKeyAuth
    api_key_name: "{{ .WEBHOOK_API_KEY_NAME }}"
    api_key_value: "{{ .WEBHOOK_API_KEY }}"

    is_default_mapping: false
    properties_mapping:
      - to: $.eventName
        from: $.event
      - to: $.customer.id
        from: $.userId
    query_params:
      - to: source
        from: rudderstack
    headers:
      - to: X-Request-Source
        from: "{{ .WEBHOOK_HEADER_VALUE }}"
    path_params:
      - path: $.properties.accountId

    is_batching_enabled: true
    max_batch_size: "50"

    connection_mode:
      web: cloud
      cloud: cloud
    consent_management:
      web:
        - provider: oneTrust
          consents:
            - analytics
```

The above example uses API key authentication and custom request mapping. `xml_root_key` is omitted because it applies only when `format` is `XML`. Several keys apply only in certain combinations — see [Key dependencies](#key-dependencies).

## Config keys

`config` accepts only the keys listed below. The [shared config key rules](https://www.rudderstack.com/docs/rudder-cli/destination-types/#config-key-rules) cover unknown keys, defaults, and immutability.

### Key dependencies

Several keys depend on another key's value. Rudder CLI enforces the **Required** rows below: the credentials for the chosen `auth`, and `max_batch_size` whenever batching is on, for any `format`. It accepts the **Applies** rows whatever the other key says, so a key that doesn't apply is stored and ignored rather than rejected.

| Key | Depends on |
| :-----| :-----|
| `username`, `password` | **Required** when `auth` is `basicAuth` |
| `bearer_token` | **Required** when `auth` is `bearerTokenAuth` |
| `api_key_name`, `api_key_value` | **Required** when `auth` is `apiKeyAuth` |
| `max_batch_size` | **Required** when `is_batching_enabled` is `true` |
| `is_batching_enabled` | **Applies** when `format` is `JSON` |
| `xml_root_key` | **Applies** when `format` is `XML` |
| `properties_mapping` | **Applies** when `is_default_mapping` is `false` |

### Endpoint

<div class="rs-ckeys">
<div class="rs-ckey" id="api_url">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">api_url</p>Required</div>
  <p class="rs-ckey__field">
    Type:
    <code>string</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Endpoint RudderStack sends events to. Both <code>http</code> and <code>https</code> are accepted; an <code>https</code> endpoint needs a valid TLS certificate for delivery to succeed.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>Must be a public domain URL: a scheme, at least one dot-separated label followed by an alphabetic top-level domain, an optional port, and an optional path.</li>
<li>A bare hostname, a bare IP address, <code>localhost</code> in any form, and <code>ngrok.io</code> addresses are all rejected.</li>
</ul>

</div>

<div class="rs-ckey__extra">
  To append path or query segments built from the event, use <code>path_params</code> and <code>query_params</code> rather than writing them into this value.
</div>

</div>

<div class="rs-ckey" id="method">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">method</p>Required</div>
  <p class="rs-ckey__field">
    Type:
    <code>string</code>
  </p>
<p class="rs-ckey__field">
  Description:
  HTTP method of the outgoing request: <code>POST</code>, <code>PUT</code>, <code>PATCH</code>, <code>GET</code>, or <code>DELETE</code>.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>The dashboard defaults this to <code>POST</code>. Rudder CLI requires it explicitly.</li>
<li>A spec that omits this key fails validation.</li>
</ul>

</div>

</div>

<div class="rs-ckey" id="format">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">format</p>Required</div>
  <p class="rs-ckey__field">
    Type:
    <code>string</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Body format of the outgoing request: <code>JSON</code>, <code>XML</code>, or <code>FORM</code>.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>The dashboard defaults this to <code>JSON</code>. Rudder CLI requires it explicitly.</li>
<li>A spec that omits this key fails validation.</li>
<li>The value also decides whether <code>xml_root_key</code> and <code>is_batching_enabled</code> apply.</li>
</ul>

</div>

</div>

<div class="rs-ckey" id="xml_root_key">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">xml_root_key</p></div>
  <p class="rs-ckey__field">
    Type:
    <code>string</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Root key wrapping every mapped field in the request body.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>Applies when <code>format</code> is <code>XML</code>.</li>
<li>At most 100 characters, and must not contain line breaks.</li>
</ul>

</div>

</div>

</div>

### Authentication

<div class="rs-ckeys">
<div class="rs-ckey" id="auth">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">auth</p>Required</div>
  <p class="rs-ckey__field">
    Type:
    <code>string</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Authentication method for the request: <code>noAuth</code>, <code>basicAuth</code>, <code>bearerTokenAuth</code>, or <code>apiKeyAuth</code>.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>The dashboard defaults this to <code>noAuth</code>. Rudder CLI requires it explicitly.</li>
<li>A spec that omits this key fails validation.</li>
<li>The value decides which credential keys below are required.</li>
</ul>

</div>

</div>

<div class="rs-ckey" id="username">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">username</p>RequiredSecret</div>
  <p class="rs-ckey__field">
    Type:
    <code>string</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Username for basic authentication.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>Required when <code>auth</code> is <code>basicAuth</code>.</li>
<li>1 to 100 characters, and must not contain line breaks.</li>
</ul>

</div>

</div>

<div class="rs-ckey" id="password">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">password</p>RequiredSecret</div>
  <p class="rs-ckey__field">
    Type:
    <code>string</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Password for basic authentication.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>Required when <code>auth</code> is <code>basicAuth</code>.</li>
<li>At most 100 characters, and must not contain line breaks.</li>
</ul>

</div>

</div>

<div class="rs-ckey" id="bearer_token">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">bearer_token</p>RequiredSecret</div>
  <p class="rs-ckey__field">
    Type:
    <code>string</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Token sent in the <code>Authorization</code> header.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>Required when <code>auth</code> is <code>bearerTokenAuth</code>.</li>
<li>1 to 2048 characters, and must not contain line breaks.</li>
</ul>

</div>

</div>

<div class="rs-ckey" id="api_key_name">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">api_key_name</p>RequiredSecret</div>
  <p class="rs-ckey__field">
    Type:
    <code>string</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Name of the header carrying the API key — for example <code>X-Api-Key</code>, which is what the dashboard prefills.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>Required when <code>auth</code> is <code>apiKeyAuth</code>.</li>
<li>1 to 100 characters, with no whitespace.</li>
</ul>

</div>

<div class="rs-ckey__extra">
  Rudder CLI treats the header <strong>name</strong> as a secret as well as its value, so write it as a <code>{{ .VAR }}</code> reference — see <a href="#secrets" >Secrets</a>.
</div>

</div>

<div class="rs-ckey" id="api_key_value">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">api_key_value</p>RequiredSecret</div>
  <p class="rs-ckey__field">
    Type:
    <code>string</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Value of the API key header.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>Required when <code>auth</code> is <code>apiKeyAuth</code>.</li>
<li>1 to 100 characters, and must not contain line breaks.</li>
</ul>

</div>

</div>

</div>

### Request mapping

By default the source event is sent through unmodified. The keys in this group either turn that off and reshape the body, or add to the request URL and headers — those additions apply either way.

Each mapping entry pairs a `from` (where the value comes from) with a `to` (where it lands on the outgoing request). A `from` is a [JSONPath](https://en.wikipedia.org/wiki/JSONPath) into the source event, such as `$.properties.orderId`, or a bare constant such as `rudderstack`. A quoted constant like `"EUR"` is not valid.

**Warning:** If you define more than one mapping for the same key, only the first is used and the rest are ignored.

<div class="rs-ckeys">
<div class="rs-ckey" id="is_default_mapping">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">is_default_mapping</p></div>
  <p class="rs-ckey__field">
    Type:
    <code>boolean</code>
  </p><p class="rs-ckey__field">
    Default value:
    <code>true</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Send the source event payload as it is, without applying <code>properties_mapping</code>. This is what the dashboard calls <strong>Send the event payload as is</strong>.
</p>

<div class="rs-ckey__extra">
  Set it to <code>false</code> to shape the body yourself. When <code>format</code> is <code>XML</code> and this stays <code>true</code>, the unmodified payload is sent under <code>xml_root_key</code>.
</div>

</div>

<div class="rs-ckey" id="properties_mapping">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">properties_mapping</p></div>
  <p class="rs-ckey__field">
    Type:
    <code>array of objects</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Maps fields of the source event onto the request body.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>Applies when <code>is_default_mapping</code> is <code>false</code>.</li>
<li><code>to</code> — JSONPath of the field on the outgoing request. Must be a JSONPath or empty; a bare token like <code>properties.value</code> is rejected.</li>
<li><code>from</code> — JSONPath into the source event, or a constant of up to 100 characters from letters, digits, and <code>! # $ % &amp; ' * + . ^ _ ` | ~ -</code>. Spaces and characters such as <code>/ : @ ;</code> are rejected.</li>
</ul>

</div>

<div class="rs-ckey__extra">
  <div class="rs-code">
  <div class="rs-code__head">yaml<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">properties_mapping:
  - to: $.messageType
    from: $.type
  - to: $.customer.firstName
    from: $.traits.firstName</code></pre></div>
</div>
</div>

</div>

<div class="rs-ckey" id="query_params">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">query_params</p></div>
  <p class="rs-ckey__field">
    Type:
    <code>array of objects</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Query parameters appended to <code>api_url</code>.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li><code>to</code> — parameter name. Must be a plain token of up to 100 characters, not a JSONPath.</li>
<li><code>from</code> — JSONPath into the source event, or a constant of up to 100 characters from letters, digits, spaces, and <code>! # $ % &amp; ' * + . ^ _ ` | ~ -</code>. Characters such as <code>/ : @ ;</code> are rejected.</li>
</ul>

</div>

<div class="rs-ckey__extra">
  <div class="rs-code">
  <div class="rs-code__head">yaml<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">query_params:
  - to: source
    from: rudderstack
  - to: order_id
    from: $.properties.orderId</code></pre></div>
</div>
</div>

</div>

<div class="rs-ckey" id="headers">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">headers</p></div>
  <p class="rs-ckey__field">
    Type:
    <code>array of objects</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Headers added to the outgoing request.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li><code>to</code> — header name. Must be a plain token of up to 100 characters, not a JSONPath.</li>
<li><code>from</code> — JSONPath into the source event, or a constant of up to 100 characters from letters, digits, spaces, <code>/</code>, <code>\</code>, and <code>! # $ % &amp; ' * + . ^ _ ` | ~ -</code>. Characters such as <code>: ; = ,</code> are rejected. <strong>Secret</strong> — see <a href="#secrets" >Secrets</a>.</li>
</ul>

</div>

<div class="rs-ckey__extra">
  <div class="rs-code">
  <div class="rs-code__head">yaml<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">headers:
  - to: X-Request-Source
    from: rudderstack</code></pre></div>
</div>
</div>

</div>

<div class="rs-ckey" id="path_params">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">path_params</p></div>
  <p class="rs-ckey__field">
    Type:
    <code>array of objects</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Path segments appended to <code>api_url</code>, in the order listed.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li><code>path</code> — JSONPath into the source event, or a plain token of up to 100 characters. A quoted value like <code>&quot;order&quot;</code> is rejected.</li>
</ul>

</div>

<div class="rs-ckey__extra">
  <div class="rs-code">
  <div class="rs-code__head">yaml<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">path_params:
  - path: accounts
  - path: $.properties.accountId</code></pre></div>
</div>
</div>

</div>

</div>

### Batching

<div class="rs-ckeys">
<div class="rs-ckey" id="is_batching_enabled">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">is_batching_enabled</p></div>
  <p class="rs-ckey__field">
    Type:
    <code>boolean</code>
  </p><p class="rs-ckey__field">
    Default value:
    <code>false</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Collect events and send them as a JSON array — <code>[{event1},{event2},...]</code> — instead of one request per event.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>Applies when <code>format</code> is <code>JSON</code>.</li>
<li>A batch is sent when it reaches <code>max_batch_size</code>, or after five seconds, whichever comes first.</li>
</ul>

</div>

</div>

<div class="rs-ckey" id="max_batch_size">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">max_batch_size</p>Required</div>
  <p class="rs-ckey__field">
    Type:
    <code>string</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Largest number of events in one batch, written as a string rather than a number.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>Required when <code>is_batching_enabled</code> is <code>true</code>.</li>
<li>Applies when <code>format</code> is <code>JSON</code>.</li>
<li>A string integer from <code>1</code> to <code>100</code>.</li>
</ul>

</div>

<div class="rs-ckey__extra">
  <div class="rs-code">
  <div class="rs-code__head">yaml<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">is_batching_enabled: true
max_batch_size: &#34;50&#34;</code></pre></div>
</div>
</div>

</div>

</div>

### Event filtering

<div class="rs-ckeys">
<div class="rs-ckey" id="event_filtering">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">event_filtering</p></div>
  <p class="rs-ckey__field">
    Type:
    <code>object</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Restricts which <code>track</code> events reach the destination, by event name.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li><code>whitelist</code> — array of event names to allow; every other <code>track</code> event is dropped.</li>
<li><code>blacklist</code> — array of event names to drop; every other <code>track</code> event is allowed.</li>
<li>The two are mutually exclusive, and Rudder CLI enforces it — setting both fails validation. Omit the block entirely to filter nothing.</li>
<li>Each name is at most 100 characters, or a <code>{{ path || fallback }}</code> template.</li>
</ul>

</div>

<div class="rs-ckey__extra">
  <div class="rs-code">
  <div class="rs-code__head">yaml<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">event_filtering:
  whitelist:
    - Order Completed
    - Order Refunded</code></pre></div>
</div><p><strong>Warning</strong></p>
<p><a href="https://www.rudderstack.com/docs/sources/event-streams/sdks/event-filtering/" >Client-side event filtering</a> is applied by the device SDK, so it covers destinations reached in device mode. HTTP Webhook connects in <code>cloud</code> mode only, for every source type, which is why the dashboard doesn't offer these controls for it. Rudder CLI accepts and sends the keys regardless.</p>
<p>Filter events with a <a href="https://www.rudderstack.com/docs/rudder-cli/manage-transformations/" >transformation</a> instead.</p>

</div>

</div>

</div>

### Per-source keys

Both keys are objects keyed by the local source type — the tokens listed under [Source types](#source-types). A key naming a source type this destination doesn't support fails validation.

<div class="rs-ckeys">
<div class="rs-ckey" id="connection_mode">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">connection_mode</p></div>
  <p class="rs-ckey__field">
    Type:
    <code>object</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Maps each source type you connect to the mode its events reach the endpoint in, using the modes in <a href="#source-types" >Source types</a>.
</p>

<div class="rs-ckey__notes">
  <p class="rs-ckey__field">Notes:</p>
  <ul>
<li>An entry is required for each source type you connect — see <a href="#connect-a-source" >Connect a source</a>.</li>
</ul>

</div>

<div class="rs-ckey__extra">
  <div class="rs-code">
  <div class="rs-code__head">yaml<button class="rs-code__copy" type="button">
      
      Copy
    </button>
  </div>
  <div class="highlight"><pre class="chroma"><code class="language-yaml" data-lang="yaml">connection_mode:
  web: cloud
  cloud: cloud</code></pre></div>
</div>
</div>

</div>

<div class="rs-ckey" id="consent_management">
  <div class="rs-ckey__title">
    <p class="rs-ckey__name">consent_management</p></div>
  <p class="rs-ckey__field">
    Type:
    <code>object</code>
  </p>
<p class="rs-ckey__field">
  Description:
  Consent provider configuration per source type. The entry shape, accepted providers, and the rules on <code>resolution_strategy</code> and <code>consents</code> are shared across all destination types — see <a href="https://www.rudderstack.com/docs/rudder-cli/destination-types/#consent-management" >Consent management</a>.
</p>

</div>

</div>

## Source types

HTTP Webhook accepts events from these source types in the mentioned connection modes:

| Source type | Connection mode |
| :-----| :-----|
| `android` | `cloud` |
| `android_kotlin` | `cloud` |
| `ios` | `cloud` |
| `ios_swift` | `cloud` |
| `web` | `cloud` |
| `unity` | `cloud` |
| `react_native` | `cloud` |
| `flutter` | `cloud` |
| `cordova` | `cloud` |
| `cloud` | `cloud` |
| `warehouse` | `cloud` |

Every source type is `cloud` only — events reach the endpoint from RudderStack's servers, never in device mode.

`warehouse` is the token a [Reverse ETL source](https://www.rudderstack.com/docs/sources/reverse-etl/) resolves to. Rudder CLI manages the destination’s `warehouse` entry — if the destination is connected to a Reverse ETL source, set `connection_mode.warehouse: cloud` in its spec, or `apply` removes it.

Rudder CLI can also declare the Reverse ETL connection itself, for the types listed under [Supported destinations](https://www.rudderstack.com/docs/rudder-cli/yaml-retl-connections/#supported-destinations) — see [Manage Reverse ETL Connections using Rudder CLI](https://www.rudderstack.com/docs/rudder-cli/retl-connections/).

**Info:** The dashboard additionally offers HTTP Webhook to AMP and Shopify sources. Rudder CLI doesn&#39;t manage those connections, so `amp` and `shopify` are invalid here.

## Connect a source

An event stream connection to this destination is checked against two rules at `validate` time.

**The source's type must be supported.** A source's type resolves to one of the tokens above before the check — a JavaScript source resolves to `web`, and webhook and server-side SDK sources resolve to `cloud`. Every type an event stream source can resolve to is supported here, so this check always passes.

**The destination config must carry a `connection_mode` entry for that source type.** This lives on the destination spec, not on the [connection spec](https://www.rudderstack.com/docs/rudder-cli/yaml-connections/). Without it:

```text
destination 'orders-webhook-prod' config has no 'connection_mode' entry for source type 'web'
```

HTTP Webhook needs no additional config keys to connect a source of any type.

## Secrets

Rudder CLI treats six keys as secrets: `username`, `password`, `bearer_token`, `api_key_name`, `api_key_value`, and every `headers` entry's `from` value. Write each as a `{{ .VAR }}` reference and supply the value at apply time:

```yaml
config:
  auth: apiKeyAuth
  api_key_name: "{{ .WEBHOOK_API_KEY_NAME }}"
  api_key_value: "{{ .WEBHOOK_API_KEY }}"
  headers:
    - to: X-Request-Source
      from: "{{ .WEBHOOK_HEADER_VALUE }}"
```

```bash
export RUDDER_WEBHOOK_API_KEY_NAME="x-api-key"
export RUDDER_WEBHOOK_API_KEY="..."
export RUDDER_WEBHOOK_HEADER_VALUE="..."
rudder-cli apply

# or
rudder-cli apply --var-file secrets.vars.yaml
```

Note that:

- `api_key_name` is secret even though it's the header name, not the key itself.
- Every header value is secret, including constants such as `application/json`. A header value that's a JSONPath, such as `$.messageId`, is read from each event, so `validate` doesn't ask for a variable there.
- `rudder-cli import` writes only the secrets the API returns, as `{{ .VAR }}` references, and leaves the others out. Add any secret key it leaves out, then supply the variables before you apply — see [Secrets](https://www.rudderstack.com/docs/rudder-cli/destination-types/#secrets).

See [How to Use Variable Substitution in Rudder CLI](https://www.rudderstack.com/docs/rudder-cli/variable-substitution/).

## Changes by release

<table>
  <thead>
    <tr>
      <th>Version</th>
      <th>Change</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>v0.29.0</td>
      <td><strong>Breaking:</strong> A <code>null</code> config value, written as <code>key: null</code>, <code>key: ~</code>, or <code>key:</code> with no value, fails <code>validate</code> with <code>'&lt;key&gt;' must not be null; set a value or remove the key</code>. Earlier releases accepted it: a top-level null planned an update on every <code>apply</code>, and a null inside a list item failed at the API.<br><strong>What to do:</strong> Remove the key to leave it unset, or give it a value.</td>
    </tr>
    <tr>
      <td>v0.29.0</td>
      <td><strong>Breaking:</strong> An empty string, <code>&quot;&quot;</code>, on a key that takes one of a fixed set of values fails <code>validate</code> with <code>'&lt;key&gt;' must be one of [...]</code>, unless <code>&quot;&quot;</code> is one of the key&rsquo;s values. Earlier releases accepted it, and the API rejected it at <code>apply</code>.<br><strong>What to do:</strong> Remove the key to leave it unset, or set one of the listed values.</td>
    </tr>
    <tr>
      <td>v0.29.0</td>
      <td><strong>Changed:</strong> <code>validate</code> warns when a secret key holds anything but one <code>{{ .VAR }}</code> reference with no default, and when one variable fills two secret keys. It also masks secret values in the lines it prints.</td>
    </tr>
    <tr>
      <td>v0.29.0</td>
      <td><strong>Changed:</strong> <code>rudder-cli import</code> writes each secret reference single-quoted, as <code>'{{ .VAR }}'</code>, so a value containing double quotes, like a JSON key file, substitutes as a string. A single quote in a value that fills such a reference is written as <code>''</code>.<br><strong>What to do:</strong> In a spec imported by an earlier release, quote each reference to a JSON value by hand: <code>'{{ .VAR }}'</code>.</td>
    </tr>
    <tr>
      <td>v0.29.0</td>
      <td><strong>Changed:</strong> <code>validate</code> reports a value of the wrong type once per key, such as <code>'email' must be a string</code>, instead of one raw decoding error.</td>
    </tr>
    <tr>
      <td>v0.26.0</td>
      <td><strong>Changed:</strong> <code>username</code>, <code>api_key_name</code>, and every <code>headers</code> entry&rsquo;s <code>from</code> value are secret keys. Write them as <code>{{ .VAR }}</code> references.</td>
    </tr>
    <tr>
      <td>v0.26.0</td>
      <td><strong>Added:</strong> <code>warehouse</code> source type, for destinations connected to a Reverse ETL source. Set <code>connection_mode.warehouse: cloud</code> for such a destination, or <code>apply</code> removes the entry.</td>
    </tr>
  </tbody>
</table>

For changes across every destination type, see [Destination Type Changes by Release](https://www.rudderstack.com/docs/rudder-cli/destination-types/changes/).

## See more

- [HTTP Webhook Destination](https://www.rudderstack.com/docs/destinations/http-webhook/setup-guide/) for the mapping UI, worked payload examples, and FAQ
- [Destination Type Reference for Rudder CLI](https://www.rudderstack.com/docs/rudder-cli/destination-types/) for the rules shared across destination types
- [Destination YAML Reference](https://www.rudderstack.com/docs/rudder-cli/yaml-destinations/) for the spec envelope

