# wb-api-client > Auto-generated client libraries for the Wildberries Seller API — Python, TypeScript, Go, Java, PHP, OneScript, C# and Rust. One version across all eight ecosystems, bearer tokens redacted by default. Every page on the site is also served as Markdown at its own address plus `.md` — `guides/quickstart.md`, for instance. That includes the {apiPages} endpoint reference pages, which are not listed individually below: each module page lists its own operations. --- title: "SDKs for the Wildberries Seller API" description: "Auto-generated SDKs for Python, TypeScript, Go, Java, PHP, OneScript (1С), C# and Rust. One version across every ecosystem. Bearer tokens redacted by default." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/ --- # wb-api-client — Wildberries API, in your language. Auto-generated SDKs for Python, TypeScript, Go, Java, PHP, OneScript (1С), C# and Rust. One version across every ecosystem. Bearer tokens redacted by default. - **13 API categories, one package** — Every category — items, orders (FBS/DBW/FBW/DBS), promotion, analytics, finances, and the rest — ships as an isolated sub-module inside a single per-language package. No cross-category name clashes. - **Secret redaction, built in** — Your bearer JWT is wrapped in a per-language SecretString so it stays out of logs, prints, and debuggers unless you explicitly ask for the raw value. - **Daily upstream sync** — A CI job re-downloads the WB specs every morning. If anything changed, a new release is cut and pushed to PyPI, npm, Go, Maven Central, Packagist, hub.oscript.io, NuGet and crates.io automatically — with a matching git tag. - **One version, eight ecosystems** — 1.YYYYMMDD.N — a valid stable semver in every language. pip / npm / go get / mvn / composer / opm / dotnet add / cargo add all pick the latest without prerelease dance. - **Bearer auth, everywhere** — The upstream YAMLs conflate an API-key scheme with a bearer JWT. A post-processing pass strips that duplication so every generated client exposes exactly one auth path. - **Deterministic regeneration** — Pinned openapi-generator + formatter versions + hidden generation timestamps. PRs that break determinism are caught by a per-PR regen check. wb-api-client is a set of client libraries for the Wildberries Seller API: 13 categories (items, orders across FBS / DBW / FBW / DBS, promotion, analytics, finances and the rest) in eight languages, sharing one version and rebuilt from the official WB specs every day. ## Install **Python** ```bash pip install valeryverkhoturov-wb-api-client ``` **TypeScript** ```bash npm install @valeryverkhoturov/wb-api-client ``` **Go** ```bash go get github.com/ValeryVerkhoturov/wb-api-client-go@latest ``` **Java (Maven)** ```xml io.github.valeryverkhoturov wb-api-client LATEST ``` **PHP** ```bash composer require valeryverkhoturov/wb-api-client ``` **OneScript** ```bash opm install wb-api-client ``` **C#** ```bash dotnet add package ValeryVerkhoturov.WbApiClient ``` **Rust** ```bash cargo add wb-api-client ``` ## Call something **Python** ```python from wb_api_client.items import Configuration, ApiClient from wb_api_client.items.api import ItemsApi cfg = Configuration(access_token="") api = ItemsApi(ApiClient(cfg)) ``` **TypeScript** ```ts import { Configuration, ItemsApi } from "@valeryverkhoturov/wb-api-client/items"; const cfg = new Configuration({}); cfg.setAccessToken(""); const api = new ItemsApi(cfg); ``` **Go** ```go import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items" cfg := wbitems.NewConfiguration() cfg.SetAccessToken("") client := wbitems.NewAPIClient(cfg) ``` **Java** ```java import io.github.valeryverkhoturov.wbapi.items.ApiClient; import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi; ApiClient client = new ApiClient(); client.setAccessToken(""); ItemsApi api = new ItemsApi(client); ``` **PHP** ```php use ValeryVerkhoturov\WbApiClient\Items\Configuration; use ValeryVerkhoturov\WbApiClient\Items\Api\ItemsApi; use GuzzleHttp\Client; $config = (new Configuration()) ->setAccessToken(''); $api = new ItemsApi(new Client(), $config); ``` **OneScript** ```bsl #Использовать "wb-api-client" Настройки = Новый Конфигурация(); Настройки.УстановитьТокен(""); Клиент = Новый ItemsApi(Настройки); ``` **C#** ```csharp using ValeryVerkhoturov.WbApiClient.Items.Api; using ValeryVerkhoturov.WbApiClient.Items.Client; var config = new Configuration(); config.SetAccessToken(""); var api = new ItemsApi(config); ``` **Rust** ```rust use wb_api_client::items::apis::{configuration::Configuration, items_api}; let mut cfg = Configuration::new(); cfg.set_access_token(""); ``` Full tour of every sub-module, per-language: [Python](https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/python) · [TypeScript](https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/typescript) · [Go](https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/go) · [Java](https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/java) · [PHP](https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/php) · [OneScript](https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/onescript) · [C#](https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/csharp) · [Rust](https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/rust). For LLM agents: [llms.txt](https://valeryverkhoturov.github.io/wb-api-client-docs/en/llms.txt) — an index of every page on the site, [llms-full.txt](https://valeryverkhoturov.github.io/wb-api-client-docs/en/llms-full.txt) — the whole documentation in one file.

wb-api-client mascot

--- title: "Quickstart" description: "Five minutes: get a token, install the client for your language, make your first API call." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/quickstart --- # Quickstart Five minutes: get a token, install the client for your language, make your first API call. ## 1. Get a WB API token The client libraries are transports — you still need a Wildberries seller account and a personal API token. 1. Sign in at [seller.wildberries.ru](https://seller.wildberries.ru) and open the [Интеграции по API](https://seller.wildberries.ru/api-integrations) (API integrations) section. 2. Click **+ Создать токен** (+ Create token) and pick the **Для интеграции вручную** (Manual integration) tab. 3. Choose the token type — **персональный** (personal) or **базовый** (base) for normal work, **тестовый** (test) for the sandbox. 4. Fill in the name, pick the method categories and the access level (**Чтение и запись** — read & write, or **Только чтение** — read only). For a first test, **Контент** (Content) is enough — that's what powers the `items` sub-module. 5. For a personal token, tick the **Я понимаю, что не следует передавать токен третьим лицам** ("I understand the token must not be shared with third parties") checkbox and click **Создать** (Create). 6. Click **Скопировать и закрыть** (Copy and close) — the token lands on your clipboard. You won't be able to view it again. Only pick the categories you plan to work with. If you need to call multiple API categories (`items`, `orders_fbs`, `analytics`, …) create a token with the union of categories — or one token per category to keep blast radius small. ## 2. Install Every SDK ships one package per language. All 13 API categories are inside as sub-modules. **Python** ```bash pip install valeryverkhoturov-wb-api-client ``` **TypeScript** ```bash npm install @valeryverkhoturov/wb-api-client ``` **Go** ```bash go get github.com/ValeryVerkhoturov/wb-api-client-go@latest ``` **Java (Maven)** ```xml io.github.valeryverkhoturov wb-api-client LATEST ``` **PHP** ```bash composer require valeryverkhoturov/wb-api-client ``` **OneScript** ```bash opm install wb-api-client ``` **C#** ```bash dotnet add package ValeryVerkhoturov.WbApiClient ``` **Rust** ```bash cargo add wb-api-client ``` `LATEST` for Java: check the [releases page](https://github.com/ValeryVerkhoturov/wb-api-client/releases) for the current `1.YYYYMMDD.N` version and paste it in. ## 3. First call — list your seller info The `general` sub-module has a `getV1SellerInfo` endpoint that's cheap to call and confirms both your token and your network path. **Python** ```python from wb_api_client.general import Configuration, ApiClient from wb_api_client.general.api import GeneralApi cfg = Configuration(access_token="") api = GeneralApi(ApiClient(cfg)) info = api.get_v1_seller_info() print(info) ``` **TypeScript** ```ts import { Configuration, GeneralApi, } from "@valeryverkhoturov/wb-api-client/general"; const cfg = new Configuration({}); cfg.setAccessToken(""); const api = new GeneralApi(cfg); const info = await api.getV1SellerInfo(); console.log(info.data); ``` **Go** ```go package main import ( "context" "fmt" wbgeneral "github.com/ValeryVerkhoturov/wb-api-client-go/general" ) func main() { cfg := wbgeneral.NewConfiguration() cfg.SetAccessToken("") client := wbgeneral.NewAPIClient(cfg) info, _, err := client.GeneralAPI.GetV1SellerInfo(context.Background()).Execute() if err != nil { panic(err) } fmt.Printf("%+v\n", info) } ``` **Java** ```java import io.github.valeryverkhoturov.wbapi.general.ApiClient; import io.github.valeryverkhoturov.wbapi.general.api.GeneralApi; ApiClient client = new ApiClient(); client.setAccessToken(""); GeneralApi api = new GeneralApi(client); System.out.println(api.getV1SellerInfo()); ``` **PHP** ```php use ValeryVerkhoturov\WbApiClient\General\Configuration; use ValeryVerkhoturov\WbApiClient\General\Api\GeneralApi; use GuzzleHttp\Client; $config = (new Configuration()) ->setAccessToken(''); $api = new GeneralApi(new Client(), $config); print_r($api->getV1SellerInfo()); ``` **OneScript** ```bsl #Использовать "wb-api-client" Настройки = Новый Конфигурация(); Настройки.УстановитьТокен(""); Клиент = Новый GeneralApi(Настройки); Сообщить(Клиент.GetV1SellerInfo().Тело); ``` **C#** ```csharp using ValeryVerkhoturov.WbApiClient.General.Api; using ValeryVerkhoturov.WbApiClient.General.Client; var config = new Configuration(); config.SetAccessToken(""); var api = new GeneralApi(config); Console.WriteLine(api.GetV1SellerInfo()); ``` **Rust** ```rust use wb_api_client::general::apis::{configuration::Configuration, general_api}; #[tokio::main] async fn main() { let mut cfg = Configuration::new(); cfg.set_access_token(""); let info = general_api::get_v1_seller_info(&cfg).await; println!("{:#?}", info); } ``` If that returns your seller data — you're wired up. ## 4. Where to go next - **[Authentication](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/authentication)** — how the secret-string wrapper works, why you should never print your token, and how to expose the raw value when you actually need to. - **[Error handling](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/error-handling)** — WB's status codes, retry strategy for 429/5xx, and how errors surface in each language. - **[Language pages](https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/python)** — every sub-module, install snippet, and the shape of each `Api` class. ## Troubleshooting the first call | Symptom | Likely cause | Fix | | ------------------------------------ | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | `401 Unauthorized` | Token wrong / expired / not for this category | Regenerate in Seller portal; ensure the category matches the module (e.g. `Контент` for `items`) | | `403 Forbidden` | Token valid but category missing | Add the required category to the token, or make a new one | | `429 Too Many Requests` | You've hit WB's rate limit | Wait `X-Ratelimit-Retry` seconds from the response and back off; the SDK does not auto-retry | | Connection hangs | Corporate proxy / TLS interception | Set `HTTPS_PROXY` env var; most clients honor it | | `ImportError` / `Cannot find module` | Installed the wrong package name | Package is `valeryverkhoturov-wb-api-client` / `@valeryverkhoturov/wb-api-client` — the bare `wb-api-client` name is a different project |

