> ## Documentation Index
> Fetch the complete documentation index at: https://developers.meshapi.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> New endpoints, breaking changes, deprecations, and SDK releases.

<Note>
  Subscribe at [`/changelog/rss.xml`](/changelog/rss.xml) to hear about deprecations and
  breaking changes without checking back. Every entry that removes or changes existing
  behaviour is tagged **Deprecated** or **Changed**; where the change is a response shape,
  the entry says which API version carries it — see
  [API versioning](/docs/reference/api-versioning) for how to pin a version so a shape
  change cannot reach your code unannounced.
</Note>

<Update label="October 2026" description="Mesh Code desktop app; limit a BYOK key to models; fal image models on image edits; clearer audio errors; fixed usage keys on all three inference endpoints; web search priced per engine" tags={["Added", "Images", "Audio", "Changed", "Deprecated"]}>
  ## Web search is priced per engine

  **Changed.** `POST /v1/web/search` is billed at the price of the engine that answered,
  instead of a flat \$0.005: `native` \$0.005, `tavily` \$0.008 (\$0.016 with
  `search_depth: "advanced"`), `tinyfish` \$0.005 plus \$0.001 per returned result
  carrying `page_content`. Failed and empty searches are still free.
  See [Web Search billing](/docs/capabilities/web-search#billing).

  ## Fixed `usage` keys on Responses and Messages; provider keys return on chat

  **Changed.** `POST /v1/responses` and `POST /v1/messages` now return a fixed set of
  `usage` keys whichever provider serves the request, all present and `0` when unused:

  * **Responses:** `input_tokens_details` (`cached_tokens`, `cache_write_tokens`,
    `cache_write_1h_tokens`, `audio_tokens`), `output_tokens_details.reasoning_tokens`,
    `total_tokens`, `byok_used` and `fallback_triggered`.
  * **Messages:** `cache_creation_input_tokens`, `cache_read_input_tokens`,
    `cache_creation` split by TTL, `byok_used` and `fallback_triggered`, on the response
    body and on `message_delta`. They were previously returned only when non-zero.

  On all three endpoints, keys a provider adds on its own (such as `is_byok`,
  `prompt_cache_hit_tokens` or `num_sources_used`) are returned alongside the fixed keys.
  On chat completions they had been removed since the change below. This applies to every
  API version. See [Usage Object](/docs/reference/usage-object).

  ## One `usage` shape on chat completions

  **Changed.** `usage` on `POST /v1/chat/completions` (including the final chunk of a stream)
  now has the same keys whichever provider serves the request, all present and `0` when
  unused: `prompt_tokens_details` (`cached_tokens`, `cache_write_tokens`,
  `cache_write_1h_tokens`, `audio_tokens`), `completion_tokens_details`, `byok_used` and
  `fallback_triggered`. `total_tokens` is always `prompt_tokens + completion_tokens`. Keys a
  provider added on its own, such as `is_byok` or `prompt_cache_hit_tokens`, are no longer
  returned; a provider's cache hits appear as `cached_tokens`. This applies to every API
  version. See [Reading the result](/docs/capabilities/caching#reading-the-result).

  **Deprecated.** The top-level `usage.cache_creation_input_tokens` on Claude responses
  duplicates `prompt_tokens_details.cache_write_tokens`. It is still returned; retirement
  date to be announced.

  ## Limit a BYOK key to specific models

  A provider key you register with Mesh used to serve every model Mesh routes to that
  provider. **Allowed models** on the key (`allowed_models` in the API) now limits it to
  exact model IDs or brand wildcards such as `openai/*`. A model outside the list is served
  on Mesh's credentials and billed to your balance, or, with fallback off, refused with
  `403 byok_model_not_allowed`. See
  [Limit a key to specific models](/docs/capabilities/byok#limit-a-key-to-specific-models).

  ## Mesh Code

  Mesh Code is a desktop app for agentic coding, with every Mesh model behind one key. It
  plans, writes files, runs commands and starts dev servers inside a project folder you
  choose; each step is either auto-approved by a mode you pick or shown to you as a diff
  first. Version 0.2.4 (1 October) ships for macOS (Apple silicon), Linux (AppImage) and
  Windows (x64). Download it from [GitHub](https://github.com/aifiesta/mesh-code).

  ## fal image models on `/v1/images/edits`

  `fal-ai/gemini-3-pro-image-preview/edit` and `fal-ai/nano-banana-pro/edit` serve
  `POST /v1/images/edits`, with one source image or several. They bill at the model's
  per-image or per-megapixel rate. See
  [Edit an image](/docs/capabilities/image-generation#edit-an-image).

  ## Clearer audio errors

  * Gemini speech-to-text accepts `.m4a`, `.mp4`, `.webm` and `.opus` audio, and a file a model
    cannot take is refused with a message that says why, not a generic capability error.
  * A rate-limited ElevenLabs speech-to-text request moves on to fallback instead of
    failing as `422` `model_id: Field required`.

  ## Fixes

  * A `tool` message whose `content` is a list of parts reaches Vertex Gemini models as
    its text instead of failing with `500`.
  * A multipart upload sent to a JSON endpoint is a `422` validation error, not a `500`.
  * Large usage exports no longer fail partway through.
</Update>

<Update label="September 2026" description="API version 2026-09; video billing; flex tier; evaluate, template versions, end-user erasure; provider stop reasons; new webhook events and models" tags={["Added", "Changed", "Deprecated", "API", "Video", "Audio"]}>
  ## Poll a video task with the id you already have

  Version `2026-09` is out (released 3 September). Pin it and
  `GET /v1/video/generations/{id}` accepts the `request_id` Mesh returned when you created
  the task, as well as the provider task id:

  ```bash theme={null}
  curl "https://api.meshapi.ai/v1/video/generations/req_01ARZ3NDEKTSV4RRFFQ69G5FAV" \
    -H "Authorization: Bearer rsk_..." \
    -H "X-Mesh-Version: 2026-09"
  ```

  Useful when the request id is what your logs and your database already hold, so you no
  longer have to store a second identifier just to check on a video. The task id is tried
  first and the request id only when it misses, so nothing you poll today changes. Both
  lookups are scoped to the key's owner: an id that belongs to someone else is a `404`,
  never a leak.

  Video task bodies also gained a `request_id` field — on the create response, on
  `GET /v1/video/generations/{id}`, and on every row of the `GET /v1/video/generations`
  list. Under `2026-08` the field is stripped, so a pinned parser sees exactly what it saw
  before.

  Two things worth knowing before you switch:

  * Tasks created before 20 August 2026 predate the recorded request id and stay reachable
    by task id only.
  * `2026-08` is still the baseline, so an unpinned request is unaffected. Send the header
    (or [pin it on your key](/docs/reference/api-versioning#pinning-a-whole-api-key)) to use
    this.

  `GET /v1/api-versions` now reports a `capabilities` list per version — `2026-09` carries
  `video.poll_by_request_id` — so you can check what a version gives you without reading
  the changelog. The list is cumulative: a later version includes everything an earlier one
  introduced. See [API versioning](/docs/reference/api-versioning).

  ## Model pricing shows the rate you are billed

  Under `2026-09`, when your account has a discount on a model, its `pricing` object on
  `GET /v1/models`, `GET /v1/models/search` and the per-model reads carries `discounted`:
  the same rates with the discount applied. That covers tokens, per-image, per-second
  video, resolution tiers, cache reads and `price_matrix` entries. With no discount the
  field is absent. List prices are unchanged. `tool_pricing` is not included, since a
  hosted-tool fee is charged after the discount. Under `2026-08` the field is stripped.

  Where a discount covers only some of the provider accounts that can serve a model,
  `discounted` shows the best rate attainable; a request bills at the rate of the account
  that served it. A request served on your own provider key (BYOK) is billed without the
  discount.

  ## Video billing follows what the provider renders

  **Changed.** These video routes now bill the clip the provider renders:

  * DeepInfra `google/veo-3.1` and `google/veo-3.1-fast` bill their fixed 8 s clip, not
    5 s.
  * AtlasCloud video bills per second at the resolution tier. `alibaba/wan-2.7` is $0.10/s
    at 720P and $0.15/s at 1080P or with no resolution sent. Most other AtlasCloud video
    models are re-priced per second from AtlasCloud's own quotes, with a separate audio
    rate where AtlasCloud charges one. A request that leaves out duration, resolution or
    audio bills what AtlasCloud renders by default.
  * Runware video bills its sound rate whenever sound is rendered, including when the
    model adds it by default (`google/veo-3.1` does unless you turn it off).
  * Venice video bills Venice's quote for the duration, resolution and audio you ask for,
    not one flat price.
  * A BytePlus Seedance request with no `resolution` bills the tier it renders by default
    (720p for Seedance 2.x and 1.5 pro, 1080p for 1.0 pro). It used to bill a higher rate.

  Two requests are now refused at submit instead of billed at the wrong rate: sound on a
  Runware model that prices sound separately but has no audio rate for that clip, and an
  AtlasCloud per-second request no rate covers (Kling o3 and Omni `video-edit`, Grok
  `edit-video`, and the `-SR` super-resolution tiers).

  ## A video input a model cannot take is now a `422`

  Send a `content` item whose role the serving model does not accept — a closing frame to
  a model with no concept of one, a reference video to a model that takes only images —
  and the submit is now rejected with `422`. The message names the item by its position
  in `content` and, where the item was reinterpreted on the way in, what it resolved to.
  It used to be dropped: the generation ran without it and you were billed for a video
  built from inputs you did not send.

  For example, `bytedance/seedance-1.0-lite` takes an opening frame but not a closing one,
  so a second image sent as the `last_frame` is refused:

  ```json theme={null}
  {
    "error": {
      "code": "unprocessable_entity",
      "message": "Model 'bytedance/seedance-1.0-lite' on provider 'together' cannot use content[1] (image_url → last_frame). It would be dropped and the generation billed anyway — remove it, or choose a model that takes it."
    },
    "request_id": "req_..."
  }
  ```

  **Top-level parameters are unchanged.** A tunable the model does not implement is still
  accepted and reported in `unsupported_params` — a dropped parameter still gives you the
  video you asked for, tuned differently, while a dropped asset changes what was generated.

  **This applies on every API version.** It is a refusal, not a response shape, so pinning
  `X-Mesh-Version` does not hold the old behaviour.

  Check `supports_video_first_frame` and its five siblings on `GET /v1/models` before you
  send, and `GET /v1/models/{model_id}/providers` to see whether any route takes the input.

  Full detail: [Finding out what a model accepts](/docs/capabilities/video-generation#finding-out-what-a-model-accepts).

  ## Error messages name the provider that answered

  **Changed.** An error from an upstream provider now keeps that provider's name in
  `message`, `upstream_error` and `provider_error.provider`. On our own provider keys a
  cloud or reseller provider's name (OpenRouter, Bedrock, Vertex, BytePlus, …) used to
  read "the provider"; your own keys (BYOK) always showed it. Only the name is new: cloud resource ids and anything credential-shaped
  are still removed, and a problem with our own provider account still reads as a
  generic `upstream_error`. The name describes who answered that one request, not a
  routing guarantee. See [What errors never contain](/docs/reference/errors#what-errors-never-contain).

  ## Stop reasons arrive as the provider sent them

  **Changed.** `finish_reason` is mapped to an OpenAI value only where the meaning is the
  same (`end_turn` → `stop`, `content_filtered` → `content_filter`) and is forwarded
  verbatim otherwise, so a Gemini `MALFORMED_FUNCTION_CALL` or an Anthropic `pause_turn`
  no longer reads as `stop`. Every chat choice also carries `native_finish_reason`, the
  provider's untranslated value, or `null` when the provider sent none. This applies on
  every API version, streamed or not.

  ## Evaluate: typed answers instead of prose

  `POST /v1/evaluate` asks a model structured questions about a piece of application state
  and returns each answer typed: a chosen option with a probability for every option, a
  score on a scale you define, or a single probability. It is served by
  `typesafe/jev-1.13` and is in beta, so the contract may change. A key restricted to
  specific scopes needs `evaluate:write`. The same call is the `evaluate` tool on the
  [MCP server](/agent/mcp). See [Evaluate](/docs/capabilities/evaluate).

  ## Prompt templates keep a version history

  Template content is stored as immutable versions. Saving a version leaves the live one
  alone; publishing switches it:

  * `POST /v1/templates/{id}/versions` saves a version; `GET .../versions` and
    `GET .../versions/{version_id}` read them.
  * `POST /v1/templates/{id}/publish` makes the latest saved version live.
  * `POST /v1/templates/{id}/versions/{version_id}/rollback` republishes an earlier
    version's content as a new version.
  * A chat request can set `template_version` to run an exact version instead of the
    published one.

  **Deprecated:** the content fields on `PATCH /v1/templates/{id}`: `system`, `messages`,
  `model`, `params` and `variables`. They are still accepted, and a `PATCH` that carries
  them saves a new version and publishes it immediately. No retirement date is set. See
  [Prompt templates](/docs/capabilities/prompt-templates).

  ## Erase an end user's identifier

  `POST /v1/end-users/erasures` takes `{"end_user_id": "..."}`, the identifier you sent as
  `safety_identifier` or `user`, and clears it from your organization's usage records, so
  a deletion request from one of your own users can be passed through. Billing rows are
  kept; only the identifier is removed. Request logs are not erased, and `pending` always
  lists them. It also lists usage records when one call hits its row cap; call again to
  finish. The analytics copy is not erased either; it expires within 90 days. A request still in
  flight can re-attribute a row, so call again once traffic for that identifier has
  stopped; the call is idempotent. The key must belong to an organization; a key
  restricted to specific scopes needs `end-users:write`.

  ## `safety_identifier` and the `reasoning` object

  **Deprecated:** `user`, in favour of `safety_identifier`, the name OpenAI moved the field
  to. Both are accepted on chat completions, Responses and embeddings, and
  `safety_identifier` wins when both are sent. `safety_identifier` is also accepted on
  images, audio, video, batch, compare, moderations, web search, file search and
  `/v1/messages`. The dashboard's usage CSV export can be filtered to one end user by the
  exact `safety_identifier` sent.

  **Deprecated:** `reasoning_effort` on `/v1/chat/completions`, in favour of an
  OpenRouter-compatible `reasoning` object. Send `reasoning.effort` (`max`, `xhigh`,
  `high`, `medium`, `low`, `minimal` or `none`) or `reasoning.max_tokens`, a thinking
  budget, but not both. When the object and `reasoning_effort` are both sent, the object
  wins. OpenRouter receives the object as sent. Other providers get the effort it implies
  where they take one, with a `max_tokens` budget mapped to `low`, `medium` or `high`.
  `exclude: true` (reason without returning the reasoning) is honoured only on OpenRouter
  and on OpenAI models served through the Responses API.

  Neither deprecated field has a retirement date yet.

  ## Flex service tier

  `service_tier: "flex"` on `/v1/chat/completions` and `/v1/responses` serves an OpenAI
  model from OpenAI's slower, cheaper flex pool and bills it at OpenAI's flex rates. A
  model that cannot serve flex is refused rather than billed at standard rates. It
  launched on the OpenAI models with published flex prices, and `openai/gpt-6-sol` and
  `openai/gpt-6-luna` joined later in the month. See
  [Service Tiers](/docs/capabilities/service-tiers#which-models-serve-flex).

  **Changed.** `service_tier` takes only `auto`, `default` and `flex`. `priority`, `fast`
  and `scale` used to be accepted and dropped; they are now a `422`.

  ## Model discovery

  * `GET /v1/models/search` takes `capability=` (for example `tools`, `thinking`,
    `image_edit`), returns `facets` counting the results each filter value would give, and
    sorts with `sort=price` when paired with exactly one `output_modality` of `text`,
    `image` or `video`.
  * Discovery follows the calling key's model policy: `GET /v1/models`,
    `/v1/models/search` and the per-model reads list only models the key may call, and a
    per-model read of an excluded model is `403`.
  * **Changed.** `supports_batching`, on `/v1/models` and `/v1/models/{id}/providers` and
    in the `batching` filter, is `true` only where `/v1/batches` can run the model. A model
    whose default provider is Anthropic, Vertex or Bedrock now reads `false`;
    `/v1/batches` answered `501` for it.

  See [Models](/docs/reference/models#listing-available-models).

  ## Exclude models from a key, an organization or a team

  An API key can carry `blocked_models` beside `allowed_models`: exact model ids or brand
  wildcards it may not call. Exclusion wins over the allow-list, and the Auto Router,
  fallbacks and `/v1/router/select` respect it. A refused request is `403 model_excluded`,
  naming the model. Set it in the key drawers under **API Keys**, or on `/v1/keys` with an
  admin key. Model access can also be set for the organization, a team or a member in the
  dashboard; every layer has to permit a model. See
  [API keys](/docs/getting-started/api-keys#model-allow-lists).

  ## Pin a key to one provider

  `fixed_provider` on `POST` and `PATCH /v1/keys` makes one provider the only one that may
  serve that key. There is no cross-provider fallback, and a model that provider does not
  serve is `400 model_not_available_on_pinned_provider`. `GET /v1/providers/pinnable`
  lists the providers you can pin to, and `GET /v1/providers/{provider}/models` lists what
  a pin still serves.

  ## Key routing policy and rate-limit tiers

  * **Changed.** A saved key routing policy stores only the fields you set, so an omitted
    field follows the gateway default. A policy saved before this change keeps what it
    stored, which can include `fallback.enabled: false` on a key whose owner only turned
    on retries. See
    [Per-key routing policy](/docs/platform/retry-and-fallback#per-key-routing-policy).
  * Your organization's rate-limit tier sets how far its admins can raise a key's
    `rpm_limit` and `rpd_limit`. `standard` allows 1,000 rpm and 1,000,000 rpd, as before;
    `enterprise` allows 20,000 and 5,000,000. Mesh sets the tier; ask
    [support](/docs/reference/support) to change it.

  ## New webhook events

  * `payment.failed`: a Stripe or Cashfree top-up failed, or an auto-recharge charge
    failed (which also fires `auto_recharge.failed`). Your balance is unchanged.
  * `byok_key.failed`: one of your provider keys was rejected upstream; the payload says
    whether the request was served on Mesh credentials instead.
  * `provider.fallback`: a request was served by a different provider than the one
    requested. It is sent at most once every 5 minutes for each pair of providers and
    model.

  See [Notifications](/docs/capabilities/notifications#available-events).

  ## New models

  * Gemini audio: `google/gemini-3.5-transcribe` (speech-to-text) and
    `google/gemini-2.5-flash-tts` on Vertex; `google/gemini-3.1-flash-tts`, already on
    Venice, now also on Vertex as a fallback; `google/gemini-3.8-flash-tts` and
    `google/gemini-3.8-flash-lite-tts` on AI Studio. See
    [Text to Speech](/docs/capabilities/text-to-speech) and
    [Speech to Text](/docs/capabilities/speech-to-text).
  * ElevenLabs: `elevenlabs/eleven_v4` and `elevenlabs/eleven_v4_turbo` text-to-speech.
  * GPT Image 2.5: `openai/gpt-image-2.5-flare` and `openai/gpt-image-2.5-sunburst`, with
    edits, inpainting and reference images. See
    [Image Generation](/docs/capabilities/image-generation).
  * Sync Lipsync v3 on fal: `fal-ai/sync-lipsync/v3` lipsyncs a video to an audio track,
    and `fal-ai/sync-lipsync/v3/image-to-video` animates a still image. See
    [Video Generation](/docs/capabilities/video-generation).
</Update>

<Update label="August 2026" description="Dated API versions; Anthropic Messages API; Node.js SDK 2.0" tags={["Added", "Changed", "API", "SDKs"]}>
  ## Pin the API contract to a date

  Mesh now versions its public contract **by date**, in a request header. The current
  version is `2026-08` (released 15 August):

  ```bash theme={null}
  curl https://api.meshapi.ai/v1/models \
    -H "Authorization: Bearer rsk_..." \
    -H "X-Mesh-Version: 2026-08"
  ```

  A pinned request states which response shapes your code can parse, so a future change to
  those shapes cannot reach it. `/v1` stays exactly where it is — it is a namespace, not a
  version, and there is no `/v2` planned.

  **You do not have to do anything.** Sending no header gets you the *oldest* supported
  version, so publishing a new one never moves existing traffic.

  * `GET /v1/api-versions` lists every version served, with its `status`, release date,
    and which one is the `baseline` and which is `latest`.
  * An API key can carry a pin, so code you do not control still gets a fixed contract —
    set it on the key's **API Version** field under **API Keys**. A header on the request
    wins over the key's pin.
  * Every response echoes `X-Mesh-Version` with the version that served it.
  * A version Mesh does not serve is rejected with `400 invalid_api_version` rather than
    silently downgraded, so a typo'd pin fails immediately instead of quietly serving you
    something else. The error body lists `supported_versions`.

  When a version is eventually retired, its responses will carry standard `Deprecation` and
  `Sunset` headers first, so monitoring can notice before anyone reads a page. Nothing is
  deprecated today.

  Full detail: [API versioning](/docs/reference/api-versioning).

  ## Anthropic Messages API

  `POST /v1/messages` speaks Anthropic's Messages format, so a client built on Anthropic's
  SDK, [Claude Code](/docs/capabilities/claude-code) included, can point at Mesh unchanged
  and address any model Mesh serves, not only Claude. Streaming, tool use and vision are
  supported. See [Messages API](/docs/capabilities/messages-api).

  ## Playground saves conversations

  The dashboard's chat Playground saves each conversation as a thread, together with the
  request configuration that produced it, so reopening a thread restores the run. Threads
  are visible only to you.

  ## Node.js SDK 2.0.0 — the request id rides on what each call returns

  **Breaking:** `MeshAPIConfig.onResponse` and the `ResponseInfo` type are gone. The hook
  was configured once per client, so with several calls in flight it could not say which
  response belonged to which call.

  The id now lives on the value the call returned:

  ```typescript theme={null}
  const completion = await client.chat.completions.create(params);
  console.log(completion.requestId);

  const stream = client.chat.completions.create({ ...params, stream: true });
  console.log(await stream.requestId); // resolves when headers arrive, before the first chunk
  for await (const chunk of stream) { /* ... */ }
  ```

  * Every streaming method returns an `SSEStream<T>`, an `AsyncIterable` that also carries
    `requestId` as a promise. It stays readable after a mid-stream failure or an abort,
    which is when you most want it.
  * `SSEStream.cancel()` releases the connection for a stream you read the id from but
    never iterated. It is also wired to `Symbol.asyncDispose` for `await using`.
  * Breaking out of a `for await` over a stream now closes the connection; previously it
    leaked the socket.
  * Iterating one stream object twice no longer issues, and bills, a second request.
  * Errors raised from a mid-stream error frame no longer report an empty `requestId`.

  Widening the return types is source-compatible: only code that set `onResponse` has to
  change. See [Streaming](/sdk/streaming) and [Error handling](/sdk/error-handling).

  ## SDK releases

  * **Node.js 2.0.0** — request id on every returned value; `onResponse` removed (above).
  * **Node.js 1.0.4** — repaired Responses API streaming and the realtime ESM entry point;
    clearer error messages; `models.free()` / `models.paid()` now filter server-side via
    `?free=`; the SDK version header matches the package version again.
  * **Node.js 0.1.2** — exposed `x-request-id` on successful responses via `onResponse`
    (superseded by 2.0.0 four days later). Tagged `v1.0.2` in the repository.
</Update>

<Update label="July 2026" description="Audio translations, structured outputs, realtime GA protocol, SDK releases" tags={["Added", "Audio", "SDKs"]}>
  ## Translate audio to English

  `POST /v1/audio/translations` accepts audio in any language and returns the speech
  translated to English. This is distinct from the transcribe-and-translate helper at
  `POST /v1/audio/transcriptions/translate`, which transcribes first and then translates
  the text.

  ```python theme={null}
  client.audio.audio_translate(audio_bytes, AudioTranslationsParams(model="..."), filename="...")
  ```

  ```typescript theme={null}
  client.audio.translations(audioBytes, { model: "..." }, { filename: "..." });
  ```

  ```go theme={null}
  client.Audio.Translations(ctx, audioBytes, "filename.mp3", meshapi.AudioTranslationParams{Model: "..."})
  ```

  `model` is required — check `GET /v1/models` for models that support translation. A
  model that does not is refused with `422`. See [Audio](/docs/capabilities/audio).

  ## Structured outputs in the SDKs

  Node.js 0.1.1 adds a `parse()` method on chat completions that sends a JSON-schema
  `response_format` and returns a typed value instead of a string:

  ```typescript theme={null}
  const country = await client.chat.completions.parse(params, Country);
  ```

  It takes any Standard Schema validator (Zod, Valibot, ArkType) or a raw schema. The
  Python `parse()` is not yet published to PyPI. See
  [Structured output](/sdk/structured-output).

  ## Realtime audio — GA protocol

  Python 0.1.11 and Go v0.1.12 move the realtime client to the GA wire protocol: audio is
  sent as base64 `input_audio_buffer.append` events, and output audio is decoded into the
  message's `audio` field rather than left as a base64 string. See
  [Realtime](/sdk/realtime).

  ## SDK releases

  * **Python 0.1.11** — realtime GA protocol.
  * **Python 0.1.10** — realtime TLS trusts the `certifi` bundle; stricter request params;
    error mapping matches the gateway's error codes; `image_bytes()` helper for reading
    generated images; spec fields that had been dropped are now declared.
  * **Python 0.1.9** — transcription `language_code` example fixed.
  * **Python 0.1.8** — audio translations.
  * **Go v0.1.12** — realtime GA protocol.
  * **Go v0.1.11** — SSE buffer no longer truncates large frames; error mapping matches the
    gateway's error codes; `Bytes()` helper for generated images; missing spec fields added.
  * **Go v0.1.10** — transcription `language_code` example fixed.
  * **Go v0.1.9** — audio translations.
  * **Node.js 0.1.1** — covers the full public data plane: moderations, web search, router
    select, models search and get, Responses API list and get, image edits, audio
    translations, plus structured outputs. The unreleased `/v1/documents` wrapper was
    removed.

  Versions are as published to PyPI (`meshapi`), npm (`meshapi-node-sdk`) and the Go module
  proxy. Before 1.0.4, Node.js repository tags do not match npm: npm 0.1.1 is tagged
  `v1.0.0` and `v1.0.1`, and 0.1.2 is `v1.0.2`. See [SDK overview](/sdk/overview) for
  install commands.
</Update>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.