Skip to content

Custom headers ​

Sometimes every request needs a header the WB spec knows nothing about: credentials for a gateway or corporate proxy sitting in front of the API, a trace identifier, an environment tag. Every client can add them — the mechanism differs per language, and all eight are covered below.

What this is not for ​

Two things are worth separating up front, or you will end up adding a header the client already sets.

The WB token. The client builds the Authorization header itself from your token — see Authentication. Do not assemble it by hand through the mechanisms on this page: in some languages (Go) that produces two Authorization headers on one request.

Headers declared in the spec. If a header is declared on an operation, the generator has already turned it into an ordinary method argument. WB has a couple: X-Nm-Id and X-Photo-Number on postV3MediaFile in the items sub-module. Pass those as arguments, not as custom headers.

What is left is everything else — headers the spec does not know about. Those are what we add here.

Headers for the whole client ​

The common case: the header belongs on every request. Set it once, when you build the client.

python
from wb_api_client.items import Configuration, ApiClient
from wb_api_client.items.api import ItemsApi

client = ApiClient(Configuration(access_token="<your WB JWT>"))
client.set_default_header("X-Gateway-Token", "<gateway credentials>")
client.set_default_header("X-Request-Source", "erp-sync")

api = ItemsApi(client)
ts
import {
  Configuration,
  ItemsApi,
} from "@valeryverkhoturov/wb-api-client/items";

const cfg = new Configuration({
  baseOptions: {
    headers: {
      "X-Gateway-Token": "<gateway credentials>",
      "X-Request-Source": "erp-sync",
    },
  },
});
cfg.setAccessToken("<your WB JWT>");

const api = new ItemsApi(cfg);
go
import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items"

cfg := wbitems.NewConfiguration()
cfg.SetAccessToken("<your WB JWT>")
cfg.AddDefaultHeader("X-Gateway-Token", "<gateway credentials>")
cfg.AddDefaultHeader("X-Request-Source", "erp-sync")

client := wbitems.NewAPIClient(cfg)
java
import io.github.valeryverkhoturov.wbapi.items.ApiClient;
import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi;

ApiClient client = new ApiClient();
client.setAccessToken("<your WB JWT>");
client.addDefaultHeader("X-Gateway-Token", "<gateway credentials>");
client.addDefaultHeader("X-Request-Source", "erp-sync");

ItemsApi api = new ItemsApi(client);
php
use ValeryVerkhoturov\WbApiClient\Items\Configuration;
use ValeryVerkhoturov\WbApiClient\Items\Api\ItemsApi;
use GuzzleHttp\Client;

$config = (new Configuration())
    ->setAccessToken('<your WB JWT>');

// PHP's Configuration has no header registry — the Guzzle client holds them.
$http = new Client([
    'headers' => [
        'X-Gateway-Token' => '<gateway credentials>',
        'X-Request-Source' => 'erp-sync',
    ],
]);

$api = new ItemsApi($http, $config);
bsl
#Использовать "wb-api-client"

Настройки = Новый Конфигурация();
Настройки.УстановитьТокен("<your WB JWT>");
Настройки.ДополнительныеЗаголовки.Вставить("X-Gateway-Token", "<gateway credentials>");
Настройки.ДополнительныеЗаголовки.Вставить("X-Request-Source", "erp-sync");

Клиент = Новый ItemsApi(Настройки);
csharp
using ValeryVerkhoturov.WbApiClient.Items.Api;
using ValeryVerkhoturov.WbApiClient.Items.Client;

var config = new Configuration();
config.SetAccessToken("<your WB JWT>");
config.DefaultHeaders["X-Gateway-Token"] = "<gateway credentials>";
config.DefaultHeaders["X-Request-Source"] = "erp-sync";

var api = new ItemsApi(config);
rust
use wb_api_client::items::apis::configuration::Configuration;

// Rust's Configuration has no header registry — the reqwest client holds them.
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("X-Gateway-Token", "<gateway credentials>".parse().unwrap());
headers.insert("X-Request-Source", "erp-sync".parse().unwrap());

let mut cfg = Configuration::new();
cfg.set_access_token("<your WB JWT>");
cfg.client = reqwest::Client::builder()
    .default_headers(headers)
    .build()
    .unwrap();

Headers live on a Configuration / ApiClient instance — and each sub-module has its own. If you use both items and orders_fbs, set the headers on both; see Multiple sub-modules, one token.

Headers for a single request ​

Not every client can set a header for one call — it depends on what openapi-generator emitted for that language.

LanguagePer-call support
Pythonyes — a _headers= keyword argument on every method
TypeScriptyes — a trailing RawAxiosRequestConfig argument
Gono — methods take no headers
Javano
PHPno
C#no
OneScriptno — Заголовки are assembled inside the method
Rustno — methods take only &Configuration
python
# Careful: default headers override _headers when the names collide.
tags = api.get_v2_tags(_headers={"X-Correlation-Id": "9f1c-…"})
ts
// The opposite here: a per-call header beats baseOptions and Authorization.
const tags = await api.getV2Tags({
  headers: { "X-Correlation-Id": "9f1c-…" },
});

In the other six languages, if a header is only needed on some requests, build a second client instance carrying its own headers and route those calls through it.

Precedence ​

Each generator merges headers in its own order, and it is not always the intuitive one. Verified against the generated code:

LanguageOrder (rightmost wins)Note
Python_headers → default headersDefaults win: default_headers are applied on top of _headers
TypeScriptauth → baseOptions.headers → per-call headersa config header can override Authorization too
Go—uses Header.Add, so it appends rather than replaces: a duplicate name sends two headers
JavadefaultHeaderMap over operation headersreplaces by name
PHPrequest headers → Guzzle defaultsGuzzle client defaults apply only when the header is not already set
C#DefaultHeaders → operation headersreplaces by name
OneScriptAccept/User-Agent/Authorization → ДополнительныеЗаголовки → operation headersСоответствие.Вставить replaces the value
Rustclient default_headers → operation headersa header set by the operation overrides the reqwest client default

The practical takeaway is the same everywhere: do not use the mechanisms on this page to override a header the client already sets. User-Agent has a dedicated setting in every language, and Authorization has the token setter.

Secrets in headers ​

SecretString wraps the WB token only. Gateway or partner-service credentials are plain strings, and the client does not redact them: they appear in full in an HTTP-client log or a configuration dump.

So the same rules apply to them as to the WB token:

  1. Keep the value in a secret manager (Vault, SSM, a GitHub Actions secret), not in source.
  2. Read it into an environment variable at process start and build the client once.
  3. Check that your request logger does not dump headers wholesale — or add your header names to its redaction list.

If you log requests through middleware, add these header names to the same filter that already holds Authorization.

wb-api-client mascot