wb-api-client mascot

--- title: "Authentication" description: "Every Wildberries Seller API call takes a bearer JWT in the Authorization header. The clients handle the header for you; you just hand them the token." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/authentication --- # Authentication Every Wildberries Seller API call takes a **bearer JWT** in the `Authorization` header. The clients handle the header for you; you just hand them the token. ## Where the token comes from Tokens are minted in the seller portal: 1. [seller.wildberries.ru](https://seller.wildberries.ru) → the [Интеграции по API](https://seller.wildberries.ru/api-integrations) (API integrations) section. 2. **+ Создать токен** (+ Create token) → the **Для интеграции вручную** (Manual integration) tab → choose the token type: **персональный** (personal), **базовый** (base), or **тестовый** (test) for the sandbox. 3. Fill in the name, pick the method categories (Контент, Маркетплейс, Аналитика, …) and the access level — **Чтение и запись** (read & write) or **Только чтение** (read only). 4. **Создать** (Create) → **Скопировать и закрыть** (Copy and close). The token is shown once — save it immediately. Each category you tick corresponds roughly to one or more sub-modules in the clients. If a call returns `403`, the token is valid but lacks the category — regenerate with it ticked. Which token type to pick: - **Personal** — for your own programs and on-premise systems (including local 1C installations). Grants access to sensitive data, so it must not be shared with third parties or used in cloud services. - **Base** — access to a limited set of data. Good for testing the integration on real data and for cases where a personal token doesn't fit. - **Test** — works only with the [WB API sandbox](https://dev.wildberries.ru/openapi/sandbox) and generated test data; real seller data is not accessible. - **Service** — created for a specific cloud service from the [Solutions for Business catalog](https://seller.wildberries.ru/auth-services); categories and access levels are filled in automatically. Every token lives 180 days from creation, after which you need to mint a new one. You can check a token's validity and categories by decoding the JWT — it's a plain [RFC 7519](https://www.rfc-editor.org/rfc/rfc7519) token. Don't paste it into online decoders: anyone can intercept it that way. ## Secret-string wrappers Once you have the token, don't pass it around as a plain string. Every client stores the value in a language-native "secret string" type — in Java, PHP, C# and Rust you hand the setter a plain string and it wraps the value internally — so the token redacts under logs, `print`, debuggers, and error dumps, unless you explicitly ask for the raw value. | Language | Wrapper | Setter | Redacted under | Explicit expose | | ---------- | -------------------------------------------------------------------------------------------- | --------------------------------------- | --------------------------------------------------------- | --------------------- | | Python | [`pydantic.SecretStr`](https://docs.pydantic.dev/latest/api/types/#pydantic.types.SecretStr) | `Configuration(access_token=…)` | `print(cfg.access_token)` → `**********` | `.get_secret_value()` | | TypeScript | inlined `SecretString` class | `Configuration.setAccessToken(…)` | `console.log(new SecretString("…"))` → `` | `.exposeSecret()` | | Go | [`secrecy.SecretString`](https://github.com/negrel/secrecy) | `Configuration.SetAccessToken(…)` | `fmt.Printf("%v", cfg.AccessToken)` → `` | `.ExposeSecret()` | | Java | per-sub-module `SecretString` | `ApiClient.setAccessToken(String)` | `System.out.println(s)` → `` | — | | PHP | per-sub-module `SecretString` | `Configuration::setAccessToken(string)` | `var_dump($config)` → token `''` | `getAccessToken()` | | C# | per-namespace `SecretString` | `Configuration.SetAccessToken(string)` | `Console.WriteLine(s)` → `` | no getter by design | | OneScript | `СекретнаяСтрока` class | `Конфигурация.УстановитьТокен(…)` | `Сообщить(Настройки.Токен())` → `СекретнаяСтрока` | `.Раскрыть()` | | Rust | per-module `SecretString` struct | `Configuration::set_access_token(&str)` | `println!("{s:?}")` / `{}` → `` | `.expose_secret()` | ### Why bother? Real incidents behind this design: - Logger middleware that dumps the request config on error. - `print(config)` sprinkled in during debugging and never removed. - Exceptions serialized to Sentry / a bug tracker with the full HTTP client context attached. - IDE debuggers displaying object internals in a shared screen-share. The wrapper flips all of those from a token leak into a `` marker. The one-line cost is the wrapper type at the boundary. ### Examples of the redaction firing **Python** ```python from wb_api_client.items import Configuration cfg = Configuration(access_token="eyJhbGciOi...") print(cfg.access_token) # SecretStr('**********') print(cfg.access_token.get_secret_value()) # eyJhbGciOi... (opt-in) ``` **TypeScript** ```ts import { Configuration } from "@valeryverkhoturov/wb-api-client/items"; const cfg = new Configuration({}); cfg.setAccessToken("eyJhbGciOi..."); console.log(cfg.accessToken); // SecretString { } console.log(cfg.accessToken.exposeSecret()); // eyJhbGciOi... (opt-in) ``` **Go** ```go import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items" cfg := wbitems.NewConfiguration() cfg.SetAccessToken("eyJhbGciOi...") fmt.Printf("%v\n", cfg.AccessToken) // fmt.Println(cfg.AccessToken.ExposeSecret()) // eyJhbGciOi... (opt-in) ``` **Java** ```java SecretString s = new SecretString("eyJhbGciOi..."); System.out.println(s); // System.out.println(s.exposeSecret()); // eyJhbGciOi... (opt-in) ``` **PHP** ```php use ValeryVerkhoturov\WbApiClient\Items\Configuration; $config = (new Configuration())->setAccessToken('eyJhbGciOi...'); var_dump($config); // accessToken => '' echo $config->getAccessToken(); // eyJhbGciOi... (opt-in) ``` **OneScript** ```bsl С = Новый СекретнаяСтрока("eyJhbGciOi..."); Сообщить(С); // СекретнаяСтрока Сообщить(С.Раскрыть()); // eyJhbGciOi... (opt-in) ``` **C#** ```csharp var s = new SecretString("eyJhbGciOi..."); Console.WriteLine($"{s}"); // Console.WriteLine(s.ExposeSecret()); // eyJhbGciOi... (opt-in) ``` **Rust** ```rust use wb_api_client::items::apis::configuration::SecretString; let s = SecretString::new("eyJhbGciOi..."); println!("{s:?}"); // println!("{}", s.expose_secret()); // eyJhbGciOi... (opt-in) ``` ## Multiple sub-modules, one token If you use several sub-modules in the same process, each has its own `Configuration` / `ApiClient` — because each sub-module is a self-contained SDK generated from a different upstream spec. Reuse the token string; construct a config per module. **Python** ```python from wb_api_client.items import Configuration as ItemsCfg, ApiClient as ItemsClient from wb_api_client.orders_fbs import Configuration as OrdCfg, ApiClient as OrdClient TOKEN = "" items_api = ItemsClient(ItemsCfg(access_token=TOKEN)) orders_api = OrdClient(OrdCfg(access_token=TOKEN)) ``` **TypeScript** ```ts import { Configuration as ItemsCfg } from "@valeryverkhoturov/wb-api-client/items"; import { Configuration as OrdCfg } from "@valeryverkhoturov/wb-api-client/orders_fbs"; const TOKEN = ""; const itemsCfg = new ItemsCfg({}); itemsCfg.setAccessToken(TOKEN); const ordCfg = new OrdCfg({}); ordCfg.setAccessToken(TOKEN); ``` ## Rotating tokens WB tokens don't rotate on the wire and live at most 180 days — you have to swap them. Best practice: 1. Store the token in a secret manager (SSM, Vault, GitHub Actions secret) — never in source. 2. Read it into an env var at process start; construct the config once. 3. On rotation: kill the process (or the connection pool) and start fresh with the new token. The clients cache the token per Configuration instance. ## Common auth failures | Response | Meaning | | ----------------------------------------- | ------------------------------------------------------------------------------------------ | | `401 Unauthorized` — `token expired` | Token past its expiry; mint a new one | | `401 Unauthorized` — `invalid signature` | Wrong Authorization header format, or token from a different environment (sandbox vs prod) | | `403 Forbidden` — `no access to resource` | Token valid, scope missing — regenerate with the right box ticked | | `429 Too Many Requests` | Rate-limited; the token itself is fine | See the [error-handling guide](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/error-handling) for retry patterns.

wb-api-client mascot

--- title: "Error handling" description: "WB returns standard HTTP status codes plus a JSON body. The clients surface these as language-native exceptions — you don't get a raw Response unless you…" lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/error-handling --- # Error handling WB returns standard HTTP status codes plus a JSON body. The clients surface these as language-native exceptions — you don't get a raw `Response` unless you explicitly ask for one. ## Status codes worth handling | Code | Meaning | Right response | | ----------- | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | `200`–`204` | Success | Parse the body (or don't, for 204) | | `400` | Request malformed | Fix the request; not retriable | | `401` | Missing / bad / expired token | Refresh the token; do NOT retry with the same token | | `402` | Service balance is out of funds | Only returned to services from the "Solutions for Business" catalog — top up the balance | | `403` | Token lacks scope for this endpoint | Check: token category matches the method, the method is allowed for the token type, the token wasn't issued by a deleted user. Regenerate if needed | | `404` | Resource doesn't exist | Check the URL; business-logic decision — not always an error. Useful details in the body's `detail` field | | `409` | Conflict (e.g. supply already delivered) | Business-logic; specific WB schemas per endpoint | | `413` | Request body exceeds the limit | Reduce the number of objects in the request | | `422` | Request parameter processing error | Check the request data — fields must not contradict each other | | `429` | Rate-limited | Wait `X-Ratelimit-Retry` seconds from the response, then retry | | `498` | WBAAS anti-bot challenge | Never fires against these clients (correct UA) — if you see it, something is proxying/rewriting | | `5xx` | Upstream problem | Retry with exponential backoff, cap attempts | ## Retry pattern The clients don't retry on your behalf — that's a policy decision. A minimal retriable-error handler: **Python** ```python import time from wb_api_client.items.exceptions import ApiException def with_retry(fn, *, max_attempts=5, base=1.0): for attempt in range(1, max_attempts + 1): try: return fn() except ApiException as e: if e.status not in (429, 500, 502, 503, 504) or attempt == max_attempts: raise retry_after = float( e.headers.get("X-Ratelimit-Retry") or e.headers.get("Retry-After") or 0 ) or base * 2 ** attempt time.sleep(retry_after) ``` **TypeScript** ```ts import { AxiosError } from "axios"; async function withRetry( fn: () => Promise, { maxAttempts = 5, base = 1000 } = {}, ): Promise { for (let attempt = 1; attempt <= maxAttempts; attempt++) { try { return await fn(); } catch (err) { const e = err as AxiosError; const status = e.response?.status ?? 0; if (![429, 500, 502, 503, 504].includes(status) || attempt === maxAttempts) throw err; const retryAfterHeader = e.response?.headers?.["x-ratelimit-retry"] ?? e.response?.headers?.["retry-after"]; const delay = retryAfterHeader ? Number(retryAfterHeader) * 1000 : base * 2 ** attempt; await new Promise((r) => setTimeout(r, delay)); } } throw new Error("unreachable"); } ``` **Go** ```go import ( "context" "errors" "net/http" "time" ) func withRetry[T any](ctx context.Context, fn func() (T, *http.Response, error)) (T, error) { var zero T for attempt := 1; attempt <= 5; attempt++ { v, resp, err := fn() if err == nil { return v, nil } status := 0 if resp != nil { status = resp.StatusCode } if status != 429 && (status < 500 || status > 504) || attempt == 5 { return zero, err } delay := time.Duration(1< wb-api-client mascot

--- title: "Custom headers" description: "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…" lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/custom-headers --- # 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](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/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** ```python from wb_api_client.items import Configuration, ApiClient from wb_api_client.items.api import ItemsApi client = ApiClient(Configuration(access_token="")) client.set_default_header("X-Gateway-Token", "") client.set_default_header("X-Request-Source", "erp-sync") api = ItemsApi(client) ``` **TypeScript** ```ts import { Configuration, ItemsApi, } from "@valeryverkhoturov/wb-api-client/items"; const cfg = new Configuration({ baseOptions: { headers: { "X-Gateway-Token": "", "X-Request-Source": "erp-sync", }, }, }); cfg.setAccessToken(""); const api = new ItemsApi(cfg); ``` **Go** ```go import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items" cfg := wbitems.NewConfiguration() cfg.SetAccessToken("") cfg.AddDefaultHeader("X-Gateway-Token", "") cfg.AddDefaultHeader("X-Request-Source", "erp-sync") client := wbitems.NewAPIClient(cfg) ``` **Java** ```java import io.github.valeryverkhoturov.wbapi.items.ApiClient; import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi; ApiClient client = new ApiClient(); client.setAccessToken(""); client.addDefaultHeader("X-Gateway-Token", ""); client.addDefaultHeader("X-Request-Source", "erp-sync"); ItemsApi api = new ItemsApi(client); ``` **PHP** ```php use ValeryVerkhoturov\WbApiClient\Items\Configuration; use ValeryVerkhoturov\WbApiClient\Items\Api\ItemsApi; use GuzzleHttp\Client; $config = (new Configuration()) ->setAccessToken(''); // PHP's Configuration has no header registry — the Guzzle client holds them. $http = new Client([ 'headers' => [ 'X-Gateway-Token' => '', 'X-Request-Source' => 'erp-sync', ], ]); $api = new ItemsApi($http, $config); ``` **OneScript** ```bsl #Использовать "wb-api-client" Настройки = Новый Конфигурация(); Настройки.УстановитьТокен(""); Настройки.ДополнительныеЗаголовки.Вставить("X-Gateway-Token", ""); Настройки.ДополнительныеЗаголовки.Вставить("X-Request-Source", "erp-sync"); Клиент = Новый ItemsApi(Настройки); ``` **C#** ```csharp using ValeryVerkhoturov.WbApiClient.Items.Api; using ValeryVerkhoturov.WbApiClient.Items.Client; var config = new Configuration(); config.SetAccessToken(""); config.DefaultHeaders["X-Gateway-Token"] = ""; config.DefaultHeaders["X-Request-Source"] = "erp-sync"; var api = new ItemsApi(config); ``` **Rust** ```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", "".parse().unwrap()); headers.insert("X-Request-Source", "erp-sync".parse().unwrap()); let mut cfg = Configuration::new(); cfg.set_access_token(""); 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](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/authentication#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. | Language | Per-call support | | ---------- | ---------------------------------------------------- | | Python | yes — a `_headers=` keyword argument on every method | | TypeScript | yes — a trailing `RawAxiosRequestConfig` argument | | Go | no — methods take no headers | | Java | no | | PHP | no | | C# | no | | OneScript | no — `Заголовки` are assembled inside the method | | Rust | no — methods take only `&Configuration` | **Python** ```python # Careful: default headers override _headers when the names collide. tags = api.get_v2_tags(_headers={"X-Correlation-Id": "9f1c-…"}) ``` **TypeScript** ```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: | Language | Order (rightmost wins) | Note | | ---------- | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------- | | Python | `_headers` → default headers | **Defaults win**: `default_headers` are applied on top of `_headers` | | TypeScript | auth → `baseOptions.headers` → per-call headers | a config header can override `Authorization` too | | Go | — | uses `Header.Add`, so it **appends rather than replaces**: a duplicate name sends two headers | | Java | `defaultHeaderMap` over operation headers | replaces by name | | PHP | request headers → Guzzle defaults | Guzzle client defaults apply only when the header is not already set | | C# | `DefaultHeaders` → operation headers | replaces by name | | OneScript | `Accept`/`User-Agent`/`Authorization` → `ДополнительныеЗаголовки` → operation headers | `Соответствие.Вставить` replaces the value | | Rust | client `default_headers` → operation headers | a 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

--- title: "Python" description: "Python client for the Wildberries Seller API: valeryverkhoturov-wb-api-client on PyPI, 13 API categories, redacted token." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/python --- # Python Package: [`valeryverkhoturov-wb-api-client`](https://pypi.org/project/valeryverkhoturov-wb-api-client/) on PyPI. - **Python:** 3.9+ - **HTTP:** `urllib3` - **Models:** `pydantic` v2 - **Secret wrapper:** `pydantic.SecretStr` ## Install ```bash pip install valeryverkhoturov-wb-api-client ``` For projects on Poetry / uv / PDM, add the dependency the usual way: **pyproject.toml** ```toml [project] dependencies = [ "valeryverkhoturov-wb-api-client", ] ``` **uv** ```bash uv add valeryverkhoturov-wb-api-client ``` **poetry** ```bash poetry add valeryverkhoturov-wb-api-client ``` ## Import shape The distribution installs one top-level package `wb_api_client` with 13 sub-modules — one per WB API category. Each sub-module is a self-contained SDK with its own `Configuration`, `ApiClient`, and exactly one API class named after the module (`GeneralApi`, `ItemsApi`, `OrdersFbsApi`, and so on). ```python from wb_api_client. import Configuration, ApiClient from wb_api_client..api import Api ``` ## Sub-modules | Slug | Category | | ----------------- | ------------------------------------------ | | `general` | Общее — ping, seller info, user management | | `items` | Работа с товарами (Content) | | `orders_fbs` | Заказы FBS | | `orders_dbw` | Заказы DBW | | `dbs` | DBS | | `in_store_pickup` | Самовывоз | | `orders_fbw` | Поставки FBW | | `promotion` | Маркетинг и продвижение | | `communications` | Общение с покупателями | | `rates` | Тарифы | | `analytics` | Аналитика и данные | | `reports` | Отчёты | | `finances` | Документы и бухгалтерия | Each sub-module exposes a single API class named after the module: `general` → `GeneralApi`, `items` → `ItemsApi`, `orders_fbs` → `OrdersFbsApi`, and so on. The full list is in the per-release README: [clients/python/README.md](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/python/README.md). ## Auth ```python from wb_api_client.items import Configuration, ApiClient from wb_api_client.items.api import ItemsApi cfg = Configuration(access_token="") # wrapped in SecretStr client = ApiClient(cfg) api = ItemsApi(client) ``` Passing a bare string is fine — it's auto-wrapped. If you want the raw token back for logging / diagnostics: ```python cfg.access_token # SecretStr(**********) cfg.access_token.get_secret_value() # "" ``` ## Async / sync The generated client is **synchronous** (urllib3). If you need async, run calls in a thread pool: ```python import asyncio result = await asyncio.to_thread(api.get_v1_seller_info) ``` A native-async variant is not on the roadmap — openapi-generator's async-python template lags the sync one. ## Type hints Every model is a pydantic v2 model. Editors and type-checkers see full types: ```python from wb_api_client.items.models import Product # dataclass-like pydantic model p: Product = Product.model_validate(some_dict) ``` ## Testing against the client The `Configuration` object accepts a `host` override so you can point at a mock server: ```python cfg = Configuration( access_token="test-token", host="http://localhost:8080", ) ``` ## See also - [Authentication guide](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/authentication) - [Error handling](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/error-handling) - [Full per-module reference on GitHub](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/python/README.md)

wb-api-client mascot

--- title: "TypeScript" description: "TypeScript client for the Wildberries Seller API: @valeryverkhoturov/wb-api-client on npm, first-class .d.ts types, axios, 13 API categories." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/typescript --- # TypeScript Package: [`@valeryverkhoturov/wb-api-client`](https://www.npmjs.com/package/@valeryverkhoturov/wb-api-client) on npm. - **Node:** 18+ - **HTTP:** `axios` - **Types:** first-class TypeScript, ships `.d.ts` - **Secret wrapper:** inlined `SecretString` class ## Install **npm** ```bash npm install @valeryverkhoturov/wb-api-client ``` **pnpm** ```bash pnpm add @valeryverkhoturov/wb-api-client ``` **yarn** ```bash yarn add @valeryverkhoturov/wb-api-client ``` **bun** ```bash bun add @valeryverkhoturov/wb-api-client ``` ## Import shape Every sub-module is a subpath export. Import from `@valeryverkhoturov/wb-api-client/`: ```ts import { Configuration, ItemsApi, } from "@valeryverkhoturov/wb-api-client/items"; ``` You never import from the root `@valeryverkhoturov/wb-api-client` — there's no barrel to avoid pulling in 13 SDKs when you only need one. ## Sub-modules Sub-path per category: `general`, `items`, `orders_fbs`, `orders_dbw`, `dbs`, `in_store_pickup`, `orders_fbw`, `promotion`, `communications`, `rates`, `analytics`, `reports`, `finances`. Full list of `Api` classes per module lives in the per-release README: [clients/typescript/README.md](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/typescript/README.md). ## Auth ```ts import { Configuration, ItemsApi } from "@valeryverkhoturov/wb-api-client/items"; const cfg = new Configuration({}); cfg.setAccessToken(""); const api = new ItemsApi(cfg); const res = await api.someEndpoint(); console.log(res.data); ``` The `setAccessToken` method wraps the value in a `SecretString` internally. Direct assignment (`cfg.accessToken = "..."`) is not supported — always go through the setter so redaction is guaranteed. To read the raw value back (e.g. for a diagnostic log): ```ts cfg.accessToken.exposeSecret(); ``` ## User-Agent The client sends `ValeryVerkhoturov/wb-api-client/typescript` on every request — WB uses this as a coarse-grained routing signal. To add your own app identifier without replacing it: ```ts const cfg = new Configuration({}); cfg.setAccessToken(TOKEN); cfg.baseOptions = { ...cfg.baseOptions, headers: { ...cfg.baseOptions.headers, "User-Agent": "MyApp/1.2.3 (+https://myapp.example) ValeryVerkhoturov/wb-api-client/typescript", }, }; ``` ## ESM / CJS The package publishes dual entry points — `import` from ESM code, `require` from CommonJS. TypeScript `"module": "NodeNext"` or `"module": "bundler"` in your `tsconfig.json` picks the right one automatically. ## Bundler support The subpath exports are declared in `package.json` `exports`. Modern bundlers (Vite, Rollup, esbuild, Webpack 5+, Turbopack) respect them out of the box. Webpack 4 does not — upgrade or add a manual alias. ## Testing Point at a mock server via `basePath`: ```ts const cfg = new Configuration({ basePath: "http://localhost:8080" }); ``` ## See also - [Authentication guide](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/authentication) - [Error handling](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/error-handling) - [Full per-module reference on GitHub](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/typescript/README.md)

wb-api-client mascot

--- title: "Go" description: "Go client for the Wildberries Seller API: module github.com/ValeryVerkhoturov/wb-api-client-go, 13 API categories, redacted bearer token." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/go --- # Go Module: `github.com/ValeryVerkhoturov/wb-api-client-go` — resolved from git tags on its [dedicated repo](https://github.com/ValeryVerkhoturov/wb-api-client-go). - **Go:** 1.22+ - **HTTP:** `net/http` (standard library) - **Secret wrapper:** [`github.com/negrel/secrecy`](https://github.com/negrel/secrecy) ## Install ```bash go get github.com/ValeryVerkhoturov/wb-api-client-go@latest ``` Pin a specific version by tag: ```bash go get github.com/ValeryVerkhoturov/wb-api-client-go@v1.20260926.0 ``` `proxy.golang.org` fetches directly from the pushed tag — no separate registry. ## Why the module lives in its own repo Go's convention is one module per repo, so the client lives in [wb-api-client-go](https://github.com/ValeryVerkhoturov/wb-api-client-go) with `go.mod` at its root. Release tags (`vX.Y.Z`) are pushed to that repo by the same pipeline that tags the main one — Go picks up whichever tag matches the release. Because `MAJOR = 1` is fixed (see [versioning](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/versioning)), there's no `/v2` suffix now or later. ## Import shape One Go package per WB API category, each with its own `Configuration`, `APIClient`, and `*API` types. ```go import wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items" ``` Alias each sub-module (`wbitems`, `wbfbs`, `wbanalytics`, …) so call sites are readable — they all export the same identifier names. ## Sub-modules `general`, `items`, `orders_fbs`, `orders_dbw`, `dbs`, `in_store_pickup`, `orders_fbw`, `promotion`, `communications`, `rates`, `analytics`, `reports`, `finances`. Per-module `*API` structs and endpoint methods are enumerated in the per-release README: [wb-api-client-go/README.md](https://github.com/ValeryVerkhoturov/wb-api-client-go#readme). ## Auth ```go package main import ( "context" "fmt" wbitems "github.com/ValeryVerkhoturov/wb-api-client-go/items" ) func main() { cfg := wbitems.NewConfiguration() cfg.SetAccessToken("") // wrapped in *secrecy.SecretString client := wbitems.NewAPIClient(cfg) v, _, err := client.DefaultAPI.SomeEndpoint(context.Background()).Execute() if err != nil { panic(err) } fmt.Printf("%+v\n", v) } ``` `SetAccessToken` is the ONLY path — the openapi-generator-default `ContextAccessToken` context-value channel is stripped so there's no way to accidentally bypass the wrapper. To read the raw value back: ```go cfg.AccessToken.ExposeSecret() ``` ## Contexts Every call takes a `context.Context`. Wire your app's deadline/cancellation through — the client honors it: ```go ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() v, _, err := client.DefaultAPI.SomeEndpoint(ctx).Execute() ``` ## Custom HTTP client Wire an `*http.Client` for proxying, custom timeouts, or a transport wrapper: ```go cfg := wbitems.NewConfiguration() cfg.HTTPClient = &http.Client{ Timeout: 60 * time.Second, Transport: &http.Transport{ Proxy: http.ProxyFromEnvironment, }, } ``` ## Testing Override the host at Configuration construction: ```go cfg := wbitems.NewConfiguration() cfg.Host = "localhost:8080" cfg.Scheme = "http" ``` ## See also - [Authentication guide](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/authentication) - [Error handling](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/error-handling) - [Full per-module reference on GitHub](https://github.com/ValeryVerkhoturov/wb-api-client-go#readme)

wb-api-client mascot

--- title: "Java" description: "Java client for the Wildberries Seller API: io.github.valeryverkhoturov:wb-api-client on Maven Central, 13 API categories, redacted token." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/java --- # Java Coordinates: `io.github.valeryverkhoturov:wb-api-client` on [Maven Central](https://central.sonatype.com/artifact/io.github.valeryverkhoturov/wb-api-client). - **Java:** 8+ (compiled with `-source 1.8 -target 1.8`) - **HTTP:** OkHttp 4 - **JSON:** Gson + gson-fire - **Secret wrapper:** per-sub-module `SecretString` ## Install **Maven** ```xml io.github.valeryverkhoturov wb-api-client LATEST ``` **Gradle (Kotlin)** ```kts dependencies { implementation("io.github.valeryverkhoturov:wb-api-client:LATEST") } ``` **Gradle (Groovy)** ```groovy dependencies { implementation 'io.github.valeryverkhoturov:wb-api-client:LATEST' } ``` **sbt** ```scala libraryDependencies += "io.github.valeryverkhoturov" % "wb-api-client" % "LATEST" ``` Replace `LATEST` with the current `1.YYYYMMDD.N` version from [releases](https://github.com/ValeryVerkhoturov/wb-api-client/releases). ## Package layout Base package: `io.github.valeryverkhoturov.wbapi`. Each WB category lives in a sub-package with its own `ApiClient`, models, and `api` classes. ```java import io.github.valeryverkhoturov.wbapi.items.ApiClient; import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi; ``` ## Sub-packages `general`, `items`, `orders_fbs`, `orders_dbw`, `dbs`, `in_store_pickup`, `orders_fbw`, `promotion`, `communications`, `rates`, `analytics`, `reports`, `finances`. Full per-module API class listing: [clients/java/README.md](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/java/README.md). ## Auth ```java import io.github.valeryverkhoturov.wbapi.items.ApiClient; import io.github.valeryverkhoturov.wbapi.items.api.ItemsApi; ApiClient client = new ApiClient(); client.setAccessToken(""); ItemsApi api = new ItemsApi(client); ``` `setAccessToken` takes a plain `String` and wraps it in the sub-module's `SecretString` internally — the token redacts under logging and debug dumps from that point on. ## One `SecretString` per sub-package — why? The wrapper is generated into each sub-package rather than shared across the whole library because openapi-generator emits fully isolated SDKs per spec — there is no shared "core" module. Consolidating would require an additional generator pass with cross-module coupling. The cost is a class per sub-package; the benefit is the client remains a pure openapi-generator output with only surgical patches. Practically you never touch them: `setAccessToken` wraps your plain string in the sub-module's own `SecretString` — `io.github.valeryverkhoturov.wbapi.items.SecretString` vs `.orders_fbs.SecretString` — automatically. ## Custom OkHttp client Wire your own instance for proxying, interceptors, or custom timeouts: ```java import okhttp3.OkHttpClient; import java.time.Duration; OkHttpClient http = new OkHttpClient.Builder() .connectTimeout(Duration.ofSeconds(5)) .readTimeout(Duration.ofSeconds(120)) .build(); ApiClient client = new ApiClient(http); client.setAccessToken(TOKEN); ``` ## Java 8 vs newer The generator emits Java-8-compatible code with `threetenbp` for date/time. If you're on Java 11+, this still works — no need to add a "modern" dependency. If you'd rather use `java.time` directly, the models expose the underlying string values via `set*ToString` / `get*ToString` methods where applicable. ## Testing Override the base path: ```java ApiClient client = new ApiClient(); client.setBasePath("http://localhost:8080"); ``` ## See also - [Authentication guide](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/authentication) - [Error handling](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/error-handling) - [Full per-module reference on GitHub](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/clients/java/README.md)

wb-api-client mascot

--- title: "PHP" description: "PHP client for the Wildberries Seller API: valeryverkhoturov/wb-api-client on Packagist, Guzzle, 13 API categories, redacted token." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/php --- # PHP Package: [`valeryverkhoturov/wb-api-client`](https://packagist.org/packages/valeryverkhoturov/wb-api-client) on Packagist. - **PHP:** 8.1+ - **HTTP:** [Guzzle 7](https://docs.guzzlephp.org/en/stable/) - **Autoload:** PSR-4, root namespace `ValeryVerkhoturov\WbApiClient\` - **Secret wrapper:** per-sub-namespace `SecretString` ## Install ```bash composer require valeryverkhoturov/wb-api-client ``` ## Where the source lives PHP is the odd language out here: Packagist requires `composer.json` at the **root** of the crawled repo, so the PHP client lives in its own repository — [`ValeryVerkhoturov/wb-api-client-php`](https://github.com/ValeryVerkhoturov/wb-api-client-php) — instead of under `clients/php` in the main mono-repo. That sibling repo is mounted as a `clients/php` git submodule in the main repo, so daily-check regenerates all eight languages in one place, then commits + tags both repos in lockstep. From a Composer consumer's perspective this is transparent — you just `composer require` and go. ## Namespace shape Root namespace: `ValeryVerkhoturov\WbApiClient\`. Every WB category is a PascalCase sub-namespace: ```php use ValeryVerkhoturov\WbApiClient\Items\Configuration; use ValeryVerkhoturov\WbApiClient\Items\Api\ItemsApi; ``` ## Sub-namespaces `General`, `Items`, `OrdersFbs`, `OrdersDbw`, `Dbs`, `InStorePickup`, `OrdersFbw`, `Promotion`, `Communications`, `Rates`, `Analytics`, `Reports`, `Finances`. Full per-sub-namespace API listing: [ValeryVerkhoturov/wb-api-client-php README](https://github.com/ValeryVerkhoturov/wb-api-client-php#readme). ## Auth ```php setAccessToken(''); $api = new ItemsApi(new Client(), $config); $result = $api->someEndpoint(); ``` `setAccessToken` takes a plain string and wraps it in a `SecretString` internally — the token redacts under `var_dump($config)` and similar dumps. To read the raw value back: ```php $config->getAccessToken(); // string|null ``` ## User-Agent Every request sends `ValeryVerkhoturov/wb-api-client/php` (not the openapi-generator default). To prepend your own app identifier: ```php $config = (new Configuration()) ->setAccessToken(TOKEN) ->setUserAgent('MyApp/1.2.3 ValeryVerkhoturov/wb-api-client/php'); ``` ## Custom Guzzle client Wire your own for proxying, middleware, custom timeouts: ```php use GuzzleHttp\Client; use GuzzleHttp\HandlerStack; $stack = HandlerStack::create(); // $stack->push(...); // your middleware here $http = new Client([ 'timeout' => 60, 'handler' => $stack, 'proxy' => getenv('HTTPS_PROXY') ?: null, ]); $api = new ItemsApi($http, $config); ``` ## Testing Override the host on Configuration: ```php $config = (new Configuration()) ->setHost('http://localhost:8080') ->setAccessToken('test-token'); ``` ## See also - [Authentication guide](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/authentication) - [Error handling](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/error-handling) - [Sibling repo readme](https://github.com/ValeryVerkhoturov/wb-api-client-php#readme)

wb-api-client mascot

--- title: "OneScript (1С)" description: "OneScript (1С) client for the Wildberries Seller API: wb-api-client on hub.oscript.io, 13 API categories, redacted bearer token." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/onescript --- # OneScript (1С) Package: [`wb-api-client`](https://hub.oscript.io/pools/default/packages/wb-api-client) on hub.oscript.io. - **OneScript:** 2.0.0+ - **HTTP:** [1connector](https://hub.oscript.io/pools/default/packages/1connector) 2.3.3 - **Dependencies:** [1connector](https://hub.oscript.io/pools/default/packages/1connector) 2.3.3 — transport, [jason](https://hub.oscript.io/pools/default/packages/jason) 0.6.0 — model serialization - **Secret wrapper:** `СекретнаяСтрока` class ## Install ```bash opm install wb-api-client ``` Or point at a package directory directly — for example by cloning the sibling repo at a tag: ```bash git clone --branch v1.20260926.0 https://github.com/ValeryVerkhoturov/wb-api-client-1c.git ``` ```bsl #Использовать "path/to/wb-api-client-1c" ``` ## Where the source lives Like PHP, OneScript lives in its own repository — [`ValeryVerkhoturov/wb-api-client-1c`](https://github.com/ValeryVerkhoturov/wb-api-client-1c) — mounted into the mono-repo as a `clients/onescript` git submodule. That lets 1C users clone just the client instead of dragging along the whole pipeline, and install it straight from a tag. Daily-check regenerates all eight languages in one place, then commits and tags both sibling repos under the same `v` tag. ## How categories are separated OneScript has no namespaces: every class name is global. Categories are therefore separated by **name**, not by import path: - **API classes** are named after the category — one class per category: `GeneralApi`, `ItemsApi`, `OrdersFbsApi`, `FinancesApi`. The names are unique across all 13 specs, so they need no prefix. - **Model classes** carry the category as a prefix: `ItemsResponse4XX`, `OrdersFbsResponse4XX`. Without it they would collide — `Response4XX` alone appears in 12 of the 13 specs. Files are grouped under `src/Классы//` and `src/Модели//`, but the directories are for humans only: `lib.config` is what resolves a class. A single `#Использовать` brings in every category at once. ## Authentication ```bsl #Использовать "wb-api-client" Настройки = Новый Конфигурация(); Настройки.УстановитьТокен(""); Клиент = Новый GeneralApi(Настройки); Ответ = Клиент.GetPing(); Сообщить(Ответ.КодСостояния); Сообщить(Ответ.Данные["Status"]); ``` `УстановитьТокен` accepts either a plain string or a ready `СекретнаяСтрока`. The token does not leak when printed: the object exposes no property holding the value, and `Строка(Объект)` in OneScript returns only the class name. ```bsl Сообщить(Настройки.Токен()); // СекретнаяСтрока Сообщить(Настройки.Токен().Раскрыть()); // eyJhbGciOi... (on request) ``` ## Method shape Required parameters are positional; optional ones arrive as a single collection in the last argument: ```bsl Клиент = Новый ItemsApi(Настройки); // Request body only Ответ = Клиент.PostV2CardsDeleteTrash(Тело); // Body + optional query parameters Ответ = Клиент.PostV2CardsErrorList(Тело, Новый Структура("locale", "ru")); ``` Keys in `ДопПараметры` match the names from the spec. When a name is not a legal `Структура` identifier — anything with a dash, such as the `X-Trace` header — pass a `Соответствие` instead: ```bsl ДопПараметры = Новый Соответствие; ДопПараметры.Вставить("X-Trace", "trace-1"); ``` ## The response Every method returns an `ОтветAPI`: | Property / method | Type | Description | | ----------------- | ------------ | ------------------------------ | | `КодСостояния` | Число | HTTP status code | | `Данные` | Произвольный | body parsed from JSON | | `Тело` | Строка | raw response body | | `Заголовки` | Соответствие | response headers | | `Успешно()` | Булево | 2xx | | `ЭтоОшибка()` | Булево | 4xx or 5xx | | `Заголовок(Имя)` | Строка | case-insensitive header lookup | When the body is not JSON (`text/plain`, `application/zip`), `Данные` is `Неопределено` and the content stays in `Тело`. ## Error handling By default a 4xx or 5xx response raises an exception whose text reads `HTTP 404 — GET /api/v3/orders: `. `ВызватьИсключение` in OneScript does not preserve the object for the handler — `ОписаниеОшибки()` would return only a class name — so there is no dedicated exception class here: the detail is all in the text. To inspect an error field by field, turn the raise off: ```bsl Настройки.ВыбрасыватьИсключениеПриОшибке = Ложь; Ответ = Клиент.GetPing(); Если Ответ.ЭтоОшибка() Тогда Сообщить(Ответ.КодСостояния); Сообщить(Ответ.Данные["detail"]); КонецЕсли; ``` ## Service address Each WB category sits behind its own host, and that address is baked into the operation. `БазовыйURL` is empty by default, which means "use the address from the spec". Fill it in to send **every** request elsewhere — a sandbox or a test stand: ```bsl Настройки.БазовыйURL = "https://marketplace-api-sandbox.wildberries.ru"; ``` ## User-Agent and headers Every request sends `ValeryVerkhoturov/wb-api-client/onescript`. To replace or extend it: ```bsl Настройки.ПользовательскийАгент = "MyApp/1.2.3 ValeryVerkhoturov/wb-api-client/onescript"; Настройки.ДополнительныеЗаголовки.Вставить("X-Request-Id", "…"); ``` > **ASCII-only headers** > > Header values must be ASCII — a limitation of the .NET HTTP stack OneScript runs on. Cyrillic in query parameters, path segments and request bodies works fine: those are encoded separately. ## Timeout and proxy ```bsl Настройки.Таймаут = 120; // seconds Настройки.Прокси = Новый ИнтернетПрокси(Истина); ``` ## Models Models are classes with properties plus a pair of conversion methods. Unset properties are omitted from the request body, so a missing field never goes out as `null`. ```bsl Товар = Новый ItemsItem(); Товар.nmID = 123; Товар.price = 1000; Данные = Товар.Данные(); // Соответствие keyed by the spec's names // And back, from a response Восстановленный = Новый ItemsItem(Ответ.Данные); ``` A request body can be passed either as a model or as a plain `Соответствие` / `Структура` — the transport handles both. ## What generates it openapi-generator ships no OneScript target, so one was written separately: [`ValeryVerkhoturov/onescript-openapi-generator`](https://github.com/ValeryVerkhoturov/onescript-openapi-generator), a plugin extending `DefaultCodegen` and loaded next to `openapi-generator-cli.jar` through `META-INF/services`. The main repo pins it by commit, exactly as it pins the openapi-generator image tag for the other languages. ## See also - [Authentication guide](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/authentication) - [Error handling](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/error-handling) - [Sibling repo readme](https://github.com/ValeryVerkhoturov/wb-api-client-1c#readme)

wb-api-client mascot

--- title: "C#" description: "C# client for the Wildberries Seller API: ValeryVerkhoturov.WbApiClient on NuGet, 13 API categories, redacted token." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/csharp --- # C# Package: [`ValeryVerkhoturov.WbApiClient`](https://www.nuget.org/packages/ValeryVerkhoturov.WbApiClient) on NuGet. - **Target framework:** .NET 8.0 - **HTTP:** [RestSharp](https://restsharp.dev/) - **Namespace:** `ValeryVerkhoturov.WbApiClient.` - **Secret wrapper:** per-namespace `SecretString` ## Install ```bash dotnet add package ValeryVerkhoturov.WbApiClient ``` Or in your `.csproj`: ```xml ``` ## Namespace shape The root is `ValeryVerkhoturov.WbApiClient`, and each WB category is a PascalCase sub-namespace. Inside one, the generator splits the code three ways: ```csharp using ValeryVerkhoturov.WbApiClient.Items.Api; // Api classes using ValeryVerkhoturov.WbApiClient.Items.Client; // Configuration, SecretString, ApiException using ValeryVerkhoturov.WbApiClient.Items.Model; // request and response models ``` Categories are isolated by namespace, so identically named models from different sections coexist: `Items.Model.Error` and `Finances.Model.Error` are separate types. ## Sub-namespaces `General`, `Items`, `OrdersFbs`, `OrdersDbw`, `Dbs`, `InStorePickup`, `OrdersFbw`, `Promotion`, `Communications`, `Rates`, `Analytics`, `Reports`, `Finances`. ## Authentication ```csharp using ValeryVerkhoturov.WbApiClient.General.Api; using ValeryVerkhoturov.WbApiClient.General.Client; var config = new Configuration(); config.SetAccessToken(""); var api = new GeneralApi(config); var ping = api.GetPing(); Console.WriteLine(ping.Status); ``` `SetAccessToken` stores the string in a `SecretString` internally. `Configuration` deliberately has no getter for the token: the generated Api classes read it through `IReadableConfiguration` when they build the `Authorization` header. The token does not leak when printed: `SecretString.ToString()` always returns ``, and string interpolation, `Console.WriteLine` and most logging frameworks all go through it. ```csharp var s = new SecretString("eyJhbGciOi..."); Console.WriteLine($"{s}"); // Console.WriteLine(s.ExposeSecret()); // eyJhbGciOi... (on request) ``` ## Async calls Every method has an `Async` counterpart taking a `CancellationToken`: ```csharp var ping = await api.GetPingAsync(cancellationToken: token); ``` ## Client settings ```csharp var config = new Configuration { Timeout = TimeSpan.FromSeconds(60), // 100 seconds by default UserAgent = "MyApp/1.2.3 ValeryVerkhoturov/wb-api-client/csharp", }; config.SetAccessToken(token); config.DefaultHeaders.Add("X-Request-Id", requestId); ``` Every request sends `ValeryVerkhoturov/wb-api-client/csharp` by default. ### Service address Each WB category sits behind its own host, already baked into the client from the spec. To send requests elsewhere — a sandbox, say — set `BasePath`: ```csharp config.BasePath = "https://marketplace-api-sandbox.wildberries.ru"; ``` ## Error handling A 4xx or 5xx response raises `ApiException`: ```csharp using ValeryVerkhoturov.WbApiClient.OrdersFbs.Api; using ValeryVerkhoturov.WbApiClient.OrdersFbs.Client; var fbsConfig = new Configuration(); fbsConfig.SetAccessToken(""); var fbs = new OrdersFbsApi(fbsConfig); try { var orders = fbs.GetV3Orders(limit: 10, next: 0); } catch (ApiException e) { Console.WriteLine(e.ErrorCode); // HTTP status code Console.WriteLine(e.Message); Console.WriteLine(e.ErrorContent); // response body } ``` > **Configuration is per-namespace** > > The generator emits its own `Configuration` — and `SecretString`, and `ApiException` — inside every category, so `OrdersFbs.Client.Configuration` is not `General.Client.Configuration`. A single instance cannot be shared across categories: each Api class takes the `Configuration` from its own namespace. The token itself is the same one. ## Dependency injection Api clients are ordinary classes with a `Configuration` constructor, so they register as-is: ```csharp services.AddSingleton(_ => { var config = new Configuration(); config.SetAccessToken(builder.Configuration["Wb:Token"]); return config; }); services.AddTransient(); ``` No DI-specific package is needed: the client is built on RestSharp rather than `IHttpClientFactory`. ## See also - [Authentication guide](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/authentication) - [Error handling](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/error-handling) - [Package on NuGet](https://www.nuget.org/packages/ValeryVerkhoturov.WbApiClient)

wb-api-client mascot

--- title: "Rust" description: "Rust client for the Wildberries Seller API: the wb-api-client crate on crates.io, 13 API categories, redacted bearer token." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/languages/rust --- # Rust Crate: [`wb-api-client`](https://crates.io/crates/wb-api-client) on crates.io — the sources live in a [dedicated repo](https://github.com/ValeryVerkhoturov/wb-api-client-rust), mounted into the main one as the `clients/rust` submodule. - **Rust:** edition 2021 (the pipeline publishes with toolchain 1.98) - **HTTP:** `reqwest` with `rustls-tls` — no system OpenSSL needed - **Secret wrapper:** a house `SecretString` in every module ## Install ```bash cargo add wb-api-client ``` Pin a specific version by tag with a git dependency: ```toml wb-api-client = { git = "https://github.com/ValeryVerkhoturov/wb-api-client-rust", tag = "v1.20261022.0" } ``` ## Why the crate lives in its own repo crates.io links the crate to a dedicated repository, and git dependencies (`wb-api-client = { git = "…", tag = "vX.Y.Z" }`) resolve from the repo root — so the client lives in [wb-api-client-rust](https://github.com/ValeryVerkhoturov/wb-api-client-rust) with `Cargo.toml` at its root. Release tags (`vX.Y.Z`) are pushed to that repo by the same pipeline that tags the main one. ## Import shape One crate, 13 category modules under `wb_api_client::`, each with `apis` and `models` submodules: ```rust use wb_api_client::items::apis::{configuration::Configuration, items_api}; ``` Endpoint methods are free `async fn`s inside `_api` taking `&Configuration`, so you need an async runtime (`tokio` in the examples). ## Sub-modules `general`, `items`, `orders_fbs`, `orders_dbw`, `dbs`, `in_store_pickup`, `orders_fbw`, `promotion`, `communications`, `rates`, `analytics`, `reports`, `finances`. Per-module functions and models are enumerated in the per-release README: [wb-api-client-rust/README.md](https://github.com/ValeryVerkhoturov/wb-api-client-rust#readme). ## Auth ```rust use wb_api_client::items::apis::{configuration::Configuration, items_api}; #[tokio::main] async fn main() { let mut cfg = Configuration::new(); cfg.set_access_token(""); // wrapped in SecretString let result = items_api::some_endpoint(&cfg).await; println!("{:#?}", result); } ``` `set_access_token` is the ONLY path: the token is stored in a house `SecretString` whose `Debug` and `Display` both print `` — a `println!("{cfg:?}")` can't leak it. To read the raw value back you have to ask explicitly: ```rust use wb_api_client::items::apis::configuration::SecretString; let s = SecretString::new(""); println!("{}", s.expose_secret()); ``` ## Custom HTTP client Wire your own `reqwest::Client` for proxying, custom timeouts, or default headers: ```rust let mut cfg = Configuration::new(); cfg.client = reqwest::Client::builder() .timeout(std::time::Duration::from_secs(60)) .build() .unwrap(); ``` ## Testing Override the base URL at Configuration construction: ```rust let mut cfg = Configuration::new(); cfg.base_path = "http://localhost:8080".to_owned(); ``` ## See also - [Authentication guide](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/authentication) - [Error handling](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/error-handling) - [Full per-module reference on GitHub](https://github.com/ValeryVerkhoturov/wb-api-client-rust#readme)

wb-api-client mascot

--- title: "Versioning" description: "All eight language packages share one version string per release: 1.YYYYMMDD.N." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/versioning --- # Versioning All eight language packages share one version string per release: **`1.YYYYMMDD.N`**. ## Format | Field | Value | Why | | ------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `MAJOR` | `1` — fixed | Keeps Go's module path free of the `/vN` suffix that `MAJOR ≥ 2` would require. Any real breaking change to the pipeline surfaces as a major-version bump of the _code repo_, not a new module path. | | `MINOR` | `YYYYMMDD` (UTC release date) | Monotonic across days — `20260919 < 20260920 < 20261001`. Sorts as an integer under semver rules. | | `PATCH` | `N` — same-day counter | `0` for the first release of a day, `1`, `2`, … for repeats (rare). | Example: `1.20260921.0` — first release cut on 21 Sep 2026 UTC. ## Where the number comes from The daily-check workflow computes it inline: ```bash today="$(date -u +%Y%m%d)" n="$(git tag --list "v1.${today}.*" | wc -l | tr -d ' ')" version="1.${today}.${n}" ``` The resulting string goes into every language's manifest — `pyproject.toml`, `package.json`, `go.mod` tag, `pom.xml`, `composer.json`, `Cargo.toml` — verbatim. No language sees a translated form. ## What "latest" resolves to Because every release is a **stable** (non-prerelease) version, `@latest` / `LATEST` / `^1.0.0` all pick the highest published: **Python** ```bash pip install valeryverkhoturov-wb-api-client ``` **TypeScript** ```bash npm install @valeryverkhoturov/wb-api-client ``` **Go** ```bash go get github.com/ValeryVerkhoturov/wb-api-client-go@latest ``` **Java** ```xml [1.0.0,) ``` **PHP** ```bash composer require valeryverkhoturov/wb-api-client ``` **OneScript** ```bash opm install wb-api-client ``` **C#** ```bash dotnet add package ValeryVerkhoturov.WbApiClient ``` **Rust** ```bash cargo add wb-api-client ``` ## Pinning a version Pin the exact `1.YYYYMMDD.N` when reproducibility matters (CI, production deploys): **Python** ```txt valeryverkhoturov-wb-api-client==1.20260926.0 ``` **TypeScript** ```json "@valeryverkhoturov/wb-api-client": "1.20260926.0" ``` **Go** ```bash go get github.com/ValeryVerkhoturov/wb-api-client-go@v1.20260926.0 ``` **Java** ```xml 1.20260926.0 ``` **PHP** ```json "valeryverkhoturov/wb-api-client": "1.20260926.0" ``` **OneScript** ```bsl .ЗависитОт("wb-api-client", "1.20260926.0") ``` **C#** ```xml ``` **Rust** ```toml wb-api-client = "=1.20260926.0" ``` ## Release cadence New versions get cut only when upstream specs actually change. Concretely: `daily-check.yml` runs at 06:15 UTC, checksum-diffs `swaggers/` against the previous run, and only re-generates + tags if anything moved. A quiet week produces no releases; a WB portal deploy typically produces one the next morning. Manually forcing a release (e.g. to pick up a client-side fix): `Actions → Daily upstream check → Run workflow` with `force: true`. ## Deprecations WB removes fields on their own schedule. When they do: 1. Their spec drops the field. 2. Next daily-check regenerates without the field. 3. A new version publishes with the field gone. 4. Callers on the previous version keep working (their local models still have it, they just get nulls). There is no formal deprecation window — the upstream contract is the source of truth. Pin an old version if you need time to migrate. ## Multi-language version drift — impossible by construction Because the same `1.YYYYMMDD.N` is stamped into every language's manifest in the same generate-clients.py invocation, and every language is regenerated in every release run, you cannot end up with "Python 1.20260921.0 but Go 1.20260919.0" from a single upstream state — they're always in lockstep. The only way to skew: a language's publish step fails (e.g. Maven Central rejects a signature). The tag exists and the source is on GitHub; a manual re-run of the failed publish job for that specific language fixes it.

wb-api-client mascot

--- title: "Architecture" description: "A one-page tour of what's actually in the main repo, so nothing about the generated clients feels magic." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/architecture --- # Architecture A one-page tour of what's actually in the [main repo](https://github.com/ValeryVerkhoturov/wb-api-client), so nothing about the generated clients feels magic. ## The pipeline ``` download-specs.py → swaggers/*.yaml (raw upstream, checksummed) process-specs.py → swaggers/processed/ (13 passes) generate-clients.py → clients//… (openapi-generator-cli, 8 langs) ├── inject-secrets.py (SecretString wrapper per lang) ├── {black|prettier|gofmt|spotless|php-cs-fixer|rustfmt} (canonicalize formatting) └── generate-readmes.py (per-language README.md) ↓ committed at v, single tag covers all 8 languages publish.yml → PyPI / npm / Go tag / Maven Central / Packagist / hub.oscript.io / NuGet / crates.io generate-docs.py → wb-api-client-docs (API reference: operation pages, call snippets and data structures in 8 languages, RU/EN) ``` Every step is a plain Python file, so nothing is hidden behind opaque tooling. ## Why generate? WB ships OpenAPI specs. Hand-writing 13 clients × 8 languages would drift within a week. The upside of generating from the spec is that every field, every enum, every response wrapper is exactly what WB documents — and re-syncing takes exactly zero effort once the pipeline exists. The downside is that generator output is generator output — sometimes ugly, sometimes carrying artifacts from spec quirks. Which is why there's a post-processing layer. ## process-specs.py — 13 passes 1. **`inject_bearer_auth`** — drops WB's `HeaderApiKey` scheme (duplicated with the real bearer JWT semantics) and installs a single `BearerAuth` scheme instead. Every generated client then exposes exactly one auth code path. 2. **`fix_untyped_arrays`** — adds `items: {type: string}` to arrays missing their inner type (Go / TS would otherwise emit `[]any`). 3. **`widen_integer_formats`** — sets `format: int64` on integer fields with no format or with `uint32`/`uint64`/Unix-timestamp formats: WB uses those for IDs and millisecond timestamps that overflow a signed 32-bit int. Explicit `int32` and currency-code formats are left alone. 4. **`inline_top_level_arrays`** — inlines every `$ref` to a `type: array` component and collapses `allOf: [array, description-only]` wrappers (openapi-generator mishandles arrays-at-root). Runs to fixed point because inlined subtrees can contain further refs. 5. **`rename_digit_prefixed_schemas`** — `409SupplyDeliverError` → `Http409SupplyDeliverError` (invalid Go identifier otherwise). 6. **`name_inline_response_schemas`** — hoists anonymous inline 4xx/5xx bodies to `Response` (again, valid Go identifiers). 7. **`drop_response_required`** — WB marks nearly every response field `required`, but the live API omits fields at will, and every generated language then throws on deserialization. The pass deletes `required` from every schema reachable from a response; request bodies keep their `required` lists, so input validation is unaffected. 8. **`sanitize_non_ascii_enums`** — adds `x-enum-varnames` for Russian enum members like `"Склад WB"` (empty JS/Go/Java identifiers otherwise). 9. **`rename_tags_to_module`** — WB tags are Russian (`Финансовые отчёты`), which openapi-generator sanitizes to an empty class-name stem, so every language fell back to `Api`/`DefaultApi` classes. The pass rewrites every operation's tags to the module name (`Finances`, `Reports`, …), giving each module a single well-named API class (`FinancesApi`, …), and stashes the original tags in `x-original-tags` for the docs generator. 10. **`drop_date_formats`** — WB returns `""` for unset date fields (`fixTariffDateFrom`, `utdUcdDate`), which no strict date parser accepts. The pass strips `format: date` / `format: date-time` from string schemas so clients type these as plain strings. 11. **`htmlize_descriptions_to_markdown`** — every `description` field is passed through `markdownify` so HTML in docstrings renders as Markdown in generated docs. 12. **`absolutize_description_links`** — relative `[…](https://valeryverkhoturov.github.io/wb-api-client-docs/openapi/…)` links get `https://dev.wildberries.ru` prepended so links in generated docstrings still work. 13. **`add_docs_links`** — appends a `Library doc: ` line to every operation's `description`, pointing at the operation's page on this site. Generators copy `description` into docstrings/Javadoc, so every client method links to its rendered docs. `generate-docs.py` strips the line back out, so the docs site itself is unaffected. ## generate-clients.py - Runs `openapi-generator-cli` (pinned Docker image) per spec per language, into scratch directories. - Splices each per-spec SDK into a unified per-language tree — Python packages nested under `wb_api_client.`, TypeScript subpath exports, Go sub-packages, Java sub-packages, PHP sub-namespaces, for OneScript a class-name prefix (the language has no namespaces), C# namespaces, and Rust modules of a single crate (`wb_api_client::`). - Runs `inject-secrets.py` to patch every generated `Configuration` / `ApiClient` with the language-native secret-string wrapper. - Runs the pinned formatter per language: `black`, `prettier`, `gofmt`, spotless (google-java-format), `php-cs-fixer`, `dotnet format`, `cargo fmt` (rustfmt). All inside Docker — no host runtime needed. - Substitutes `__VERSION__` in top-level manifests (`pyproject.toml`, `package.json`, `go.mod`, `pom.xml`, `composer.json`, `Cargo.toml`) with the passed version. - Runs `generate-readmes.py` to emit a per-language `README.md`. The result: `clients/{python,typescript,go,java,php,onescript,csharp,rust}/` — eight ready-to-publish packages. ## Determinism The daily job would flap constantly if generation weren't deterministic. To keep it stable: - `openapi-generator-cli` Docker tag is pinned. - Every language's `openapi-generator` config sets `hideGenerationTimestamp: true`. - Every formatter is pinned to a specific version. - The PR-check workflow regenerates from scratch and fails the PR if `git diff swaggers/ clients/` is non-empty. This catches any change that would make subsequent runs produce drift. ## Publishing `daily-check.yml` computes the version, regenerates, commits, tags. Then it fires 8 `workflow_dispatch` events at `publish.yml` — one per language — with the tag as the `ref` input. Each language branch of `publish.yml`: - Python — OIDC → PyPI trusted publishing (`pypa/gh-action-pypi-publish`). - TypeScript — OIDC → npm trusted publishing (`npm publish --provenance`). - Go — the tag is pushed to [`wb-api-client-go`](https://github.com/ValeryVerkhoturov/wb-api-client-go) (see below); the job itself is a no-op because `proxy.golang.org` fetches directly from the tag. - Java — GPG-signed deploy to Maven Central via `central-publishing-maven-plugin`. Maven is pinned to 3.9.x (`stCarolas/setup-maven`): the plugin builds its bundle through a Maven-2 stager, and Maven 3.10's Resolver drops a stray `maven-metadata-local.xml` into it, so Central rejects the bundle ("Bundle has content that does NOT have a .pom file"). - PHP — ping the Packagist update-package API. - OneScript — `opm build` then `opm push` to hub.oscript.io. - C# — `dotnet pack` then `dotnet nuget push` to NuGet. - Rust — `cargo publish` to crates.io (GitHub environment `crates-io`, toolchain pinned at 1.98). Separately, daily-check regenerates the API reference (`generate-docs.py` — operation pages, call snippets, and request/response data structures per language, with links to the WB specification) and pushes it to `wb-api-client-docs`, which deploys to GitHub Pages. Reusable workflows (`workflow_call`) would be cleaner, but PyPI + npm trusted publishing don't support them — both the OIDC token's `job_workflow_ref` (callee) and the Sigstore attestation cert's `workflow_ref` (caller) have to point at the same file, which is impossible with `workflow_call`. Hence `workflow_dispatch`. ## The sibling repositories Four languages live in their own repos, mounted here as git submodules: - [`ValeryVerkhoturov/wb-api-client-php`](https://github.com/ValeryVerkhoturov/wb-api-client-php) → `clients/php`. Reason: Packagist requires `composer.json` at the ROOT of the crawled repo. - [`ValeryVerkhoturov/wb-api-client-1c`](https://github.com/ValeryVerkhoturov/wb-api-client-1c) → `clients/onescript`. So 1C users can clone just the client and install it from a tag. - [`ValeryVerkhoturov/wb-api-client-go`](https://github.com/ValeryVerkhoturov/wb-api-client-go) → `clients/go`. Reason: Go versions modules with repository tags, so the module must sit at a repo root (`go get github.com/ValeryVerkhoturov/wb-api-client-go`) — a subdirectory module would need `clients/go/`-prefixed tags and `go get` would fetch the whole monorepo. - [`ValeryVerkhoturov/wb-api-client-rust`](https://github.com/ValeryVerkhoturov/wb-api-client-rust) → `clients/rust`. Reason: crates.io links the crate to a dedicated repo, and git dependencies (`wb-api-client = { git = "…", tag = "vX.Y.Z" }`) resolve from the repo root. Every daily run commits and tags all four siblings BEFORE the main repo — otherwise the submodule pointers recorded in the release commit would reference commits their remotes have never seen, and a fresh `git clone --recurse-submodules` would fail. A fifth sibling is [`ValeryVerkhoturov/wb-api-client-docs`](https://github.com/ValeryVerkhoturov/wb-api-client-docs) — this site. Not a submodule: daily-check clones it, runs `generate-docs.py`, and pushes the result back. ## The OneScript generator openapi-generator ships no OneScript target, so it lives in a separate repo — [`ValeryVerkhoturov/onescript-openapi-generator`](https://github.com/ValeryVerkhoturov/onescript-openapi-generator). It is an ordinary plugin: the class extends `DefaultCodegen`, registers through `META-INF/services`, and is loaded next to `openapi-generator-cli.jar`, so `-g onescript` resolves like a built-in generator. The main repo pins it by commit — the OneScript equivalent of pinning the Docker image tag for the other languages. ## What's not generated The hand-written parts of the main repo: - `scripts/` — the pipeline itself, organized by bounded context under `scripts/pipeline/`: `core/` (shared kernel: repo paths, spec/module/language naming, YAML IO, external-tool runners (subprocess, docker, git, python, macOS codesign)), `specs/` (download, postprocess), `generation/` (generate, secrets), `onescript/` (toolchain, manifests, verify), `docs/` (reference, readmes). The flat `scripts/*.py` files are thin CLI shims over those modules, named verb-object by what they produce: `download-specs`, `process-specs`, `generate-clients`, `generate-readmes`, `generate-docs`, `generate-onescript-manifests`, `inject-secrets`, `verify-onescript`. House rule: no `#` comments and no docstrings under `scripts/` — enforced by `make scripts-check` and the PR check. - `generator-configs/*.yaml` — one openapi-generator config per language. - `templates/*` — top-level manifests with `__VERSION__` placeholders. - `.github/workflows/*` — daily-check, per-language publish, PR drift check. - `Makefile`, `README.md`, `CLAUDE.md`. Everything under `clients/` is generated output — never hand-edit it, regenerate. ## Full pipeline docs The main-repo [`CLAUDE.md`](https://github.com/ValeryVerkhoturov/wb-api-client/blob/main/CLAUDE.md) is a working, machine-readable version of the same content — kept up to date because CI itself reads it.

wb-api-client mascot

--- title: "Contributing" description: "Bugs, spec-drift oddities, generator quirks: open an issue on the main code repo — ValeryVerkhoturov/wb-api-client/issues." lang: en-US source: https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/contributing --- # Contributing ## Reporting issues Bugs, spec-drift oddities, generator quirks: open an issue on the main code repo — [`ValeryVerkhoturov/wb-api-client/issues`](https://github.com/ValeryVerkhoturov/wb-api-client/issues). Include: - Which language client (Python / TypeScript / Go / Java / PHP / OneScript / C# / Rust) - Which sub-module (`items`, `orders_fbs`, `analytics`, …) - Version string from the manifest (`1.YYYYMMDD.N`) - Minimal reproduction — one API call, one exception, one expected-vs-actual Docs-site issues (typos, missing content, broken examples) go on the docs repo: [`ValeryVerkhoturov/wb-api-client-docs/issues`](https://github.com/ValeryVerkhoturov/wb-api-client-docs/issues). ## Do NOT hand-edit generated code Every file under `clients/` in the main repo is generated. Editing there gets overwritten on the next daily-check run — the PR check will also flag it before merge. If you find something wrong in generated output, the fix goes in one of: - **`scripts/pipeline/specs/postprocess.py`** — a new pass, if the fix applies to the spec before generation. - **`scripts/pipeline/generation/secrets.py`** — if the fix is in the generated `Configuration` / `ApiClient` boilerplate. - **`generator-configs/*.yaml`** — if it's an openapi-generator option toggle. - **`templates/{lang}/*`** — if it's in the top-level manifest. The [architecture reference](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/architecture) has the full pipeline map. ## Local development ```bash git clone --recurse-submodules https://github.com/ValeryVerkhoturov/wb-api-client cd wb-api-client pip install -r scripts/requirements.txt # ruamel.yaml, markdownify, requests, black, flask ./scripts/download-specs.py # pull upstream (may 498 locally) python scripts/process-specs.py # -> swaggers/processed/ ./scripts/generate-clients.py 1.20260920.0 # -> clients/{python,typescript,go,java,php,onescript,csharp,rust} make verify # build + format check every language ``` `generate-clients.py` always regenerates all eight languages. For per-language iteration during debugging, comment out the other `queue_generation` calls rather than adding a flag. The `OPENAPI_GENERATOR_THREADS` environment variable controls generation concurrency (default: 2). Formatting runs inside Docker so contributors don't need language runtimes installed. `make verify-` and `make {black,prettier,gofmt,spotless,php-cs-fixer,dotnet-format}` are individual targets. OneScript has no formatter, so `make verify-onescript` compiles every module with `oscript -check` and loads the package instead. `make help` lists everything. The pipeline itself lives in `scripts/pipeline/`, organized by bounded context (see [architecture](https://valeryverkhoturov.github.io/wb-api-client-docs/en/guides/architecture)); the flat `scripts/*.py` files are just CLI shims. Changes under `scripts/` are additionally gated by `make scripts-check` (kernel smoke checks, no `#` comments, no docstrings), and black covers `scripts/` too, not just `clients/python`. ## PR flow 1. Fork the main repo. 2. Make your change to `scripts/`, `templates/`, `generator-configs/`, or a workflow. 3. Regenerate locally so `clients/` matches. 4. Commit `swaggers/`, `clients/`, and your source changes together. 5. Open a PR. The PR-check workflow will regenerate from scratch and fail if `git diff` is non-empty against your commit — that catches non-determinism. ## Contributing to the docs site This site lives in [`ValeryVerkhoturov/wb-api-client-docs`](https://github.com/ValeryVerkhoturov/wb-api-client-docs). ```bash git clone https://github.com/ValeryVerkhoturov/wb-api-client-docs cd wb-api-client-docs npm install npm run docs:dev # → http://localhost:5173/wb-api-client-docs/ ``` Push to `main`; the deploy workflow builds and publishes to GitHub Pages automatically. ## License Both repos are Apache 2.0. Contributions are assumed to be under the same license.

wb-api-client mascot