Browse release notes for the HoneyHive Python SDK, TypeScript API SDK, TypeScript Control Plane SDK, and CLI. Stay current with new features, bug fixes, and breaking API changes.
Added usage get and usage current for organization usage. usage get takes --org-id, --start-date, and --end-date (YYYY-MM-DD in UTC, end exclusive, 1 to 92 days after the start), plus optional --group-by (workspace_id or project_id). usage current takes --org-id. Both use a control plane API key (--control-plane-api-key / HH_CONTROL_PLANE_API_KEY).
Added charts query, which runs a chart query over a project’s events. It takes --project-id, --date-range, and --query, plus optional --filters and --product-filters, and authenticates with a data plane API key.
Added --project-id to datasets list, create, update, delete, add-datapoints, and remove-datapoint. Also added it to datapoints list, create, create-batch, get, update, and delete, to every experiments command, and to events get and events search. With --project-id, the command calls the project-scoped route and authenticates with a data plane API key (--data-plane-api-key / HH_DATA_PLANE_API_KEY). Without it, the command calls the legacy route with the project API key, the same as before.
datapoints create with more than one dataset in linked_datasets now fails with a 400 that explains the problem. It used to fail with a 500. A datapoint can link to one dataset only.
Calling the commands listed above without --project-id is deprecated. --project-id becomes required in the next major version. Pass it with a data plane API key.
--linked-event on datapoints update is deprecated and ignored, and now prints a deprecation warning. Set the linked event when you create the datapoint. The flag will be removed in the next major version.
The standalone binaries (Homebrew and direct downloads) now embed Node.js 24, which requires macOS 13.5 or newer. The Homebrew formula now requires macOS Ventura (13) or newer. npm and npx installs are unaffected.
Homebrew 6.0.0 and later load formulae only from taps or formulae you have trusted. Install with brew install honeyhiveai/tap/honeyhive, which trusts the formula. If brew upgrade stops upgrading the CLI, run brew trust --formula honeyhiveai/tap/honeyhive once. npm, npx, and install-script installs are unaffected.
Clearer deprecation message for the events project parameter
When you pass the deprecated project argument to a client.events method, the DeprecationWarning now says that the project comes from the API key. Before, it said that the backend infers the project from session_id or the filters. The parameter still works and is still scheduled for removal in v2.0.
New client.charts.query() method runs a chart query on a project and returns one aggregated result. query.kind selects time series, categorical, scalar, histogram, or table. It needs project_id and a data plane API key. New exported QueryChartRequest and QueryChartResponse types.
More methods now accept an optional project_id. With project_id, the call uses the project-scoped route and authenticates with the data plane API key (dataPlaneApiKey / HH_DATA_PLANE_API_KEY):
client.events: get and search.
client.datasets: list, create, update, delete, addDatapoints, and removeDatapoint.
client.datapoints: list, get, create, createBatch, update, and delete.
Calling any of the methods listed above without project_id is deprecated. It becomes required in the next major version. These calls still use the legacy route and the project API key, and each method logs a one-time deprecation warning to stderr. To migrate, pass project_id and configure a data plane API key.
A call with project_id needs a data plane API key and never falls back to the project API key.
linked_event is deprecated and ignored on client.datapoints.update(). Set linked_event when you create the datapoint. The property stays in the type definitions, so existing code keeps compiling.
Organization usage reporting through the new usage namespace. usage.get({ org_id, start_date, end_date, group_by? }) returns event counts and computed-metric counts in one bucket per calendar month. You can split the results by workspace_id or by project_id. The range starts on start_date and ends before end_date, and it can cover at most 92 days.
usage.getCurrent({ org_id }) returns the organization’s current counts of users, workspaces, and projects.
New exported types: GetUsageRequest, GetUsageResponse, GetUsageCurrentRequest, and GetUsageCurrentResponse.
A fine-grained control plane key needs two things to call the usage endpoints: it must be rooted at the organization, and it must hold the org.analytics.query permission. That permission can now be granted when you create a key.
New client.charts.query() and query_async() aggregate a project’s events into a single chart result. They take a ChartQueryRequest (date range, filters including an event_type filter, and the query) and a required keyword-only project_id, and they return a ChartQueryResponse. Both models can be imported from honeyhive.models. The call is sent with the data plane API key, which needs project.event.get.
Project-scoped dataset, datapoint, and experiment run operations
Every client.datasets, client.datapoints, and client.experiments run method (list, get, create, update, delete, add/remove datapoints, schema, results, comparisons, and their _async variants and aliases) accepts an optional keyword-only project_id. With project_id, the call targets that project and is sent with the data plane API key (data_plane_api_key / HH_DATA_PLANE_API_KEY).
datapoints.create_batch()
New client.datapoints.create_batch() and create_batch_async() create datapoints from events, optionally adding them to a dataset.
experiments.get_run_metrics()
New client.experiments.get_run_metrics() and get_run_metrics_async() return the event metrics of an experiment run, with optional dateRange and filters.
Dataset, datapoint, and experiment run calls without project_id
Calling these methods without project_id still uses the project API key, but now emits a DeprecationWarning. These calls stop working in v2.0. Pass project_id and configure a data plane API key. evaluate() and run_experiment() do not emit this warning.
The Claude Agent SDK extras (openinference-claude-agent-sdk, all-openinference) now require claude-agent-sdk>=0.2.121. The aws-strands extra now requires strands-agents>=1.48.0. If you pin either package below these versions, raise the pin.
Added --data-plane-api-key and HH_DATA_PLANE_API_KEY for fine-grained data plane API keys (hh_fgdp_...). Create one in the HoneyHive app under project, workspace, or organization Settings → API keys, on the Data Plane tab.
Added --project-id to charts list, charts create, charts get, and charts update. With --project-id, the command calls the project-scoped route and authenticates with the data plane API key. Pass --project-id even when the key is rooted at that project. Without it, the command calls the legacy route with the project API key, even when a data plane key is configured. A project API key cannot stand in for a data plane key on the scoped route.
Added data-plane-api-keys create, which creates a data plane API key. It takes --project-id, --name, --permissions (a JSON array such as ["project.chart.get"]), --expires-at (ISO 8601), and an optional --description.
Added ingestion-api-keys create, which creates an ingestion API key. It takes --project-id, --name, and an optional --description.
charts create and charts update now accept five_minute and auto for --bucketing.
An error for a missing API key now names the key the command needs and the flag and environment variable that supply it. For data plane and control plane keys, the error also says where in the app to create one. An error for a malformed key names the flag or environment variable the bad value came from.
--verbose on data plane commands now logs all three data plane keys (project, ingestion, and data plane), and prints (none) for any that are unset. A masked ingestion or fine-grained key shows its prefix and key id with the secret replaced by ******, which matches the masked form in the HoneyHive app.
Calling charts commands without --project-id is deprecated. --project-id becomes required in the next major version. Pass it with a data plane API key.
The CLI now checks every typed key you have configured (ingestion, data plane, control plane) when a command starts, including keys that command does not use. A key of the wrong type or an incomplete key in HH_INGESTION_API_KEY, HH_DATA_PLANE_API_KEY, or the matching flag now fails the command before it sends a request. The error names the flag or environment variable that holds the bad value. Fix or unset the stale value.
The CLI no longer checks the format of the project API key (--project-api-key / HH_PROJECT_API_KEY) locally. It sends the value as given, and the server decides whether to accept it.
HoneyHive(...) accepts data_plane_api_key=, also read from HH_DATA_PLANE_API_KEY, for fine-grained hh_fgdp_ keys. It is exposed as client.data_plane_api_key. A value that is set but is not a complete hh_fgdp_ key raises ValueError at construction.
Project-scoped chart operations
Every client.charts method (list, get, create, update, delete and their _async variants) accepts an optional keyword-only project_id. With project_id, the call targets that project and is sent with the data plane API key. It never falls back to api_key.
MissingApiKeyError
New exception, importable from honeyhive. It subclasses ValueError and has a key_kind attribute. An operation raises it before sending a request when no key it accepts is configured, and the message names the key and environment variable to set.
HTTPException raised by API calls now includes the server’s error message, and a 404 adds a hint that the resource does not exist or the key lacks access to it. New server_message and error_code attributes expose the parsed error body. The exception type, status_code, and the message prefix are unchanged, so existing handlers keep working.
Missing keys fail before the request is sent
An operation called with no usable API key now raises MissingApiKeyError locally, instead of sending an unauthenticated request and failing at the API.
events.update_event() accepts a model
update_event() now accepts an UpdateEventRequest as well as a dict. A dict is validated into the model.
Calling a client.charts method without project_id still uses the project API key, but now emits a DeprecationWarning. These calls stop working in v2.0. Pass project_id and configure a data plane API key.
Unused error-handling helpers in honeyhive.utils
ErrorHandler, get_error_handler, handle_api_errors, ErrorContext, ValidationError, HoneyHiveConnectionError, RateLimitError, and AuthenticationError can still be imported from honeyhive.utils and honeyhive.utils.error_handler, but importing them now emits a DeprecationWarning.
New dataPlaneApiKey client option and HH_DATA_PLANE_API_KEY environment variable for a fine-grained data plane API key (a value beginning with hh_fgdp_). The client sends this key on operations that are scoped to a project, which today are the chart operations.
Every client.charts method (list, create, get, update, delete) now accepts an optional project_id. With project_id, the call goes to the project-scoped chart endpoints and authenticates with the data plane API key. client.charts.list() now takes an optional request argument, a new GetChartsRequest type.
New client.dataPlaneApiKeys.create() creates a fine-grained data plane API key rooted at a project. New client.ingestionApiKeys.create() creates an ingestion API key for a project. Both return key_value once, and you cannot get it again.
New exported request and response types: CreateDataPlaneApiKeyRequest/Response and CreateIngestionApiKeyRequest/Response.
New exported error classes MissingApiKeyError and MalformedApiKeyError, both subclasses of HoneyHiveError, and the ApiKeyKind type. Each error has a keyKind property ('project', 'ingestion' or 'dataPlane'). MalformedApiKeyError also has a source property that names the option or environment variable the bad value came from.
The chart bucketing option now accepts five_minute and auto.
Verbose logging now prints a Data plane API key: line. The line shows hh_fgdp_, the key id and none of the secret, and shows (none) when no key is set.
An operation whose required key is not configured now throws a MissingApiKeyError before it sends a request. The error names the key that the operation needs. Previously, some operations sent the request without a key and failed with a 401.
An ingestionApiKey, HH_INGESTION_API_KEY, dataPlaneApiKey or HH_DATA_PLANE_API_KEY value that is not a well-formed key of that kind now throws a MalformedApiKeyError when the client is constructed. The error never echoes the value.
Calling a client.charts method without project_id is deprecated. It becomes required in the next major version. These calls still use the legacy route and the project API key. Each method logs a one-time deprecation warning to stderr. To migrate, pass project_id and configure a data plane API key.
A call with project_id needs a data plane API key and never falls back to the project API key. A data plane API key cannot send traces, so keep an ingestion key or a project key for ingestion.
A client with no API key configured now constructs successfully. The first operation that needs a key throws a MissingApiKeyError. Previously, the constructor threw a Missing API key error. If your code catches the missing-key error at construction, also handle it at the first call.
A client that has custom middleware or its own Authorization header never throws MissingApiKeyError. Authentication is left to the middleware or the header.
New exported error classes MissingApiKeyError and MalformedApiKeyError. Both extend HoneyHiveError.
Client construction now throws MissingApiKeyError when no API key and no middleware are configured, and MalformedApiKeyError when apiKey or HH_CONTROL_PLANE_API_KEY is not a well-formed fine-grained control plane key. Before this release, both cases threw a plain Error. You can now catch these with instanceof checks. MalformedApiKeyError.source names the option or environment variable that held the bad value. The error never echoes the key.
Added --ingestion-api-key and HH_INGESTION_API_KEY for the commands that send traces and events: sessions create, sessions create-event-batch, events create, events update, and events create-batch. These take an ingestion API key (hh_ingst_...), created at project scope on the API Keys page under Settings. They still accept a project API key, so a shell that exports only HH_PROJECT_API_KEY keeps working. Give ingestion its own key in a new setup. Every other command is unchanged.
Added --slug to workspaces create and virtual-dataplanes create. A slug takes letters, digits, and underscores only, and must be globally unique. Omit it and the server derives a slug from the name with a random suffix, exactly as before. Supply it when the identifier has to match a value maintained outside HoneyHive, such as an identity provider group that grants access to the scope. A slug already in use fails with a 409 naming the slug.
API keys are now trimmed before the CLI checks their shape. A key pasted with surrounding whitespace or a trailing newline is no longer rejected locally when the API would have accepted it.
The pre-flight key check now judges the exact credential each command sends, rather than assuming one key per API. Its error names the key and the flag or environment variable that command needs.
metrics run no longer fails with a 400 when the metric reads ground_truth and the event carries none. The command now exits 0 and prints success: false with result: null and the reason in explanation. A script that read the non-zero exit code as the signal must read success in the printed JSON instead.
Every command now gets a 404 where the API used to answer a permission failure with 403 or a rejected API key with 401. A rejected key is one that is invalid, revoked, or expired. The response does not say which case applies, and the error no longer names the check that refused. A script that branched on 401 or 403 must read the not-found error as covering all three cases. These are server changes, so they apply to every CLI version.
--needs-ground-truth and --no-needs-ground-truth on metrics create and metrics update are deprecated and ignored, and now log a deprecation warning to stderr. The server infers ground-truth use from the metric definition. A Python metric that reads ground_truth needs it, and so does an LLM metric whose template references it. The same field inside a metric-versions create --content payload, and inside any --filename payload, is likewise accepted and ignored. The flags will be removed in the next major version. Stop passing them.
workspace_id inside the --event payload of metrics run is deprecated and ignored. The workspace whose provider credentials run the metric is derived from the authenticated key’s scope, so naming a different workspace has no effect.
The bundled YAML parser used for --filename input now bounds recursive merge aliases. A YAML file with deeply chained merge keys no longer expands unchecked.
New ingestionApiKey client option and HH_INGESTION_API_KEY environment variable for supplying an ingestion API key (a value beginning with hh_ingst_). The client sends it on the operations that send traces and events: client.sessions.create(), client.sessions.createEventBatch(), client.events.create(), client.events.update(), and client.events.createBatch(). The client sends the project API key on everything else.
An ingestion API key is now a credential on its own. A process that only sends traces and events can construct a client with the ingestion key and no project API key. Every other operation on such a client behaves as it does with no project API key configured.
An ingestionApiKey or HH_INGESTION_API_KEY value that is not a well-formed ingestion key throws when the client is constructed. The error names the option or variable the value came from and never echoes the value.
Verbose logging now prints an Ingestion API key: line next to the project key. The line shows hh_ingst_, the key id, and none of the secret. This is the form HoneyHive displays, so you can match the line against the key in your account. A credential that is not configured logs as (none).
client.metrics.run() now derives the workspace whose provider credentials run the metric from the caller’s authenticated scope rather than from the request body. Previously a workspace_id in the request event was forwarded as-is. That let an authenticated caller run an ad-hoc LLM metric against another tenant’s configured provider credentials.
A metric run that needs ground truth the event does not carry is now skipped instead of failed. client.metrics.run() returns 200 with success: false, a null result, and an explanation. It previously threw a 400 with ground_truth_missing. Adding ground truth to the event re-enqueues it, and the metric computes on that pass.
The error thrown when no credential is configured now reads Missing API key and names the four current sources: projectApiKey, ingestionApiKey, HH_PROJECT_API_KEY, and HH_INGESTION_API_KEY.
A client configured with a project API key and no ingestion key still sends the project key on the ingestion operations.
HH_INGESTION_API_KEY is read at client construction for the first time in this release. A process that already sets it to a non-empty value that is not a well-formed ingestion key now fails to construct a client. Unset the variable, or set it to your ingestion key, before upgrading.
needs_ground_truth is deprecated and ignored on every metric request and response type, including CreateMetricRequest, UpdateMetricRequest, MetricItem, MetricVersionContent, and the metric passed to client.metrics.run(). The API now infers the need for ground truth from the metric definition. The property remains in the type definitions so existing code keeps compiling, and reads still return the stored value.
workspace_id on the event passed to client.metrics.run() is deprecated and ignored. It remains accepted, so existing code keeps compiling. Remove it and rely on the key’s scope.
Scope creation accepts a caller-supplied identifier: an optional slug on CreateWorkspaceRequest and CreateVirtualDataplaneRequest. Omit it and the server derives one from name with a random suffix, as before. Supply it when the identifier has to match a value maintained outside HoneyHive, such as an identity provider group that grants access to the scope. A slug already in use gets a 409 that names it.
The client validates the API key at construction. A value in apiKey or HH_CONTROL_PLANE_API_KEY that is not a well-formed fine-grained control plane key (hh_fgcp_…) throws when you build the client. The error names the option or the environment variable the value came from, and it does not echo the key. Before this release the same value failed on the first request as an authentication error.
Every control plane endpoint answers an authorization failure with 404 instead of 403. The status no longer reveals whether a resource you may not touch exists. The control plane does not emit 403 at all any more.
An authentication failure on a fine-grained control plane key also answers 404 instead of 401. This covers a missing Authorization header and a key that is invalid, revoked, or expired.
A 404 now means “not found”, “not permitted”, or “not authenticated”, and the response does not say which. Code that refreshes a credential on 401 or branches on 403 no longer sees either status.
A 404 body carries the fixed message"Not found" and the fixed errorCode"not_found". ApiError.message and parseError() no longer name which check refused. The server logs carry that detail.
The three status changes above are control plane changes, so they apply to every SDK version, not only 1.2.0.
The axios runtime dependency moves from 1.19.0 to 1.20.0.
HoneyHive(...), HoneyHiveTracer.init(...), and evaluate(...) accept ingestion_api_key=, also read from HH_INGESTION_API_KEY. The ingestion key is sent only on the routes that create sessions and write events, including OTLP trace export; every other route keeps using api_key. When it is unset, api_key is used for ingestion too, so existing setups are unchanged. A value that is set but is not an hh_ingst_ key raises ValueError at construction rather than failing later at request time.
Connection settings resolve the same way in every entry point
The client, the tracer, evaluate(), and the CLI now resolve api_key, ingestion_api_key, and the API URL through one resolver, so a single process can no longer authenticate with one key in evaluate() and another in a tracer. An explicit argument beats the environment, and a blank value counts as unset.
HH_API_KEY and HH_API_URL are the preferred variables and win when more than one is set. HONEYHIVE_API_KEY, HONEYHIVE_SERVER_URL, and HH_SERVER_URL still work but are deprecated and emit a DeprecationWarning once per process. If you set both HONEYHIVE_API_KEY and HH_API_KEY to different values, evaluate() and the CLI now use HH_API_KEY; they previously preferred HONEYHIVE_API_KEY.
Tracer: RecursionError from trace.get_tracer() after tracer shutdown
Shutting down the main HoneyHiveTracer reset the global OpenTelemetry provider to a fresh ProxyTracerProvider, which delegates back to the global provider — itself. Any subsequent opentelemetry.trace.get_tracer(...) call (including module-level calls made when importing libraries such as mcp) raised RecursionError. Shutdown now clears the global provider so OpenTelemetry returns its own proxy.
Events API: retry config now controls events.export()
HoneyHive(retry_config=...) and HH_MAX_RETRIES were accepted and ignored. They did not reach events.export() / export_async(), which fell back to a fixed budget of 3 retries. Both configuration methods now set the retry budget for events.export() / export_async().
honeyhive[openinference-anthropic], honeyhive[traceloop-anthropic], and the bundles that include them now require anthropic<1.0.0, and the OpenInference extras also require openinference-instrumentation-anthropic<2.0.0. A mismatched pair either raised ModuleNotFoundError on import or logged a dependency conflict and skipped instrumentation. The SDK already implicitly depended on anthropic<1.0.0 as that was the only version available, which worked until Anthropic recently published v1.0 of their SDK.
LangChain extras now install langchain-openai
honeyhive[openinference-langchain] and honeyhive[traceloop-langchain] pull in langchain-openai>=1.0.0 alongside langchain. These dependencies were always required for tracing to work correctly with LangChain but they weren’t declared before.
Added workspaces subcommands for managing workspaces: workspaces create, workspaces get, workspaces update, and workspaces delete. workspaces create takes --virtual-dataplane-id and --name, plus optional --description and --workspace-creator (the email of the user to grant workspace-creator membership to, for API key callers).
Added virtual-dataplanes subcommands for managing virtual data planes: virtual-dataplanes create, virtual-dataplanes get, virtual-dataplanes update, and virtual-dataplanes delete. virtual-dataplanes create takes --org-id and --name, plus optional --cluster-id (required when the organization has no virtual data planes yet, or its virtual data planes span more than one cluster) and --dataplane-creator.
workspaces delete and virtual-dataplanes delete accept --dangerously-delete-child-scopes to archive a scope that still has active children, archiving those children too. Without the flag, deleting a workspace with active projects — or a virtual data plane with active workspaces — fails with a 409 and changes nothing.
Like projects and alerts, both new command groups talk to the HoneyHive control plane: they take a fine-grained control plane API key (hh_fgcp_...) via --control-plane-api-key / HH_CONTROL_PLANE_API_KEY and honor --control-plane-url / HH_CONTROL_PLANE_URL. Data plane commands are unchanged and still use --project-api-key / HH_PROJECT_API_KEY.
client.metrics.run() now distinguishes failure classes: 400 when the request is invalid or is missing inputs the metric needs (e.g. ground truth the event doesn’t carry), 422 when evaluation itself failed (execution_error, compilation_error, template_render_error, llm_response_parse_error, …), and 500 for internal errors. ApiError.parseError() returns the errorCode along with the evaluator’s detailed failure text in message.
Empty or whitespace-only name values passed to client.experiments.createRun() and client.experiments.updateRun() are now rejected with a 400, and accepted names are trimmed before storage. Such names previously created runs with a blank display name.
Composite metrics are no longer supported. child_metrics is marked deprecated on every metric request and response type and is now ignored by the API, and creating or updating a metric with type: 'COMPOSITE' returns a 400. Both remain in the type definitions so existing code keeps compiling; replace composite metrics with standalone metrics.
Workspace management via the new workspaces namespace: create(), get(), update(), and delete(), with matching CreateWorkspaceRequest/Response, GetWorkspaceRequest/Response, UpdateWorkspaceRequest/Response, and DeleteWorkspaceRequest/Response types.
Virtual data plane management via the new virtualDataplanes namespace: create(), get(), update(), and delete(), with matching CreateVirtualDataplaneRequest/Response, GetVirtualDataplaneRequest/Response, UpdateVirtualDataplaneRequest/Response, and DeleteVirtualDataplaneRequest/Response types.
Deleting a workspace or virtual data plane archives everything inside it, so a resource with active children is rejected with a 409 unless the request sets dangerously_delete_child_scopes: true.
Creating a project with project_creator now marks that user’s sessions for refresh, so they no longer have to sign out and back in to see the new project. The change is in the control plane, so it applies to any SDK version. The refresh takes effect on their next request to the control plane, so an idle browser tab may still need a page reload.
Fixed the Homebrew formula published for each release so brew install honeyhive and brew upgrade honeyhive resolve the current CLI version. The formula no longer declares an explicit version that recent Homebrew versions reject as redundant during validation.
Installing a stable release via Homebrew now requires Homebrew 6.0.14 or newer, which reads the version from the release URL. An older client that installs without updating first — most commonly HOMEBREW_NO_AUTO_UPDATE=1 in a Docker or CI image — records the wrong version and will not report the install as outdated afterwards. Run brew update before installing, or reinstall once Homebrew is current. npm and npx installs are unaffected.
Added projects subcommands for managing projects: projects create, projects get, projects update, and projects delete.
Added alerts subcommands: alerts list, alerts create, and alerts get.
Added --control-plane-api-key / HH_CONTROL_PLANE_API_KEY and --control-plane-url / HH_CONTROL_PLANE_URL. The new projects and alerts commands talk to the HoneyHive control plane, which takes a fine-grained control plane API key (hh_fgcp_...) created at workspace or organization scope — not a project API key. Every other command is unchanged and still uses --project-api-key / HH_PROJECT_API_KEY; you only need the key for the commands you actually run.
The CLI now checks what kind of API key you supplied before sending a request. Passing a control plane key to a data plane command (or the reverse) fails immediately with a message naming the key kind the command needs and the flag or environment variable the wrong key came from, instead of an unexplained 401 from the server.
--verbose now reports the URL and key for whichever API the command talks to (Control plane URL: for projects and alerts), and masks a fine-grained control plane key exactly as the HoneyHive app displays it, so you can match a log line to a key in your account.
Composite metrics are no longer supported. metrics create and metrics update now fail with a 400 when passed --type COMPOSITE, existing composite metrics and their versions have been deleted, and --child-metrics no longer has any effect on a metric’s score.
First stable release of the HoneyHive TypeScript Control Plane SDK (@honeyhive/control-plane-sdk). Functionally identical to 1.0.0-rc.1 — no code changes, so upgrading from the release candidate requires no migration.
Tracing: root SERVER spans get an automatic session_id
When HoneyHiveTracer is initialized with skip_backend_session_creation=True and no session_id, the SDK now generates a session_id for each root SERVER span (SpanKind.SERVER, no parent) and propagates it via baggage. Previously these root spans were dropped and their children appeared orphaned in the Sessions tab — common with FastAPIInstrumentor, where the root span is created before any middleware runs. Existing single-session and client-span behavior is unchanged.
Experiments: evaluate() normalizes run names
evaluate(name=...) now trims surrounding whitespace, and a blank or whitespace-only name falls back to the default experiment-<id> name instead of being submitted as-is. The backend also now rejects empty run names.
Composite metrics removed from the metric API surface
The child_metrics field and the generated *ChildMetricsItem models have been removed from the metric request/response models in honeyhive.models, completing the composite-metrics deprecation. Composite metrics are no longer part of the product, so any remaining code referencing them can be safely cleaned up and removed.
Existing code that passes child_metrics=... keeps working — metric models accept unknown fields (extra="allow"), so the value is still accepted without error. The API already ignored and dropped child_metrics, so this change aligns the client with the backend behavior. The only difference: child_metrics is no longer a declared field, so reading metric.child_metrics off a response now raises AttributeError instead of returning None.
New client.events.get({ event_id }) method for GET /v1/events/{event_id}, fetching a single event by ID.
All SDK methods now accept an optional options?: FetchOptions second argument for cancelling in-flight requests via AbortController. New exported FetchOptions type ({ signal?: AbortSignal }). Example: await client.events.search(request, { signal: controller.signal }).
New projectApiKey client option and HH_PROJECT_API_KEY environment variable for supplying the project-scoped API key. These replace the previous apiKey option and HH_API_KEY environment variable.
Experiment run objects now include a dataset_name field alongside dataset_id.
The outputs field on update-event requests is now typed as an object or null (previously unknown). Passing a non-object value (array, string, or scalar) previously corrupted the stored event and broke downstream consumers such as the Python SDK; such values are now rejected. Passing null preserves the existing outputs.
The apiKey client option and HH_API_KEY environment variable are deprecated and will be removed in the next major version. They continue to work but log a one-time deprecation warning to stderr on client construction. Migrate to projectApiKey / HH_PROJECT_API_KEY.
Added events get --event-id <id> to fetch a single event by its ID.
Added the --project-api-key flag and HH_PROJECT_API_KEY environment variable as the primary way to authenticate the CLI. Verbose output now labels the key as Project API key: (previously API Key:).
experiments list-runs, experiments get-run, and experiments get-summary output now includes the linked dataset_name, and get-summary now reports dataset_id for offline (EXT-*) datasets that previously returned null.
events update --outputs now requires an object or null (where null preserves the existing outputs); non-object values (strings, arrays, scalars) are rejected up front. These values were previously accepted but corrupted the stored event, which could break other consumers reading it back.
The --api-key flag and HH_API_KEY environment variable are now deprecated aliases for --project-api-key / HH_PROJECT_API_KEY. They still work but log a deprecation warning to stderr and will be removed in the next major version. Migrate to the new names.
New honeyhive.adapters.copilot_studio.copilot_studio_records_to_spans() converts Azure Application Insights diagnostic-export records from a Copilot Studio agent into OpenTelemetry spans you can forward to HoneyHive via OTLP. Maps user/agent messages, tool calls, and errors using GenAI semantic conventions, preserves parent/child trace correlation, and groups a conversation into a single HoneyHive session. Set COPILOT_STUDIO_ADAPTER_KEEP_TOPIC_SPANS=1 to retain internal orchestration spans (dropped by default) and COPILOT_STUDIO_ADAPTER_DEBUG=1 to attach the raw source record for debugging.
dataset_name on experiment runs
ExperimentRunObject now includes a dataset_name field alongside dataset_id, so the linked dataset’s name is available when listing or fetching experiment runs. The field is optional and resolves to None for offline (EXT-*), deleted, or unset datasets.
UpdateEventRequest.outputs and LegacyUpdateEventRequest.outputs are now typed as Dict[str, Any] instead of Any. Update calls accept only normalized (object-shaped) outputs; previously an array could slip through and corrupt the stored event. If you were passing a list when updating an event, wrap it as {"value": [...]}. Event creation and OTEL ingestion continue to normalize arrays automatically.
HoneyHive(timeout=...) now sets the per-request timeout, in seconds, for standard API client calls. You can also set HH_API_TIMEOUT; the explicit timeout= argument takes precedence. The default remains 5 seconds, so existing clients keep the same timeout unless you opt in.
Event lookup by ID
The generated sync and async event services now include GET /v1/events/{event_id} as getEvent(..., event_id=...), returning GetEventResponse / GetEventResponseEvent models exported from honeyhive.models.
Custom tracer export HTTP sessions
HoneyHiveTracer.init(requests_session=...) and TracerConfig(requests_session=...) now accept a caller-owned requests.Session for OTLP span export. Use this when you need custom proxies, retries, TLS settings, or connection pooling. The SDK does not close a session that you provide.
Self-host: GET /v1/events/{event_id} requires backend v1.3.0+
Self-hosted deployments must run a HoneyHive backend on version 1.3.0 or greater before calling GET /v1/events/{event_id}. Older self-host backends do not expose this route, so event-by-id calls will fail until the backend is upgraded. HoneyHive Cloud users are unaffected.
New client.metric_versions.list(metric_id), .create(metric_id, request), and .deploy(metric_id, version_name) methods (plus *_async variants) for managing a metric’s immutable version history, backed by /v1/metrics/{metric_id}/versions. Each metric keeps a history of versions with one deployed at a time; pass deploy_immediately=True on create to atomically deploy the new version. Version request/response models are exported from honeyhive.models.
OTLP JSON exporter: serialize numeric fields as JSON strings
Integer span attributes (intValue) and uint64 timestamp fields
(startTimeUnixNano, endTimeUnixNano, timeUnixNano) were emitted as
raw JSON numbers. The protobuf JSON mapping spec requires these to be JSON
strings; values above 2^53 could otherwise lose precision silently through
float64 rounding. Now matches native opentelemetry-exporter-otlp-proto-http
behavior.
Decorator input capture: remove truncation of serialized list/dict values
List and dict span attributes are serialized to strings and
previously were truncated to 1000 characters in length. The arbitrary limit
of 1000 characters has been removed.
New client.metricVersions namespace with list, create, and deploy methods for managing snapshot versions of a metric’s definition via /v1/metrics/{metric_id}/versions and /v1/metrics/{metric_id}/versions/{version_name}/deploy.
New exported request/response types: GetMetricVersionsRequest/Response, CreateMetricVersionRequest/Response, and DeployMetricVersionRequest/Response.
Added metric-versions subcommands for managing snapshot versions of a metric’s definition: metric-versions list, metric-versions create, and metric-versions deploy. Use these to review history, create new draft or immediately-deployed versions, and roll between versions without losing history.
Added a dataPlaneUrl client option and HH_DATA_PLANE_URL environment variable for configuring the data plane URL. These replace the previous serverUrl option and HH_API_URL environment variable.
The verbose logging output now labels the resolved URL as Data plane URL: (previously API URL:).
Environment variables set to the empty string (e.g. HH_API_KEY=, HH_DATA_PLANE_URL=) are now treated as unset and fall back to defaults, rather than being propagated as a literal empty string.
The serverUrl client option and HH_API_URL environment variable are deprecated and will be removed in the next major version. They continue to work but log a one-time deprecation warning to stderr on client construction. Migrate to dataPlaneUrl / HH_DATA_PLANE_URL.
Added --data-plane-url flag and HH_DATA_PLANE_URL environment variable for pointing the CLI at a specific HoneyHive data plane (e.g. self-hosted or staging deployments). Verbose output now labels this value as Data plane URL: instead of API URL:.
The --base-url flag and HH_API_URL environment variable are deprecated and will be removed in the next major version. They still work as aliases for the new names but now log a deprecation warning to stderr on each invocation. Migrate to --data-plane-url / HH_DATA_PLANE_URL.
Error output for failed API requests now includes the server’s error message. All CLI commands that hit the API previously printed only API error <status>; they now print API error <status>: <message> with the actionable detail from the server.
New client.charts.create(), .list(), .get(chart_id), .update(chart_id, request), and .delete(chart_id) methods (plus *_async variants) backed by POST /v1/charts, GET /v1/charts, GET /v1/charts/{chart_id}, PUT /v1/charts/{chart_id}, and DELETE /v1/charts/{chart_id}. Chart request and response models are exported from honeyhive.models.
The CrewAI example (examples/integrations/crewai_integration.py) has been rewritten with three realistic customer-support scenarios (single-agent with tool calls, sequential two-agent crew, and @trace escalation). Crews are now named for clearer span labels, and teardown properly flushes remaining spans.
Tracer init: project / HH_PROJECT no longer required
HoneyHiveTracer.init() previously required project and treated a missing value as degraded mode. Since the backend determines the project from the API key, only api_key is actually needed. Callers that followed the v1.0.1 deprecation guidance and removed project were silently running in degraded mode — this is now fixed. project and HH_PROJECT remain accepted for backwards compatibility (deprecated, to be removed in v2.0).
Duplicate model spans in evaluate()
evaluate() could occasionally emit two model spans for a single LLM call when running multiple datapoints concurrently. The underlying race condition in instrumentation setup has been fixed.
cp_base_url= parameter and HH_CP_API_URL env var removed
These options were vestigial configuration for an incomplete Control Plane integration that was never wired up or shipped. No customer code could have depended on them. They have been removed to clean up the public API surface. If you happen to pass cp_base_url= or set HH_CP_API_URL, simply remove them — HH_API_URL / base_url= is the only base-URL setting.
New client.charts namespace with create, list, get, update, and delete methods for managing charts via /v1/charts and /v1/charts/{chart_id}.
New client.experiments.getSummary() method for GET /v1/runs/{run_id}/summary, returning pass/fail results, metric aggregations, per-datapoint results, and the experiment run object.
New client.sessions.createEventBatch() method for POST /v1/sessions/{session_id}/events/batch, accepting a batch of events scoped to a single session (the session_id from the path overrides any value in the event body).
New exported request/response types: CreateChartRequest/Response, GetChartRequest/Response, GetChartsResponse, UpdateChartRequest/Response, DeleteChartRequest/Response, GetExperimentSummaryRequest/Response, and CreateSessionEventBatchRequest/Response.
Experiments: client-side evaluator scores now flow end-to-end in evaluate()
Scores returned by evaluators in evaluate() are now written to the correct user-function chain span via enrich_span(metrics=…), exported over OTLP, and surfaced in server-side run comparison’s per-event metric_deltas. Previously, evaluator scores were duplicated into run.metadata.evaluator_metrics and onto the per-datapoint session event, but neither path actually populated the per-event surface that run comparison requires — client-side evaluator scoring and comparison did not work end-to-end.
The two legacy write paths have been removed now that the scores are correctly written to the chain span as the single source of truth.
API Client: event_id typed as required on POST /events responses
PostEventResponse.event_id was incorrectly typed as Optional[str] in the OpenAPI spec; the backend always returns the field on a 2xx response. The spec was corrected and the generated SDK now reflects the runtime guarantee, removing the need for defensive if event.event_id: guards. No runtime behavior change, but strictly typed mypy codebases with warn_unreachable = True or pyright with reportUnnecessaryComparison enabled may produce “unnecessary comparison” or “redundant Optional guard” warnings, depending on exact usage patterns.
ID = "id" was never a valid backend wire value and has always been rejected on the wire. Use "string" for UUID-shaped fields such as session_id. The alias is preserved so existing imports continue to resolve and will be removed in the next major.
honeyhive Python CLI entry point removed from pyproject.toml
The shipped Python honeyhive console script was non-functional (dead code) and shadowed the official TypeScript CLI on $PATH. CLI functionality is now provided by the official honeyhive TypeScript CLI; removing the Python script entry point lets honeyhive resolve correctly when both packages are installed globally.
Default API URL fallback corrected to https://api.dp1.us.honeyhive.ai
The fallback value for the base API URL in the v1.0.0 release was incorrect. Any callers who relied on the implicit default would fail. The default now points at the host the SDK is built against, though self-hosted / non-default Data Plane deployments will still need to set HH_API_URL explicitly. Callers that explicitly set base_url= / server_url= or HH_API_URL are unaffected.
project argument and HH_PROJECT environment variable
The SDK’s public surface has accepted project= on HoneyHive() and HoneyHiveTracer.init() and the HH_PROJECT env var for historical reasons. These arguments and env vars have no influence on the backend and they were maintained purely for backwards compatibility of callers. This release sweeps the stale references out of the example scripts, README, and docstrings, and adds a DeprecationWarning at the remaining call sites that previously didn’t emit one. The argument and the HH_PROJECT env var alias remain accepted for backwards compatibility and will be removed in v2.0.
First stable GA release of the HoneyHive Python SDK. Public APIs follow semver from this release forward — breaking changes only on major versions, additive on minor and patch.The changes below are relative to 1.0.0rc22. For the full picture of what shipped during the release-candidate cycle, see the 1.0.0rc1–1.0.0rc22 entries.
Tracing: skip_backend_session_creation=True now works without an explicit session_id
Previously the flag only opted out of the init-time backend roundtrip when a session_id was also supplied. Setting the flag alone is now supported: the SDK skips the roundtrip, does not mint a UUID, and per-request create_session(session_id=..., skip_api_call=True) calls emit spans that ingestion materializes into Session rows on first arrival.
Tracing: create_session(skip_api_call=True) propagates session_name into baggage
When session_name is supplied, it now travels with session_id across async / threaded boundaries.
Internal query-params type that was exported but unused — no public method accepted or returned it. The public client.metrics.delete(id=...) signature is unchanged.
Was this page helpful?
{"docsConfig":{"theme":"aspen","$schema":"https://mintlify.com/docs.json","name":"HoneyHive AI Docs","description":"AI observability and evaluation platform. Trace, evaluate, monitor, and improve AI agents and LLM applications.","colors":{"primary":"#A56311","light":"#E8A23D","dark":"#A56311"},"logo":{"light":"https://mintcdn.com/honeyhiveai/lWy7JAPdMsmL06l1/logo/light.svg?fit=max\u0026auto=format\u0026n=lWy7JAPdMsmL06l1\u0026q=85\u0026s=419c99bb22ce1e89ae8fb4a87c589528","dark":"https://mintcdn.com/honeyhiveai/lWy7JAPdMsmL06l1/logo/dark.svg?fit=max\u0026auto=format\u0026n=lWy7JAPdMsmL06l1\u0026q=85\u0026s=2cee835bc5438cd29005218a18e0168b"},"favicon":{"light":"https://mintcdn.com/honeyhiveai/W7aN2oGPMTbOQxZv/images/favicon.svg?fit=max\u0026auto=format\u0026n=W7aN2oGPMTbOQxZv\u0026q=85\u0026s=1c201da27b06120f1c630bf53ad1a831","dark":"https://mintcdn.com/honeyhiveai/W7aN2oGPMTbOQxZv/images/favicon-dark.svg?fit=max\u0026auto=format\u0026n=W7aN2oGPMTbOQxZv\u0026q=85\u0026s=01cb0bffc66454f0e19853e701832110"},"api":{"openapi":["output.yaml","https://raw.githubusercontent.com/honeyhiveai/honeyhive-openapi/main/data_plane_openapi.yaml","https://raw.githubusercontent.com/honeyhiveai/honeyhive-openapi/main/control_plane_openapi.yaml"],"mdx":{"auth":{"method":"key","name":"key"},"server":"https://api.dp1.us.honeyhive.ai"}},"appearance":{"default":"light"},"background":{"color":{"light":"#ffffff","dark":"#101010"}},"navbar":{"links":[{"href":"https://discord.gg/vqctGpqA97","label":"Community"}],"primary":{"type":"button","label":"Log In","href":"https://app.us.honeyhive.ai"}},"footer":{"socials":{"x":"https://x.com/honeyhiveai","linkedin":"https://www.linkedin.com/company/honeyhive-ai","github":"https://github.com/honeyhiveai"},"links":[{"header":"Resources","items":[{"label":"Quickstart","href":"https://docs.honeyhive.ai/v2/introduction/tracing-quickstart"},{"label":"SDK Reference","href":"https://docs.honeyhive.ai/v2/sdk-reference/overview"},{"label":"API Reference","href":"https://docs.honeyhive.ai/v2/api-reference-autogen/sessions/start-a-new-session"},{"label":"Integrations","href":"https://docs.honeyhive.ai/v2/integrations/openai"},{"label":"Changelog","href":"https://docs.honeyhive.ai/v2/changelog/product"}]},{"header":"Explore","items":[{"label":"Sign Up","href":"https://app.us.honeyhive.ai"},{"label":"Pricing","href":"https://www.honeyhive.ai/pricing"},{"label":"Blog","href":"https://honeyhive.ai/blog"}]},{"header":"Platform","items":[{"label":"Observability","href":"https://honeyhive.ai/observability"},{"label":"Evaluation","href":"https://honeyhive.ai/evaluation"},{"label":"Prompt Management","href":"https://honeyhive.ai/playground"}]},{"header":"Legal","items":[{"label":"Terms of Service","href":"https://honeyhive.ai/terms"},{"label":"Privacy Policy","href":"https://honeyhive.ai/privacy"}]}]},"search":{"prompt":"Search or ask AI..."},"seo":{"metatags":{"og:site_name":"HoneyHive AI Docs"},"indexing":"navigable"},"integrations":{"posthog":{"apiKey":"phc_CtmMXHNhIs1Iem8viAsXYH7lB0aaj4X2nCrCunPqDbF","sessionRecording":false},"telemetry":{"enabled":true}},"contextual":{"options":["copy","view","chatgpt","claude","mcp","cursor","vscode",{"title":"Join Discord","description":"Get help from the HoneyHive community","icon":"discord","href":"https://discord.gg/vqctGpqA97"}],"display":"header"},"styling":{"latex":false},"navigation":{"global":{},"pages":[]}},"deployment":{"deploymentMetadataValue":{"subdomain":"honeyhiveai","hasAudioNarration":false,"actualSubdomain":"honeyhiveai","customDomains":["docs.honeyhive.ai"],"gitSource":{"type":"github","owner":"honeyhiveai","repo":"honeyhive-ai-docs","deployBranch":"main","contentDirectory":"","isPrivate":false,"mountPath":"","label":""},"chroma":{"liveDeploymentHistoryId":"6ae8c397e4f1cb0cd68b6573","collectionId":"1d927e58-1a13-4963-a70b-ce2b99edc5de","schemaVersion":"v3","latestBuiltDeploymentHistoryId":"6ae8c397e4f1cb0cd68b6573"},"feedback":{"issues":false,"edits":false,"thumbs":true,"agentFeedback":true},"entitlements":{"AI_CHAT":{"status":"ENABLED"},"WEB_MCP":{"status":"ENABLED"},"NARRATION":{"status":"ENABLED"}},"buildId":"6ae8c397e4f1cb0cd68b6573","clientVersion":"0.0.4067","searchFilterCounts":[{"language":"default","tag":"v2","count":393},{"language":"default","tag":"SDK Reference","count":175},{"language":"default","tag":"Reference","count":172},{"language":"default","tag":"TypeScript API SDK","count":114},{"language":"default","tag":"Documentation","count":91},{"language":"default","tag":"API Reference","count":69},{"language":"default","tag":"Data Plane API","count":51},{"language":"default","tag":"TypeScript Control Plane SDK","count":50},{"language":"default","tag":"Experiments","count":40},{"language":"default","tag":"Integrations","count":34}]},"assistantConfig":{"deflection":{"enabled":true,"email":"support@honeyhive.ai"}},"basePath":"","hasChatPermissions":true,"hasWebMcpPermissions":true,"toggles":[{"name":"kill-isr","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-mintlify-slack","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"agent-integrations-settings","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"rbac-expansion","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-pr-grouping","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"custom-prefix","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"analytics-v7","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"user-flows-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"no-publish-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-onboarding","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"llms-txt-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving-live","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"stripTemperatureMiddleware","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"support-platform-pylon-zendesk","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"snapshot-serving","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"instant-rollback","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications-server","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"granular-notifications","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automations-fable","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fixed-ai-action-pricing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"automation-resolve-merge-conflict","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"scoped-publishing","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"mintql","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"page-level-analytics","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"editor-version-history-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"billing-page-v2","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"full-page-search","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workspace-projects","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"assistant-v5","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"fork-pr-previews","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"revalidation-jwt","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"vite-origin","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-sso-scim","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"hobby-abuse-scan","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"dashboard-inbox","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-signup","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"turbopuffer-dual-write","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"workos-enabled","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"approve-review-flow","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false},{"name":"sso-setup-deadline-banner","enabled":true,"variant":{"name":"disabled","enabled":false,"feature_enabled":true},"impressionData":false}],"googleFontsInlined":false},"customCss":[{"filePath":"style.css","content":".update-container .hh-update-entry:first-child>h2{margin-top:0}"}],"page":{"pagePath":"v2/changelog/clients","slug":"/v2/changelog/clients","pageMetadata":{"title":"Clients","description":"Browse release notes for the HoneyHive Python SDK, TypeScript API SDK, TypeScript Control Plane SDK, and CLI. Stay current with new features, bug fixes, and breaking API changes.","mode":"wide","icon":"cube","href":"/v2/changelog/clients"},"description":{"compiledSource":"\"use strict\";\nconst {jsx: _jsx} = arguments[0];\nconst {useMDXComponents: _provideComponents} = arguments[0];\nfunction _createMdxContent(props) {\n const _components = {\n p: \"p\",\n ..._provideComponents(),\n ...props.components\n };\n return _jsx(_components.p, {\n children: \"Browse release notes for the HoneyHive Python SDK, TypeScript API SDK, TypeScript Control Plane SDK, and CLI. Stay current with new features, bug fixes, and breaking API changes.\"\n });\n}\nfunction MDXContent(props = {}) {\n const {wrapper: MDXLayout} = {\n ..._provideComponents(),\n ...props.components\n };\n return MDXLayout ? _jsx(MDXLayout, {\n ...props,\n children: _jsx(_createMdxContent, {\n ...props\n })\n }) : _createMdxContent(props);\n}\nreturn {\n default: MDXContent\n};\n","frontmatter":{},"scope":{}},"pageType":"default","mdx":{"mdxSource":{"compiledSource":"\"use strict\";const{Fragment:_Fragment,jsx:_jsx,jsxs:_jsxs}=arguments[0];const{useMDXComponents:_provideComponents}=arguments[0];const FilterButton=({children,isActive=false,isFirst=false,isLast=false,onClick})=>{const base=`px-4 py-1.5 text-sm font-medium inline-flex items-center whitespace-nowrap transition-colors`;const active=`bg-primary-dark text-white`;const inactive=`bg-gray-950/5 dark:bg-white/5 text-gray-950/70 dark:text-white/70 hover:bg-gray-950/10 dark:hover:bg-white/10`;const radius=[isFirst\u0026\u0026`rounded-l-xl`,isLast\u0026\u0026`rounded-r-xl`].filter(Boolean).join(` `);return _jsx(`button`,{className:`${`mint-px-4 mint-py-1.5 mint-text-sm mint-font-medium mint-inline-flex mint-items-center mint-whitespace-nowrap mint-transition-colors`} ${radius} ${isActive?`bg-primary-dark mint-text-white`:`mint-bg-gray-950/5 dark:mint-bg-white/5 mint-text-gray-950/70 dark:mint-text-white/70 hover:mint-bg-gray-950/10 dark:hover:mint-bg-white/10`}`,onClick,children})};const applyFilter=filter=>{const containers=document.querySelectorAll(`.update-container`);for(const container of containers){const entries=container.querySelectorAll(`.hh-update-entry`);let firstVisible=null;for(const entry of entries){const matches=filter===`all`||entry.dataset.filter===filter;entry.style.display=matches?``:`none`;const h2=entry.querySelector(`h2`);if(h2)h2.style.marginTop=``;if(matches\u0026\u0026!firstVisible)firstVisible=entry}if(firstVisible){const h2=firstVisible.querySelector(`h2`);if(h2)h2.style.marginTop=`0`;container.style.display=``}else{container.style.display=`none`}}};const Filter=()=>{const DEFAULT_FILTER=`all`;const[isSkeletonHidden,setIsSkeletonHidden]=useState(false);const[activeFilter,setActiveFilter]=useState(DEFAULT_FILTER);useEffect(()=>{if(!isSkeletonHidden){const skeleton=document.querySelector(`.filter-skeleton`);if(skeleton)skeleton.style.display=`none`;setIsSkeletonHidden(true)}else{applyFilter(DEFAULT_FILTER)}},[isSkeletonHidden]);const onSelect=filter=>{setActiveFilter(filter);applyFilter(filter)};if(!isSkeletonHidden){return null}return _jsxs(`div`,{className:`mint-flex mint-items-center`,children:[_jsx(FilterButton,{isActive:activeFilter===`all`,isFirst:true,onClick:()=>onSelect(`all`),children:`All`}),_jsx(FilterButton,{isActive:activeFilter===`python-sdk`,onClick:()=>onSelect(`python-sdk`),children:`Python SDK`}),_jsx(FilterButton,{isActive:activeFilter===`ts-api-sdk`,onClick:()=>onSelect(`ts-api-sdk`),children:`TypeScript API SDK`}),_jsx(FilterButton,{isActive:activeFilter===`ts-cp-sdk`,onClick:()=>onSelect(`ts-cp-sdk`),children:`Control Plane SDK`}),_jsx(FilterButton,{isActive:activeFilter===`ts-cli`,isLast:true,onClick:()=>onSelect(`ts-cli`),children:`CLI`})]})};function _createMdxContent(props){const _components={a:`a`,code:`code`,li:`li`,p:`p`,strong:`strong`,ul:`ul`,..._provideComponents(),...props.components},{Heading,Update,_MdxComponentBoundary}=_components;if(!Heading)_missingMdxReference(`Heading`,true);if(!Update)_missingMdxReference(`Update`,true);if(!_MdxComponentBoundary)_missingMdxReference(`_MdxComponentBoundary`,true);return _jsxs(_Fragment,{children:[_jsxs(`div`,{className:`mint-overflow-x-auto`,style:{minHeight:`2.25rem`},children:[_jsxs(`div`,{className:`filter-skeleton mint-flex mint-items-center`,children:[_jsx(`button`,{disabled:true,className:`mint-px-4 mint-py-1.5 mint-text-sm mint-font-medium mint-inline-flex mint-items-center mint-whitespace-nowrap mint-rounded-l-xl bg-primary-dark mint-text-white mint-cursor-not-allowed`,children:`All`}),_jsx(`button`,{disabled:true,className:`mint-px-4 mint-py-1.5 mint-text-sm mint-font-medium mint-inline-flex mint-items-center mint-whitespace-nowrap mint-bg-gray-950/5 dark:mint-bg-white/5 mint-text-gray-950/70 dark:mint-text-white/70 mint-cursor-not-allowed`,children:`Python SDK`}),_jsx(`button`,{disabled:true,className:`mint-px-4 mint-py-1.5 mint-text-sm mint-font-medium mint-inline-flex mint-items-center mint-whitespace-nowrap mint-bg-gray-950/5 dark:mint-bg-white/5 mint-text-gray-950/70 dark:mint-text-white/70 mint-cursor-not-allowed`,children:`TypeScript API SDK`}),_jsx(`button`,{disabled:true,className:`mint-px-4 mint-py-1.5 mint-text-sm mint-font-medium mint-inline-flex mint-items-center mint-whitespace-nowrap mint-bg-gray-950/5 dark:mint-bg-white/5 mint-text-gray-950/70 dark:mint-text-white/70 mint-cursor-not-allowed`,children:`Control Plane SDK`}),_jsx(`button`,{disabled:true,className:`mint-px-4 mint-py-1.5 mint-text-sm mint-font-medium mint-inline-flex mint-items-center mint-whitespace-nowrap mint-rounded-r-xl mint-bg-gray-950/5 dark:mint-bg-white/5 mint-text-gray-950/70 dark:mint-text-white/70 mint-cursor-not-allowed`,children:`CLI`})]}),_jsx(_MdxComponentBoundary,{name:`Filter`,children:_jsx(Filter,{})})]}),`\n`,_jsxs(Update,{label:`October 30th, 2026`,id:`october-30th%2C-2026`,children:[_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-9-0`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.9.0`,children:`v1.9.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`usage get`}),` and `,_jsx(_components.code,{children:`usage current`}),` for organization usage. `,_jsx(_components.code,{children:`usage get`}),` takes `,_jsx(_components.code,{children:`--org-id`}),`, `,_jsx(_components.code,{children:`--start-date`}),`, and `,_jsx(_components.code,{children:`--end-date`}),` (`,_jsx(_components.code,{children:`YYYY-MM-DD`}),` in UTC, end exclusive, 1 to 92 days after the start), plus optional `,_jsx(_components.code,{children:`--group-by`}),` (`,_jsx(_components.code,{children:`workspace_id`}),` or `,_jsx(_components.code,{children:`project_id`}),`). `,_jsx(_components.code,{children:`usage current`}),` takes `,_jsx(_components.code,{children:`--org-id`}),`. Both use a control plane API key (`,_jsx(_components.code,{children:`--control-plane-api-key`}),` / `,_jsx(_components.code,{children:`HH_CONTROL_PLANE_API_KEY`}),`).`]}),`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`charts query`}),`, which runs a chart query over a project’s events. It takes `,_jsx(_components.code,{children:`--project-id`}),`, `,_jsx(_components.code,{children:`--date-range`}),`, and `,_jsx(_components.code,{children:`--query`}),`, plus optional `,_jsx(_components.code,{children:`--filters`}),` and `,_jsx(_components.code,{children:`--product-filters`}),`, and authenticates with a data plane API key.`]}),`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`--project-id`}),` to `,_jsx(_components.code,{children:`datasets list`}),`, `,_jsx(_components.code,{children:`create`}),`, `,_jsx(_components.code,{children:`update`}),`, `,_jsx(_components.code,{children:`delete`}),`, `,_jsx(_components.code,{children:`add-datapoints`}),`, and `,_jsx(_components.code,{children:`remove-datapoint`}),`. Also added it to `,_jsx(_components.code,{children:`datapoints list`}),`, `,_jsx(_components.code,{children:`create`}),`, `,_jsx(_components.code,{children:`create-batch`}),`, `,_jsx(_components.code,{children:`get`}),`, `,_jsx(_components.code,{children:`update`}),`, and `,_jsx(_components.code,{children:`delete`}),`, to every `,_jsx(_components.code,{children:`experiments`}),` command, and to `,_jsx(_components.code,{children:`events get`}),` and `,_jsx(_components.code,{children:`events search`}),`. With `,_jsx(_components.code,{children:`--project-id`}),`, the command calls the project-scoped route and authenticates with a data plane API key (`,_jsx(_components.code,{children:`--data-plane-api-key`}),` / `,_jsx(_components.code,{children:`HH_DATA_PLANE_API_KEY`}),`). Without it, the command calls the legacy route with the project API key, the same as before.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`datapoints create`}),` with more than one dataset in `,_jsx(_components.code,{children:`linked_datasets`}),` now fails with a `,_jsx(_components.code,{children:`400`}),` that explains the problem. It used to fail with a `,_jsx(_components.code,{children:`500`}),`. A datapoint can link to one dataset only.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Calling the commands listed above without `,_jsx(_components.code,{children:`--project-id`}),` is deprecated. `,_jsx(_components.code,{children:`--project-id`}),` becomes required in the next major version. Pass it with a data plane API key.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`--linked-event`}),` on `,_jsx(_components.code,{children:`datapoints update`}),` is deprecated and ignored, and now prints a deprecation warning. Set the linked event when you create the datapoint. The flag will be removed in the next major version.`]}),`\n`,_jsxs(_components.li,{children:[`The standalone binaries (Homebrew and direct downloads) now embed Node.js 24, which requires macOS 13.5 or newer. The Homebrew formula now requires macOS Ventura (13) or newer. `,_jsx(_components.code,{children:`npm`}),` and `,_jsx(_components.code,{children:`npx`}),` installs are unaffected.`]}),`\n`,_jsxs(_components.li,{children:[`Homebrew 6.0.0 and later load formulae only from taps or formulae you have trusted. Install with `,_jsx(_components.code,{children:`brew install honeyhiveai/tap/honeyhive`}),`, which trusts the formula. If `,_jsx(_components.code,{children:`brew upgrade`}),` stops upgrading the CLI, run `,_jsx(_components.code,{children:`brew trust --formula honeyhiveai/tap/honeyhive`}),` once. `,_jsx(_components.code,{children:`npm`}),`, `,_jsx(_components.code,{children:`npx`}),`, and install-script installs are unaffected.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-8-1`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.8.1`,children:`v1.8.1`})}),_jsx(Heading,{level:`4`,id:`changed`,children:`Changed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Clearer deprecation message for the events `,_jsx(_components.code,{children:`project`}),` parameter`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`When you pass the deprecated `,_jsx(_components.code,{children:`project`}),` argument to a `,_jsx(_components.code,{children:`client.events`}),` method, the `,_jsx(_components.code,{children:`DeprecationWarning`}),` now says that the project comes from the API key. Before, it said that the backend infers the project from `,_jsx(_components.code,{children:`session_id`}),` or the filters. The parameter still works and is still scheduled for removal in v2.0.`]}),`\n`]}),`\n`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-api-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-api-sdk`,children:`TypeScript API SDK`}),_jsx(Heading,{level:`3`,id:`v1-7-0`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-api-client/releases/tag/v1.7.0`,children:`v1.7.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-2`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`client.charts.query()`}),` method runs a chart query on a project and returns one aggregated result. `,_jsx(_components.code,{children:`query.kind`}),` selects time series, categorical, scalar, histogram, or table. It needs `,_jsx(_components.code,{children:`project_id`}),` and a data plane API key. New exported `,_jsx(_components.code,{children:`QueryChartRequest`}),` and `,_jsx(_components.code,{children:`QueryChartResponse`}),` types.`]}),`\n`,_jsxs(_components.li,{children:[`More methods now accept an optional `,_jsx(_components.code,{children:`project_id`}),`. With `,_jsx(_components.code,{children:`project_id`}),`, the call uses the project-scoped route and authenticates with the data plane API key (`,_jsx(_components.code,{children:`dataPlaneApiKey`}),` / `,_jsx(_components.code,{children:`HH_DATA_PLANE_API_KEY`}),`):`,`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`client.events`}),`: `,_jsx(_components.code,{children:`get`}),` and `,_jsx(_components.code,{children:`search`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`client.datasets`}),`: `,_jsx(_components.code,{children:`list`}),`, `,_jsx(_components.code,{children:`create`}),`, `,_jsx(_components.code,{children:`update`}),`, `,_jsx(_components.code,{children:`delete`}),`, `,_jsx(_components.code,{children:`addDatapoints`}),`, and `,_jsx(_components.code,{children:`removeDatapoint`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`client.datapoints`}),`: `,_jsx(_components.code,{children:`list`}),`, `,_jsx(_components.code,{children:`get`}),`, `,_jsx(_components.code,{children:`create`}),`, `,_jsx(_components.code,{children:`createBatch`}),`, `,_jsx(_components.code,{children:`update`}),`, and `,_jsx(_components.code,{children:`delete`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`client.experiments`}),`: `,_jsx(_components.code,{children:`listRuns`}),`, `,_jsx(_components.code,{children:`createRun`}),`, `,_jsx(_components.code,{children:`getRun`}),`, `,_jsx(_components.code,{children:`updateRun`}),`, `,_jsx(_components.code,{children:`deleteRun`}),`, `,_jsx(_components.code,{children:`getRunsSchema`}),`, `,_jsx(_components.code,{children:`getRunSchema`}),`, `,_jsx(_components.code,{children:`getSummary`}),`, `,_jsx(_components.code,{children:`getRunMetrics`}),`, `,_jsx(_components.code,{children:`compareRuns`}),`, and `,_jsx(_components.code,{children:`compareRunEvents`}),`.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-2`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`client.datapoints.create()`}),` now rejects a `,_jsx(_components.code,{children:`linked_datasets`}),` array with more than one dataset ID and returns a `,_jsx(_components.code,{children:`400`}),`.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-2`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Calling any of the methods listed above without `,_jsx(_components.code,{children:`project_id`}),` is deprecated. It becomes required in the next major version. These calls still use the legacy route and the project API key, and each method logs a one-time deprecation warning to stderr. To migrate, pass `,_jsx(_components.code,{children:`project_id`}),` and configure a data plane API key.`]}),`\n`,_jsxs(_components.li,{children:[`A call with `,_jsx(_components.code,{children:`project_id`}),` needs a data plane API key and never falls back to the project API key.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`linked_event`}),` is deprecated and ignored on `,_jsx(_components.code,{children:`client.datapoints.update()`}),`. Set `,_jsx(_components.code,{children:`linked_event`}),` when you create the datapoint. The property stays in the type definitions, so existing code keeps compiling.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-cp-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-control-plane-sdk`,children:`TypeScript Control Plane SDK`}),_jsx(Heading,{level:`3`,id:`v1-4-0`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-control-plane-sdk/releases/tag/v1.4.0`,children:`v1.4.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-3`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Organization usage reporting through the new `,_jsx(_components.code,{children:`usage`}),` namespace. `,_jsx(_components.code,{children:`usage.get({ org_id, start_date, end_date, group_by? })`}),` returns event counts and computed-metric counts in one bucket per calendar month. You can split the results by `,_jsx(_components.code,{children:`workspace_id`}),` or by `,_jsx(_components.code,{children:`project_id`}),`. The range starts on `,_jsx(_components.code,{children:`start_date`}),` and ends before `,_jsx(_components.code,{children:`end_date`}),`, and it can cover at most 92 days.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`usage.getCurrent({ org_id })`}),` returns the organization’s current counts of users, workspaces, and projects.`]}),`\n`,_jsxs(_components.li,{children:[`New exported types: `,_jsx(_components.code,{children:`GetUsageRequest`}),`, `,_jsx(_components.code,{children:`GetUsageResponse`}),`, `,_jsx(_components.code,{children:`GetUsageCurrentRequest`}),`, and `,_jsx(_components.code,{children:`GetUsageCurrentResponse`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`A fine-grained control plane key needs two things to call the usage endpoints: it must be rooted at the organization, and it must hold the `,_jsx(_components.code,{children:`org.analytics.query`}),` permission. That permission can now be granted when you create a key.`]}),`\n`]})]})]}),`\n`,_jsx(Update,{label:`October 27th, 2026`,id:`october-27th%2C-2026`,children:_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-2`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-8-0`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.8.0`,children:`v1.8.0`})}),_jsx(Heading,{level:`4`,id:`added`,children:`Added`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Chart queries`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`client.charts.query()`}),` and `,_jsx(_components.code,{children:`query_async()`}),` aggregate a project’s events into a single chart result. They take a `,_jsx(_components.code,{children:`ChartQueryRequest`}),` (date range, filters including an `,_jsx(_components.code,{children:`event_type`}),` filter, and the query) and a required keyword-only `,_jsx(_components.code,{children:`project_id`}),`, and they return a `,_jsx(_components.code,{children:`ChartQueryResponse`}),`. Both models can be imported from `,_jsx(_components.code,{children:`honeyhive.models`}),`. The call is sent with the data plane API key, which needs `,_jsx(_components.code,{children:`project.event.get`}),`.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Project-scoped dataset, datapoint, and experiment run operations`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Every `,_jsx(_components.code,{children:`client.datasets`}),`, `,_jsx(_components.code,{children:`client.datapoints`}),`, and `,_jsx(_components.code,{children:`client.experiments`}),` run method (list, get, create, update, delete, add/remove datapoints, schema, results, comparisons, and their `,_jsx(_components.code,{children:`_async`}),` variants and aliases) accepts an optional keyword-only `,_jsx(_components.code,{children:`project_id`}),`. With `,_jsx(_components.code,{children:`project_id`}),`, the call targets that project and is sent with the data plane API key (`,_jsx(_components.code,{children:`data_plane_api_key`}),` / `,_jsx(_components.code,{children:`HH_DATA_PLANE_API_KEY`}),`).`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:_jsx(_components.code,{children:`datapoints.create_batch()`})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`client.datapoints.create_batch()`}),` and `,_jsx(_components.code,{children:`create_batch_async()`}),` create datapoints from events, optionally adding them to a dataset.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:_jsx(_components.code,{children:`experiments.get_run_metrics()`})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`client.experiments.get_run_metrics()`}),` and `,_jsx(_components.code,{children:`get_run_metrics_async()`}),` return the event metrics of an experiment run, with optional `,_jsx(_components.code,{children:`dateRange`}),` and `,_jsx(_components.code,{children:`filters`}),`.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:_jsx(_components.code,{children:`experiments.compare_run_events_async()`})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New async variant of `,_jsx(_components.code,{children:`compare_run_events()`}),`.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`deprecated`,children:`Deprecated`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Dataset, datapoint, and experiment run calls without `,_jsx(_components.code,{children:`project_id`})]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Calling these methods without `,_jsx(_components.code,{children:`project_id`}),` still uses the project API key, but now emits a `,_jsx(_components.code,{children:`DeprecationWarning`}),`. These calls stop working in v2.0. Pass `,_jsx(_components.code,{children:`project_id`}),` and configure a data plane API key. `,_jsx(_components.code,{children:`evaluate()`}),` and `,_jsx(_components.code,{children:`run_experiment()`}),` do not emit this warning.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility`,children:`Compatibility`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Higher minimums for optional integration extras`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The Claude Agent SDK extras (`,_jsx(_components.code,{children:`openinference-claude-agent-sdk`}),`, `,_jsx(_components.code,{children:`all-openinference`}),`) now require `,_jsx(_components.code,{children:`claude-agent-sdk>=0.2.121`}),`. The `,_jsx(_components.code,{children:`aws-strands`}),` extra now requires `,_jsx(_components.code,{children:`strands-agents>=1.48.0`}),`. If you pin either package below these versions, raise the pin.`]}),`\n`]}),`\n`]}),`\n`]})]})}),`\n`,_jsxs(Update,{label:`October 5th, 2026`,id:`october-5th%2C-2026`,children:[_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli-2`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-8-0-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.8.0`,children:`v1.8.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-4`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`--data-plane-api-key`}),` and `,_jsx(_components.code,{children:`HH_DATA_PLANE_API_KEY`}),` for fine-grained data plane API keys (`,_jsx(_components.code,{children:`hh_fgdp_...`}),`). Create one in the HoneyHive app under project, workspace, or organization `,_jsx(_components.strong,{children:`Settings → API keys`}),`, on the `,_jsx(_components.strong,{children:`Data Plane`}),` tab.`]}),`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`--project-id`}),` to `,_jsx(_components.code,{children:`charts list`}),`, `,_jsx(_components.code,{children:`charts create`}),`, `,_jsx(_components.code,{children:`charts get`}),`, and `,_jsx(_components.code,{children:`charts update`}),`. With `,_jsx(_components.code,{children:`--project-id`}),`, the command calls the project-scoped route and authenticates with the data plane API key. Pass `,_jsx(_components.code,{children:`--project-id`}),` even when the key is rooted at that project. Without it, the command calls the legacy route with the project API key, even when a data plane key is configured. A project API key cannot stand in for a data plane key on the scoped route.`]}),`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`data-plane-api-keys create`}),`, which creates a data plane API key. It takes `,_jsx(_components.code,{children:`--project-id`}),`, `,_jsx(_components.code,{children:`--name`}),`, `,_jsx(_components.code,{children:`--permissions`}),` (a JSON array such as `,_jsx(_components.code,{children:`[\"project.chart.get\"]`}),`), `,_jsx(_components.code,{children:`--expires-at`}),` (ISO 8601), and an optional `,_jsx(_components.code,{children:`--description`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`ingestion-api-keys create`}),`, which creates an ingestion API key. It takes `,_jsx(_components.code,{children:`--project-id`}),`, `,_jsx(_components.code,{children:`--name`}),`, and an optional `,_jsx(_components.code,{children:`--description`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`charts create`}),` and `,_jsx(_components.code,{children:`charts update`}),` now accept `,_jsx(_components.code,{children:`five_minute`}),` and `,_jsx(_components.code,{children:`auto`}),` for `,_jsx(_components.code,{children:`--bucketing`}),`.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-3`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`An error for a missing API key now names the key the command needs and the flag and environment variable that supply it. For data plane and control plane keys, the error also says where in the app to create one. An error for a malformed key names the flag or environment variable the bad value came from.`}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`--verbose`}),` on data plane commands now logs all three data plane keys (project, ingestion, and data plane), and prints `,_jsx(_components.code,{children:`(none)`}),` for any that are unset. A masked ingestion or fine-grained key shows its prefix and key id with the secret replaced by `,_jsx(_components.code,{children:`******`}),`, which matches the masked form in the HoneyHive app.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-3`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Calling `,_jsx(_components.code,{children:`charts`}),` commands without `,_jsx(_components.code,{children:`--project-id`}),` is deprecated. `,_jsx(_components.code,{children:`--project-id`}),` becomes required in the next major version. Pass it with a data plane API key.`]}),`\n`,_jsxs(_components.li,{children:[`The CLI now checks every typed key you have configured (ingestion, data plane, control plane) when a command starts, including keys that command does not use. A key of the wrong type or an incomplete key in `,_jsx(_components.code,{children:`HH_INGESTION_API_KEY`}),`, `,_jsx(_components.code,{children:`HH_DATA_PLANE_API_KEY`}),`, or the matching flag now fails the command before it sends a request. The error names the flag or environment variable that holds the bad value. Fix or unset the stale value.`]}),`\n`,_jsxs(_components.li,{children:[`The CLI no longer checks the format of the project API key (`,_jsx(_components.code,{children:`--project-api-key`}),` / `,_jsx(_components.code,{children:`HH_PROJECT_API_KEY`}),`) locally. It sends the value as given, and the server decides whether to accept it.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-3`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-7-0-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.7.0`,children:`v1.7.0`})}),_jsx(Heading,{level:`4`,id:`added-2`,children:`Added`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Data plane API keys`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`HoneyHive(...)`}),` accepts `,_jsx(_components.code,{children:`data_plane_api_key=`}),`, also read from `,_jsx(_components.code,{children:`HH_DATA_PLANE_API_KEY`}),`, for fine-grained `,_jsx(_components.code,{children:`hh_fgdp_`}),` keys. It is exposed as `,_jsx(_components.code,{children:`client.data_plane_api_key`}),`. A value that is set but is not a complete `,_jsx(_components.code,{children:`hh_fgdp_`}),` key raises `,_jsx(_components.code,{children:`ValueError`}),` at construction.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Project-scoped chart operations`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Every `,_jsx(_components.code,{children:`client.charts`}),` method (`,_jsx(_components.code,{children:`list`}),`, `,_jsx(_components.code,{children:`get`}),`, `,_jsx(_components.code,{children:`create`}),`, `,_jsx(_components.code,{children:`update`}),`, `,_jsx(_components.code,{children:`delete`}),` and their `,_jsx(_components.code,{children:`_async`}),` variants) accepts an optional keyword-only `,_jsx(_components.code,{children:`project_id`}),`. With `,_jsx(_components.code,{children:`project_id`}),`, the call targets that project and is sent with the data plane API key. It never falls back to `,_jsx(_components.code,{children:`api_key`}),`.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:_jsx(_components.code,{children:`MissingApiKeyError`})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New exception, importable from `,_jsx(_components.code,{children:`honeyhive`}),`. It subclasses `,_jsx(_components.code,{children:`ValueError`}),` and has a `,_jsx(_components.code,{children:`key_kind`}),` attribute. An operation raises it before sending a request when no key it accepts is configured, and the message names the key and environment variable to set.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`changed-2`,children:`Changed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Clearer API error messages`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`HTTPException`}),` raised by API calls now includes the server’s error message, and a 404 adds a hint that the resource does not exist or the key lacks access to it. New `,_jsx(_components.code,{children:`server_message`}),` and `,_jsx(_components.code,{children:`error_code`}),` attributes expose the parsed error body. The exception type, `,_jsx(_components.code,{children:`status_code`}),`, and the message prefix are unchanged, so existing handlers keep working.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Missing keys fail before the request is sent`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`An operation called with no usable API key now raises `,_jsx(_components.code,{children:`MissingApiKeyError`}),` locally, instead of sending an unauthenticated request and failing at the API.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[_jsx(_components.code,{children:`events.update_event()`}),` accepts a model`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`update_event()`}),` now accepts an `,_jsx(_components.code,{children:`UpdateEventRequest`}),` as well as a dict. A dict is validated into the model.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`deprecated-2`,children:`Deprecated`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Chart calls without `,_jsx(_components.code,{children:`project_id`})]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Calling a `,_jsx(_components.code,{children:`client.charts`}),` method without `,_jsx(_components.code,{children:`project_id`}),` still uses the project API key, but now emits a `,_jsx(_components.code,{children:`DeprecationWarning`}),`. These calls stop working in v2.0. Pass `,_jsx(_components.code,{children:`project_id`}),` and configure a data plane API key.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Unused error-handling helpers in `,_jsx(_components.code,{children:`honeyhive.utils`})]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`ErrorHandler`}),`, `,_jsx(_components.code,{children:`get_error_handler`}),`, `,_jsx(_components.code,{children:`handle_api_errors`}),`, `,_jsx(_components.code,{children:`ErrorContext`}),`, `,_jsx(_components.code,{children:`ValidationError`}),`, `,_jsx(_components.code,{children:`HoneyHiveConnectionError`}),`, `,_jsx(_components.code,{children:`RateLimitError`}),`, and `,_jsx(_components.code,{children:`AuthenticationError`}),` can still be imported from `,_jsx(_components.code,{children:`honeyhive.utils`}),` and `,_jsx(_components.code,{children:`honeyhive.utils.error_handler`}),`, but importing them now emits a `,_jsx(_components.code,{children:`DeprecationWarning`}),`.`]}),`\n`]}),`\n`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-api-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-api-sdk-2`,children:`TypeScript API SDK`}),_jsx(Heading,{level:`3`,id:`v1-6-0`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-api-client/releases/tag/v1.6.0`,children:`v1.6.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-5`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`dataPlaneApiKey`}),` client option and `,_jsx(_components.code,{children:`HH_DATA_PLANE_API_KEY`}),` environment variable for a fine-grained data plane API key (a value beginning with `,_jsx(_components.code,{children:`hh_fgdp_`}),`). The client sends this key on operations that are scoped to a project, which today are the chart operations.`]}),`\n`,_jsxs(_components.li,{children:[`Every `,_jsx(_components.code,{children:`client.charts`}),` method (`,_jsx(_components.code,{children:`list`}),`, `,_jsx(_components.code,{children:`create`}),`, `,_jsx(_components.code,{children:`get`}),`, `,_jsx(_components.code,{children:`update`}),`, `,_jsx(_components.code,{children:`delete`}),`) now accepts an optional `,_jsx(_components.code,{children:`project_id`}),`. With `,_jsx(_components.code,{children:`project_id`}),`, the call goes to the project-scoped chart endpoints and authenticates with the data plane API key. `,_jsx(_components.code,{children:`client.charts.list()`}),` now takes an optional request argument, a new `,_jsx(_components.code,{children:`GetChartsRequest`}),` type.`]}),`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`client.dataPlaneApiKeys.create()`}),` creates a fine-grained data plane API key rooted at a project. New `,_jsx(_components.code,{children:`client.ingestionApiKeys.create()`}),` creates an ingestion API key for a project. Both return `,_jsx(_components.code,{children:`key_value`}),` once, and you cannot get it again.`]}),`\n`,_jsxs(_components.li,{children:[`New exported request and response types: `,_jsx(_components.code,{children:`CreateDataPlaneApiKeyRequest`}),`/`,_jsx(_components.code,{children:`Response`}),` and `,_jsx(_components.code,{children:`CreateIngestionApiKeyRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`New exported error classes `,_jsx(_components.code,{children:`MissingApiKeyError`}),` and `,_jsx(_components.code,{children:`MalformedApiKeyError`}),`, both subclasses of `,_jsx(_components.code,{children:`HoneyHiveError`}),`, and the `,_jsx(_components.code,{children:`ApiKeyKind`}),` type. Each error has a `,_jsx(_components.code,{children:`keyKind`}),` property (`,_jsx(_components.code,{children:`'project'`}),`, `,_jsx(_components.code,{children:`'ingestion'`}),` or `,_jsx(_components.code,{children:`'dataPlane'`}),`). `,_jsx(_components.code,{children:`MalformedApiKeyError`}),` also has a `,_jsx(_components.code,{children:`source`}),` property that names the option or environment variable the bad value came from.`]}),`\n`,_jsxs(_components.li,{children:[`The chart `,_jsx(_components.code,{children:`bucketing`}),` option now accepts `,_jsx(_components.code,{children:`five_minute`}),` and `,_jsx(_components.code,{children:`auto`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Verbose logging now prints a `,_jsx(_components.code,{children:`Data plane API key:`}),` line. The line shows `,_jsx(_components.code,{children:`hh_fgdp_`}),`, the key id and none of the secret, and shows `,_jsx(_components.code,{children:`(none)`}),` when no key is set.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-4`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`An operation whose required key is not configured now throws a `,_jsx(_components.code,{children:`MissingApiKeyError`}),` before it sends a request. The error names the key that the operation needs. Previously, some operations sent the request without a key and failed with a `,_jsx(_components.code,{children:`401`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`An `,_jsx(_components.code,{children:`ingestionApiKey`}),`, `,_jsx(_components.code,{children:`HH_INGESTION_API_KEY`}),`, `,_jsx(_components.code,{children:`dataPlaneApiKey`}),` or `,_jsx(_components.code,{children:`HH_DATA_PLANE_API_KEY`}),` value that is not a well-formed key of that kind now throws a `,_jsx(_components.code,{children:`MalformedApiKeyError`}),` when the client is constructed. The error never echoes the value.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-4`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Calling a `,_jsx(_components.code,{children:`client.charts`}),` method without `,_jsx(_components.code,{children:`project_id`}),` is deprecated. It becomes required in the next major version. These calls still use the legacy route and the project API key. Each method logs a one-time deprecation warning to stderr. To migrate, pass `,_jsx(_components.code,{children:`project_id`}),` and configure a data plane API key.`]}),`\n`,_jsxs(_components.li,{children:[`A call with `,_jsx(_components.code,{children:`project_id`}),` needs a data plane API key and never falls back to the project API key. A data plane API key cannot send traces, so keep an ingestion key or a project key for ingestion.`]}),`\n`,_jsxs(_components.li,{children:[`A client with no API key configured now constructs successfully. The first operation that needs a key throws a `,_jsx(_components.code,{children:`MissingApiKeyError`}),`. Previously, the constructor threw a `,_jsx(_components.code,{children:`Missing API key`}),` error. If your code catches the missing-key error at construction, also handle it at the first call.`]}),`\n`,_jsxs(_components.li,{children:[`A client that has custom middleware or its own `,_jsx(_components.code,{children:`Authorization`}),` header never throws `,_jsx(_components.code,{children:`MissingApiKeyError`}),`. Authentication is left to the middleware or the header.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-cp-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-control-plane-sdk-2`,children:`TypeScript Control Plane SDK`}),_jsx(Heading,{level:`3`,id:`v1-3-0`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-control-plane-sdk/releases/tag/v1.3.0`,children:`v1.3.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-6`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New exported error classes `,_jsx(_components.code,{children:`MissingApiKeyError`}),` and `,_jsx(_components.code,{children:`MalformedApiKeyError`}),`. Both extend `,_jsx(_components.code,{children:`HoneyHiveError`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Client construction now throws `,_jsx(_components.code,{children:`MissingApiKeyError`}),` when no API key and no middleware are configured, and `,_jsx(_components.code,{children:`MalformedApiKeyError`}),` when `,_jsx(_components.code,{children:`apiKey`}),` or `,_jsx(_components.code,{children:`HH_CONTROL_PLANE_API_KEY`}),` is not a well-formed fine-grained control plane key. Before this release, both cases threw a plain `,_jsx(_components.code,{children:`Error`}),`. You can now catch these with `,_jsx(_components.code,{children:`instanceof`}),` checks. `,_jsx(_components.code,{children:`MalformedApiKeyError.source`}),` names the option or environment variable that held the bad value. The error never echoes the key.`]}),`\n`]})]})]}),`\n`,_jsx(Update,{label:`September 29th, 2026`,id:`september-29th%2C-2026`,children:_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-4`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-6-1`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.6.1`,children:`v1.6.1`})}),_jsx(Heading,{level:`4`,id:`fixed`,children:`Fixed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Install: `,_jsx(_components.code,{children:`import honeyhive`}),` failed with `,_jsx(_components.code,{children:`ModuleNotFoundError: No module named 'requests'`})]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`On a fresh install of 1.6.0, `,_jsx(_components.code,{children:`import honeyhive`}),` could fail with this error. `,_jsx(_components.code,{children:`pip install honeyhive`}),` now installs everything the SDK needs to import.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-2`,children:`Compatibility`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[_jsx(_components.code,{children:`requests>=2.32.0`}),` and `,_jsx(_components.code,{children:`urllib3>=1.26`}),` are now required`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`If your environment pins `,_jsx(_components.code,{children:`requests`}),` or `,_jsx(_components.code,{children:`urllib3`}),` below these versions, raise the pin to install 1.6.1.`]}),`\n`]}),`\n`]}),`\n`]})]})}),`\n`,_jsxs(Update,{label:`September 22nd, 2026`,id:`september-22nd%2C-2026`,children:[_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli-3`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-7-0-3`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.7.0`,children:`v1.7.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-7`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`--ingestion-api-key`}),` and `,_jsx(_components.code,{children:`HH_INGESTION_API_KEY`}),` for the commands that send traces and events: `,_jsx(_components.code,{children:`sessions create`}),`, `,_jsx(_components.code,{children:`sessions create-event-batch`}),`, `,_jsx(_components.code,{children:`events create`}),`, `,_jsx(_components.code,{children:`events update`}),`, and `,_jsx(_components.code,{children:`events create-batch`}),`. These take an ingestion API key (`,_jsx(_components.code,{children:`hh_ingst_...`}),`), created at project scope on the `,_jsx(_components.strong,{children:`API Keys`}),` page under `,_jsx(_components.strong,{children:`Settings`}),`. They still accept a project API key, so a shell that exports only `,_jsx(_components.code,{children:`HH_PROJECT_API_KEY`}),` keeps working. Give ingestion its own key in a new setup. Every other command is unchanged.`]}),`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`--slug`}),` to `,_jsx(_components.code,{children:`workspaces create`}),` and `,_jsx(_components.code,{children:`virtual-dataplanes create`}),`. A slug takes letters, digits, and underscores only, and must be globally unique. Omit it and the server derives a slug from the name with a random suffix, exactly as before. Supply it when the identifier has to match a value maintained outside HoneyHive, such as an identity provider group that grants access to the scope. A slug already in use fails with a `,_jsx(_components.code,{children:`409`}),` naming the slug.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-5`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`API keys are now trimmed before the CLI checks their shape. A key pasted with surrounding whitespace or a trailing newline is no longer rejected locally when the API would have accepted it.`}),`\n`,_jsx(_components.li,{children:`The pre-flight key check now judges the exact credential each command sends, rather than assuming one key per API. Its error names the key and the flag or environment variable that command needs.`}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`metrics run`}),` no longer fails with a `,_jsx(_components.code,{children:`400`}),` when the metric reads `,_jsx(_components.code,{children:`ground_truth`}),` and the event carries none. The command now exits 0 and prints `,_jsx(_components.code,{children:`success: false`}),` with `,_jsx(_components.code,{children:`result: null`}),` and the reason in `,_jsx(_components.code,{children:`explanation`}),`. A script that read the non-zero exit code as the signal must read `,_jsx(_components.code,{children:`success`}),` in the printed JSON instead.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-5`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Every command now gets a `,_jsx(_components.code,{children:`404`}),` where the API used to answer a permission failure with `,_jsx(_components.code,{children:`403`}),` or a rejected API key with `,_jsx(_components.code,{children:`401`}),`. A rejected key is one that is invalid, revoked, or expired. The response does not say which case applies, and the error no longer names the check that refused. A script that branched on `,_jsx(_components.code,{children:`401`}),` or `,_jsx(_components.code,{children:`403`}),` must read the not-found error as covering all three cases. These are server changes, so they apply to every CLI version.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`--needs-ground-truth`}),` and `,_jsx(_components.code,{children:`--no-needs-ground-truth`}),` on `,_jsx(_components.code,{children:`metrics create`}),` and `,_jsx(_components.code,{children:`metrics update`}),` are deprecated and ignored, and now log a deprecation warning to stderr. The server infers ground-truth use from the metric definition. A Python metric that reads `,_jsx(_components.code,{children:`ground_truth`}),` needs it, and so does an LLM metric whose template references it. The same field inside a `,_jsx(_components.code,{children:`metric-versions create --content`}),` payload, and inside any `,_jsx(_components.code,{children:`--filename`}),` payload, is likewise accepted and ignored. The flags will be removed in the next major version. Stop passing them.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`workspace_id`}),` inside the `,_jsx(_components.code,{children:`--event`}),` payload of `,_jsx(_components.code,{children:`metrics run`}),` is deprecated and ignored. The workspace whose provider credentials run the metric is derived from the authenticated key’s scope, so naming a different workspace has no effect.`]}),`\n`,_jsxs(_components.li,{children:[`The bundled YAML parser used for `,_jsx(_components.code,{children:`--filename`}),` input now bounds recursive merge aliases. A YAML file with deeply chained merge keys no longer expands unchecked.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-api-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-api-sdk-3`,children:`TypeScript API SDK`}),_jsx(Heading,{level:`3`,id:`v1-5-0`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-api-client/releases/tag/v1.5.0`,children:`v1.5.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-8`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`ingestionApiKey`}),` client option and `,_jsx(_components.code,{children:`HH_INGESTION_API_KEY`}),` environment variable for supplying an ingestion API key (a value beginning with `,_jsx(_components.code,{children:`hh_ingst_`}),`). The client sends it on the operations that send traces and events: `,_jsx(_components.code,{children:`client.sessions.create()`}),`, `,_jsx(_components.code,{children:`client.sessions.createEventBatch()`}),`, `,_jsx(_components.code,{children:`client.events.create()`}),`, `,_jsx(_components.code,{children:`client.events.update()`}),`, and `,_jsx(_components.code,{children:`client.events.createBatch()`}),`. The client sends the project API key on everything else.`]}),`\n`,_jsx(_components.li,{children:`An ingestion API key is now a credential on its own. A process that only sends traces and events can construct a client with the ingestion key and no project API key. Every other operation on such a client behaves as it does with no project API key configured.`}),`\n`,_jsxs(_components.li,{children:[`An `,_jsx(_components.code,{children:`ingestionApiKey`}),` or `,_jsx(_components.code,{children:`HH_INGESTION_API_KEY`}),` value that is not a well-formed ingestion key throws when the client is constructed. The error names the option or variable the value came from and never echoes the value.`]}),`\n`,_jsxs(_components.li,{children:[`Verbose logging now prints an `,_jsx(_components.code,{children:`Ingestion API key:`}),` line next to the project key. The line shows `,_jsx(_components.code,{children:`hh_ingst_`}),`, the key id, and none of the secret. This is the form HoneyHive displays, so you can match the line against the key in your account. A credential that is not configured logs as `,_jsx(_components.code,{children:`(none)`}),`.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-6`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`client.metrics.run()`}),` now derives the workspace whose provider credentials run the metric from the caller’s authenticated scope rather than from the request body. Previously a `,_jsx(_components.code,{children:`workspace_id`}),` in the request event was forwarded as-is. That let an authenticated caller run an ad-hoc LLM metric against another tenant’s configured provider credentials.`]}),`\n`,_jsxs(_components.li,{children:[`A metric run that needs ground truth the event does not carry is now skipped instead of failed. `,_jsx(_components.code,{children:`client.metrics.run()`}),` returns `,_jsx(_components.code,{children:`200`}),` with `,_jsx(_components.code,{children:`success: false`}),`, a null `,_jsx(_components.code,{children:`result`}),`, and an `,_jsx(_components.code,{children:`explanation`}),`. It previously threw a `,_jsx(_components.code,{children:`400`}),` with `,_jsx(_components.code,{children:`ground_truth_missing`}),`. Adding ground truth to the event re-enqueues it, and the metric computes on that pass.`]}),`\n`,_jsxs(_components.li,{children:[`The error thrown when no credential is configured now reads `,_jsx(_components.code,{children:`Missing API key`}),` and names the four current sources: `,_jsx(_components.code,{children:`projectApiKey`}),`, `,_jsx(_components.code,{children:`ingestionApiKey`}),`, `,_jsx(_components.code,{children:`HH_PROJECT_API_KEY`}),`, and `,_jsx(_components.code,{children:`HH_INGESTION_API_KEY`}),`.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-6`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`A client configured with a project API key and no ingestion key still sends the project key on the ingestion operations.`}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`HH_INGESTION_API_KEY`}),` is read at client construction for the first time in this release. A process that already sets it to a non-empty value that is not a well-formed ingestion key now fails to construct a client. Unset the variable, or set it to your ingestion key, before upgrading.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`needs_ground_truth`}),` is deprecated and ignored on every metric request and response type, including `,_jsx(_components.code,{children:`CreateMetricRequest`}),`, `,_jsx(_components.code,{children:`UpdateMetricRequest`}),`, `,_jsx(_components.code,{children:`MetricItem`}),`, `,_jsx(_components.code,{children:`MetricVersionContent`}),`, and the metric passed to `,_jsx(_components.code,{children:`client.metrics.run()`}),`. The API now infers the need for ground truth from the metric definition. The property remains in the type definitions so existing code keeps compiling, and reads still return the stored value.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`workspace_id`}),` on the event passed to `,_jsx(_components.code,{children:`client.metrics.run()`}),` is deprecated and ignored. It remains accepted, so existing code keeps compiling. Remove it and rely on the key’s scope.`]}),`\n`,_jsxs(_components.li,{children:[`Bumped `,_jsx(_components.code,{children:`axios`}),` from `,_jsx(_components.code,{children:`1.19.0`}),` to `,_jsx(_components.code,{children:`1.20.0`}),`.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-cp-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-control-plane-sdk-3`,children:`TypeScript Control Plane SDK`}),_jsx(Heading,{level:`3`,id:`v1-2-0`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-control-plane-sdk/releases/tag/v1.2.0`,children:`v1.2.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-9`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Scope creation accepts a caller-supplied identifier: an optional `,_jsx(_components.code,{children:`slug`}),` on `,_jsx(_components.code,{children:`CreateWorkspaceRequest`}),` and `,_jsx(_components.code,{children:`CreateVirtualDataplaneRequest`}),`. Omit it and the server derives one from `,_jsx(_components.code,{children:`name`}),` with a random suffix, as before. Supply it when the identifier has to match a value maintained outside HoneyHive, such as an identity provider group that grants access to the scope. A slug already in use gets a 409 that names it.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-7`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The client validates the API key at construction. A value in `,_jsx(_components.code,{children:`apiKey`}),` or `,_jsx(_components.code,{children:`HH_CONTROL_PLANE_API_KEY`}),` that is not a well-formed fine-grained control plane key (`,_jsx(_components.code,{children:`hh_fgcp_…`}),`) throws when you build the client. The error names the option or the environment variable the value came from, and it does not echo the key. Before this release the same value failed on the first request as an authentication error.`]}),`\n`,_jsx(_components.li,{children:`Every control plane endpoint answers an authorization failure with 404 instead of 403. The status no longer reveals whether a resource you may not touch exists. The control plane does not emit 403 at all any more.`}),`\n`,_jsxs(_components.li,{children:[`An authentication failure on a fine-grained control plane key also answers 404 instead of 401. This covers a missing `,_jsx(_components.code,{children:`Authorization`}),` header and a key that is invalid, revoked, or expired.`]}),`\n`,_jsx(_components.li,{children:`A 404 now means “not found”, “not permitted”, or “not authenticated”, and the response does not say which. Code that refreshes a credential on 401 or branches on 403 no longer sees either status.`}),`\n`,_jsxs(_components.li,{children:[`A 404 body carries the fixed `,_jsx(_components.code,{children:`message`}),` `,_jsx(_components.code,{children:`\"Not found\"`}),` and the fixed `,_jsx(_components.code,{children:`errorCode`}),` `,_jsx(_components.code,{children:`\"not_found\"`}),`. `,_jsx(_components.code,{children:`ApiError.message`}),` and `,_jsx(_components.code,{children:`parseError()`}),` no longer name which check refused. The server logs carry that detail.`]}),`\n`,_jsx(_components.li,{children:`The three status changes above are control plane changes, so they apply to every SDK version, not only 1.2.0.`}),`\n`,_jsxs(_components.li,{children:[`The `,_jsx(_components.code,{children:`axios`}),` runtime dependency moves from 1.19.0 to 1.20.0.`]}),`\n`]})]})]}),`\n`,_jsx(Update,{label:`September 17th, 2026`,id:`september-17th%2C-2026`,children:_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-5`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-6-0-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.6.0`,children:`v1.6.0`})}),_jsx(Heading,{level:`4`,id:`added-3`,children:`Added`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Ingestion API keys`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`HoneyHive(...)`}),`, `,_jsx(_components.code,{children:`HoneyHiveTracer.init(...)`}),`, and `,_jsx(_components.code,{children:`evaluate(...)`}),` accept `,_jsx(_components.code,{children:`ingestion_api_key=`}),`, also read from `,_jsx(_components.code,{children:`HH_INGESTION_API_KEY`}),`. The ingestion key is sent only on the routes that create sessions and write events, including OTLP trace export; every other route keeps using `,_jsx(_components.code,{children:`api_key`}),`. When it is unset, `,_jsx(_components.code,{children:`api_key`}),` is used for ingestion too, so existing setups are unchanged. A value that is set but is not an `,_jsx(_components.code,{children:`hh_ingst_`}),` key raises `,_jsx(_components.code,{children:`ValueError`}),` at construction rather than failing later at request time.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`changed-3`,children:`Changed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Connection settings resolve the same way in every entry point`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The client, the tracer, `,_jsx(_components.code,{children:`evaluate()`}),`, and the CLI now resolve `,_jsx(_components.code,{children:`api_key`}),`, `,_jsx(_components.code,{children:`ingestion_api_key`}),`, and the API URL through one resolver, so a single process can no longer authenticate with one key in `,_jsx(_components.code,{children:`evaluate()`}),` and another in a tracer. An explicit argument beats the environment, and a blank value counts as unset.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`HH_API_KEY`}),` and `,_jsx(_components.code,{children:`HH_API_URL`}),` are the preferred variables and win when more than one is set. `,_jsx(_components.code,{children:`HONEYHIVE_API_KEY`}),`, `,_jsx(_components.code,{children:`HONEYHIVE_SERVER_URL`}),`, and `,_jsx(_components.code,{children:`HH_SERVER_URL`}),` still work but are deprecated and emit a `,_jsx(_components.code,{children:`DeprecationWarning`}),` once per process. If you set both `,_jsx(_components.code,{children:`HONEYHIVE_API_KEY`}),` and `,_jsx(_components.code,{children:`HH_API_KEY`}),` to different values, `,_jsx(_components.code,{children:`evaluate()`}),` and the CLI now use `,_jsx(_components.code,{children:`HH_API_KEY`}),`; they previously preferred `,_jsx(_components.code,{children:`HONEYHIVE_API_KEY`}),`.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixed-2`,children:`Fixed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Tracer: `,_jsx(_components.code,{children:`RecursionError`}),` from `,_jsx(_components.code,{children:`trace.get_tracer()`}),` after tracer shutdown`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Shutting down the main `,_jsx(_components.code,{children:`HoneyHiveTracer`}),` reset the global OpenTelemetry provider to a fresh `,_jsx(_components.code,{children:`ProxyTracerProvider`}),`, which delegates back to the global provider — itself. Any subsequent `,_jsx(_components.code,{children:`opentelemetry.trace.get_tracer(...)`}),` call (including module-level calls made when importing libraries such as `,_jsx(_components.code,{children:`mcp`}),`) raised `,_jsx(_components.code,{children:`RecursionError`}),`. Shutdown now clears the global provider so OpenTelemetry returns its own proxy.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Events API: retry config now controls `,_jsx(_components.code,{children:`events.export()`})]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`HoneyHive(retry_config=...)`}),` and `,_jsx(_components.code,{children:`HH_MAX_RETRIES`}),` were accepted and ignored. They did not reach `,_jsx(_components.code,{children:`events.export()`}),` / `,_jsx(_components.code,{children:`export_async()`}),`, which fell back to a fixed budget of 3 retries. Both configuration methods now set the retry budget for `,_jsx(_components.code,{children:`events.export()`}),` / `,_jsx(_components.code,{children:`export_async()`}),`.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-3`,children:`Compatibility`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Anthropic extras cap `,_jsx(_components.code,{children:`anthropic`}),` below 1.0.0`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`honeyhive[openinference-anthropic]`}),`, `,_jsx(_components.code,{children:`honeyhive[traceloop-anthropic]`}),`, and the bundles that include them now require `,_jsx(_components.code,{children:`anthropic\u003c1.0.0`}),`, and the OpenInference extras also require `,_jsx(_components.code,{children:`openinference-instrumentation-anthropic\u003c2.0.0`}),`. A mismatched pair either raised `,_jsx(_components.code,{children:`ModuleNotFoundError`}),` on import or logged a dependency conflict and skipped instrumentation. The SDK already implicitly depended on `,_jsx(_components.code,{children:`anthropic\u003c1.0.0`}),` as that was the only version available, which worked until Anthropic recently published v1.0 of their SDK.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`LangChain extras now install `,_jsx(_components.code,{children:`langchain-openai`})]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`honeyhive[openinference-langchain]`}),` and `,_jsx(_components.code,{children:`honeyhive[traceloop-langchain]`}),` pull in `,_jsx(_components.code,{children:`langchain-openai>=1.0.0`}),` alongside `,_jsx(_components.code,{children:`langchain`}),`. These dependencies were always required for tracing to work correctly with LangChain but they weren’t declared before.`]}),`\n`]}),`\n`]}),`\n`]})]})}),`\n`,_jsxs(Update,{label:`August 14th, 2026`,id:`august-14th%2C-2026`,children:[_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli-4`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-6-0-3`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.6.0`,children:`v1.6.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-10`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`workspaces`}),` subcommands for managing workspaces: `,_jsx(_components.code,{children:`workspaces create`}),`, `,_jsx(_components.code,{children:`workspaces get`}),`, `,_jsx(_components.code,{children:`workspaces update`}),`, and `,_jsx(_components.code,{children:`workspaces delete`}),`. `,_jsx(_components.code,{children:`workspaces create`}),` takes `,_jsx(_components.code,{children:`--virtual-dataplane-id`}),` and `,_jsx(_components.code,{children:`--name`}),`, plus optional `,_jsx(_components.code,{children:`--description`}),` and `,_jsx(_components.code,{children:`--workspace-creator`}),` (the email of the user to grant workspace-creator membership to, for API key callers).`]}),`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`virtual-dataplanes`}),` subcommands for managing virtual data planes: `,_jsx(_components.code,{children:`virtual-dataplanes create`}),`, `,_jsx(_components.code,{children:`virtual-dataplanes get`}),`, `,_jsx(_components.code,{children:`virtual-dataplanes update`}),`, and `,_jsx(_components.code,{children:`virtual-dataplanes delete`}),`. `,_jsx(_components.code,{children:`virtual-dataplanes create`}),` takes `,_jsx(_components.code,{children:`--org-id`}),` and `,_jsx(_components.code,{children:`--name`}),`, plus optional `,_jsx(_components.code,{children:`--cluster-id`}),` (required when the organization has no virtual data planes yet, or its virtual data planes span more than one cluster) and `,_jsx(_components.code,{children:`--dataplane-creator`}),`.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`workspaces delete`}),` and `,_jsx(_components.code,{children:`virtual-dataplanes delete`}),` accept `,_jsx(_components.code,{children:`--dangerously-delete-child-scopes`}),` to archive a scope that still has active children, archiving those children too. Without the flag, deleting a workspace with active projects — or a virtual data plane with active workspaces — fails with a `,_jsx(_components.code,{children:`409`}),` and changes nothing.`]}),`\n`,_jsxs(_components.li,{children:[`Like `,_jsx(_components.code,{children:`projects`}),` and `,_jsx(_components.code,{children:`alerts`}),`, both new command groups talk to the HoneyHive control plane: they take a fine-grained control plane API key (`,_jsx(_components.code,{children:`hh_fgcp_...`}),`) via `,_jsx(_components.code,{children:`--control-plane-api-key`}),` / `,_jsx(_components.code,{children:`HH_CONTROL_PLANE_API_KEY`}),` and honor `,_jsx(_components.code,{children:`--control-plane-url`}),` / `,_jsx(_components.code,{children:`HH_CONTROL_PLANE_URL`}),`. Data plane commands are unchanged and still use `,_jsx(_components.code,{children:`--project-api-key`}),` / `,_jsx(_components.code,{children:`HH_PROJECT_API_KEY`}),`.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-7`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Security fixes`}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-api-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-api-sdk-4`,children:`TypeScript API SDK`}),_jsx(Heading,{level:`3`,id:`v1-4-1`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-api-client/releases/tag/v1.4.1`,children:`v1.4.1`})}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-8`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`client.metrics.run()`}),` now distinguishes failure classes: `,_jsx(_components.code,{children:`400`}),` when the request is invalid or is missing inputs the metric needs (e.g. ground truth the event doesn’t carry), `,_jsx(_components.code,{children:`422`}),` when evaluation itself failed (`,_jsx(_components.code,{children:`execution_error`}),`, `,_jsx(_components.code,{children:`compilation_error`}),`, `,_jsx(_components.code,{children:`template_render_error`}),`, `,_jsx(_components.code,{children:`llm_response_parse_error`}),`, …), and `,_jsx(_components.code,{children:`500`}),` for internal errors. `,_jsx(_components.code,{children:`ApiError.parseError()`}),` returns the `,_jsx(_components.code,{children:`errorCode`}),` along with the evaluator’s detailed failure text in `,_jsx(_components.code,{children:`message`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Empty or whitespace-only `,_jsx(_components.code,{children:`name`}),` values passed to `,_jsx(_components.code,{children:`client.experiments.createRun()`}),` and `,_jsx(_components.code,{children:`client.experiments.updateRun()`}),` are now rejected with a `,_jsx(_components.code,{children:`400`}),`, and accepted names are trimmed before storage. Such names previously created runs with a blank display name.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-8`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Composite metrics are no longer supported. `,_jsx(_components.code,{children:`child_metrics`}),` is marked deprecated on every metric request and response type and is now ignored by the API, and creating or updating a metric with `,_jsx(_components.code,{children:`type: 'COMPOSITE'`}),` returns a `,_jsx(_components.code,{children:`400`}),`. Both remain in the type definitions so existing code keeps compiling; replace composite metrics with standalone metrics.`]}),`\n`,_jsxs(_components.li,{children:[`Bumped `,_jsx(_components.code,{children:`axios`}),` from `,_jsx(_components.code,{children:`1.18.0`}),` to `,_jsx(_components.code,{children:`1.19.0`}),`.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-cp-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-control-plane-sdk-4`,children:`TypeScript Control Plane SDK`}),_jsx(Heading,{level:`3`,id:`v1-1-0`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-control-plane-sdk/releases/tag/v1.1.0`,children:`v1.1.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-11`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Workspace management via the new `,_jsx(_components.code,{children:`workspaces`}),` namespace: `,_jsx(_components.code,{children:`create()`}),`, `,_jsx(_components.code,{children:`get()`}),`, `,_jsx(_components.code,{children:`update()`}),`, and `,_jsx(_components.code,{children:`delete()`}),`, with matching `,_jsx(_components.code,{children:`CreateWorkspaceRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, `,_jsx(_components.code,{children:`GetWorkspaceRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, `,_jsx(_components.code,{children:`UpdateWorkspaceRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, and `,_jsx(_components.code,{children:`DeleteWorkspaceRequest`}),`/`,_jsx(_components.code,{children:`Response`}),` types.`]}),`\n`,_jsxs(_components.li,{children:[`Virtual data plane management via the new `,_jsx(_components.code,{children:`virtualDataplanes`}),` namespace: `,_jsx(_components.code,{children:`create()`}),`, `,_jsx(_components.code,{children:`get()`}),`, `,_jsx(_components.code,{children:`update()`}),`, and `,_jsx(_components.code,{children:`delete()`}),`, with matching `,_jsx(_components.code,{children:`CreateVirtualDataplaneRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, `,_jsx(_components.code,{children:`GetVirtualDataplaneRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, `,_jsx(_components.code,{children:`UpdateVirtualDataplaneRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, and `,_jsx(_components.code,{children:`DeleteVirtualDataplaneRequest`}),`/`,_jsx(_components.code,{children:`Response`}),` types.`]}),`\n`,_jsxs(_components.li,{children:[`Deleting a workspace or virtual data plane archives everything inside it, so a resource with active children is rejected with a 409 unless the request sets `,_jsx(_components.code,{children:`dangerously_delete_child_scopes: true`}),`.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-9`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Creating a project with `,_jsx(_components.code,{children:`project_creator`}),` now marks that user’s sessions for refresh, so they no longer have to sign out and back in to see the new project. The change is in the control plane, so it applies to any SDK version. The refresh takes effect on their next request to the control plane, so an idle browser tab may still need a page reload.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-9`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`Security fixes`}),`\n`]})]})]}),`\n`,_jsx(Update,{label:`August 4th, 2026`,id:`august-4th%2C-2026`,children:_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli-5`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-5-1`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.5.1`,children:`v1.5.1`})}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-10`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Fixed the Homebrew formula published for each release so `,_jsx(_components.code,{children:`brew install honeyhive`}),` and `,_jsx(_components.code,{children:`brew upgrade honeyhive`}),` resolve the current CLI version. The formula no longer declares an explicit version that recent Homebrew versions reject as redundant during validation.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-10`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Installing a stable release via Homebrew now requires Homebrew 6.0.14 or newer, which reads the version from the release URL. An older client that installs without updating first — most commonly `,_jsx(_components.code,{children:`HOMEBREW_NO_AUTO_UPDATE=1`}),` in a Docker or CI image — records the wrong version and will not report the install as outdated afterwards. Run `,_jsx(_components.code,{children:`brew update`}),` before installing, or reinstall once Homebrew is current. `,_jsx(_components.code,{children:`npm`}),` and `,_jsx(_components.code,{children:`npx`}),` installs are unaffected.`]}),`\n`]})]})}),`\n`,_jsxs(Update,{label:`August 3rd, 2026`,id:`august-3rd%2C-2026`,children:[_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli-6`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-5-0-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.5.0`,children:`v1.5.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-12`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`projects`}),` subcommands for managing projects: `,_jsx(_components.code,{children:`projects create`}),`, `,_jsx(_components.code,{children:`projects get`}),`, `,_jsx(_components.code,{children:`projects update`}),`, and `,_jsx(_components.code,{children:`projects delete`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`alerts`}),` subcommands: `,_jsx(_components.code,{children:`alerts list`}),`, `,_jsx(_components.code,{children:`alerts create`}),`, and `,_jsx(_components.code,{children:`alerts get`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`--control-plane-api-key`}),` / `,_jsx(_components.code,{children:`HH_CONTROL_PLANE_API_KEY`}),` and `,_jsx(_components.code,{children:`--control-plane-url`}),` / `,_jsx(_components.code,{children:`HH_CONTROL_PLANE_URL`}),`. The new `,_jsx(_components.code,{children:`projects`}),` and `,_jsx(_components.code,{children:`alerts`}),` commands talk to the HoneyHive control plane, which takes a fine-grained control plane API key (`,_jsx(_components.code,{children:`hh_fgcp_...`}),`) created at workspace or organization scope — not a project API key. Every other command is unchanged and still uses `,_jsx(_components.code,{children:`--project-api-key`}),` / `,_jsx(_components.code,{children:`HH_PROJECT_API_KEY`}),`; you only need the key for the commands you actually run.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-11`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The CLI now checks what kind of API key you supplied before sending a request. Passing a control plane key to a data plane command (or the reverse) fails immediately with a message naming the key kind the command needs and the flag or environment variable the wrong key came from, instead of an unexplained `,_jsx(_components.code,{children:`401`}),` from the server.`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`--verbose`}),` now reports the URL and key for whichever API the command talks to (`,_jsx(_components.code,{children:`Control plane URL:`}),` for `,_jsx(_components.code,{children:`projects`}),` and `,_jsx(_components.code,{children:`alerts`}),`), and masks a fine-grained control plane key exactly as the HoneyHive app displays it, so you can match a log line to a key in your account.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-11`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Composite metrics are no longer supported. `,_jsx(_components.code,{children:`metrics create`}),` and `,_jsx(_components.code,{children:`metrics update`}),` now fail with a `,_jsx(_components.code,{children:`400`}),` when passed `,_jsx(_components.code,{children:`--type COMPOSITE`}),`, existing composite metrics and their versions have been deleted, and `,_jsx(_components.code,{children:`--child-metrics`}),` no longer has any effect on a metric’s score.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-cp-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-control-plane-sdk-5`,children:`TypeScript Control Plane SDK`}),_jsx(Heading,{level:`3`,id:`v1-0-0`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-control-plane-sdk/releases/tag/v1.0.0`,children:`v1.0.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-13`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`First stable release of the HoneyHive TypeScript Control Plane SDK (`,_jsx(_components.code,{children:`@honeyhive/control-plane-sdk`}),`). Functionally identical to `,_jsx(_components.code,{children:`1.0.0-rc.1`}),` — no code changes, so upgrading from the release candidate requires no migration.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-cp-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-control-plane-sdk-6`,children:`TypeScript Control Plane SDK`}),_jsx(Heading,{level:`3`,id:`v1-0-0-rc-1`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-control-plane-sdk/releases/tag/v1.0.0-rc.1`,children:`v1.0.0-rc.1`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-14`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Initial release candidate of the HoneyHive TypeScript Control Plane SDK (`,_jsx(_components.code,{children:`@honeyhive/control-plane-sdk`}),`).`]}),`\n`]})]})]}),`\n`,_jsx(Update,{label:`July 21st, 2026`,id:`july-21st%2C-2026`,children:_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-6`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-5-1-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.5.1`,children:`v1.5.1`})}),_jsx(_components.p,{children:`No customer-facing changes. Internal release tooling only.`})]})}),`\n`,_jsx(Update,{label:`July 20th, 2026`,id:`july-20th%2C-2026`,children:_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-7`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-5-0-3`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.5.0`,children:`v1.5.0`})}),_jsx(Heading,{level:`4`,id:`fixed-3`,children:`Fixed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`\n`,_jsx(_components.p,{children:_jsxs(_components.strong,{children:[`Tracing: root SERVER spans get an automatic `,_jsx(_components.code,{children:`session_id`})]})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`When `,_jsx(_components.code,{children:`HoneyHiveTracer`}),` is initialized with `,_jsx(_components.code,{children:`skip_backend_session_creation=True`}),` and no `,_jsx(_components.code,{children:`session_id`}),`, the SDK now generates a `,_jsx(_components.code,{children:`session_id`}),` for each root SERVER span (`,_jsx(_components.code,{children:`SpanKind.SERVER`}),`, no parent) and propagates it via baggage. Previously these root spans were dropped and their children appeared orphaned in the Sessions tab — common with `,_jsx(_components.code,{children:`FastAPIInstrumentor`}),`, where the root span is created before any middleware runs. Existing single-session and client-span behavior is unchanged.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsx(_components.p,{children:_jsxs(_components.strong,{children:[`Experiments: `,_jsx(_components.code,{children:`evaluate()`}),` normalizes run names`]})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`evaluate(name=...)`}),` now trims surrounding whitespace, and a blank or whitespace-only name falls back to the default `,_jsx(_components.code,{children:`experiment-\u003cid>`}),` name instead of being submitted as-is. The backend also now rejects empty run names.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`removed`,children:`Removed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Composite metrics removed from the metric API surface`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The `,_jsx(_components.code,{children:`child_metrics`}),` field and the generated `,_jsx(_components.code,{children:`*ChildMetricsItem`}),` models have been removed from the metric request/response models in `,_jsx(_components.code,{children:`honeyhive.models`}),`, completing the composite-metrics deprecation. Composite metrics are no longer part of the product, so any remaining code referencing them can be safely cleaned up and removed.`]}),`\n`,_jsxs(_components.li,{children:[`Existing code that passes `,_jsx(_components.code,{children:`child_metrics=...`}),` keeps working — metric models accept unknown fields (`,_jsx(_components.code,{children:`extra=\"allow\"`}),`), so the value is still accepted without error. The API already ignored and dropped `,_jsx(_components.code,{children:`child_metrics`}),`, so this change aligns the client with the backend behavior. The only difference: `,_jsx(_components.code,{children:`child_metrics`}),` is no longer a declared field, so reading `,_jsx(_components.code,{children:`metric.child_metrics`}),` off a response now raises `,_jsx(_components.code,{children:`AttributeError`}),` instead of returning `,_jsx(_components.code,{children:`None`}),`.`]}),`\n`]}),`\n`]}),`\n`]})]})}),`\n`,_jsxs(Update,{label:`June 26th, 2026`,id:`june-26th%2C-2026`,children:[_jsxs(`div`,{\"data-filter\":`ts-api-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-api-sdk-5`,children:`TypeScript API SDK`}),_jsx(Heading,{level:`3`,id:`v1-4-0-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-api-client/releases/tag/v1.4.0`,children:`v1.4.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-15`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`client.events.get({ event_id })`}),` method for `,_jsx(_components.code,{children:`GET /v1/events/{event_id}`}),`, fetching a single event by ID.`]}),`\n`,_jsxs(_components.li,{children:[`All SDK methods now accept an optional `,_jsx(_components.code,{children:`options?: FetchOptions`}),` second argument for cancelling in-flight requests via `,_jsx(_components.code,{children:`AbortController`}),`. New exported `,_jsx(_components.code,{children:`FetchOptions`}),` type (`,_jsx(_components.code,{children:`{ signal?: AbortSignal }`}),`). Example: `,_jsx(_components.code,{children:`await client.events.search(request, { signal: controller.signal })`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`projectApiKey`}),` client option and `,_jsx(_components.code,{children:`HH_PROJECT_API_KEY`}),` environment variable for supplying the project-scoped API key. These replace the previous `,_jsx(_components.code,{children:`apiKey`}),` option and `,_jsx(_components.code,{children:`HH_API_KEY`}),` environment variable.`]}),`\n`,_jsxs(_components.li,{children:[`Experiment run objects now include a `,_jsx(_components.code,{children:`dataset_name`}),` field alongside `,_jsx(_components.code,{children:`dataset_id`}),`.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-12`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The `,_jsx(_components.code,{children:`outputs`}),` field on update-event requests is now typed as an object or `,_jsx(_components.code,{children:`null`}),` (previously `,_jsx(_components.code,{children:`unknown`}),`). Passing a non-object value (array, string, or scalar) previously corrupted the stored event and broke downstream consumers such as the Python SDK; such values are now rejected. Passing `,_jsx(_components.code,{children:`null`}),` preserves the existing outputs.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-12`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The `,_jsx(_components.code,{children:`apiKey`}),` client option and `,_jsx(_components.code,{children:`HH_API_KEY`}),` environment variable are deprecated and will be removed in the next major version. They continue to work but log a one-time deprecation warning to stderr on client construction. Migrate to `,_jsx(_components.code,{children:`projectApiKey`}),` / `,_jsx(_components.code,{children:`HH_PROJECT_API_KEY`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Bumped `,_jsx(_components.code,{children:`axios`}),` from `,_jsx(_components.code,{children:`1.16.0`}),` to `,_jsx(_components.code,{children:`1.18.0`}),`.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli-7`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-4-0-3`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.4.0`,children:`v1.4.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-16`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`events get --event-id \u003cid>`}),` to fetch a single event by its ID.`]}),`\n`,_jsxs(_components.li,{children:[`Added the `,_jsx(_components.code,{children:`--project-api-key`}),` flag and `,_jsx(_components.code,{children:`HH_PROJECT_API_KEY`}),` environment variable as the primary way to authenticate the CLI. Verbose output now labels the key as `,_jsx(_components.code,{children:`Project API key:`}),` (previously `,_jsx(_components.code,{children:`API Key:`}),`).`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`experiments list-runs`}),`, `,_jsx(_components.code,{children:`experiments get-run`}),`, and `,_jsx(_components.code,{children:`experiments get-summary`}),` output now includes the linked `,_jsx(_components.code,{children:`dataset_name`}),`, and `,_jsx(_components.code,{children:`get-summary`}),` now reports `,_jsx(_components.code,{children:`dataset_id`}),` for offline (`,_jsx(_components.code,{children:`EXT-*`}),`) datasets that previously returned `,_jsx(_components.code,{children:`null`}),`.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-13`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`events update --outputs`}),` now requires an object or `,_jsx(_components.code,{children:`null`}),` (where `,_jsx(_components.code,{children:`null`}),` preserves the existing outputs); non-object values (strings, arrays, scalars) are rejected up front. These values were previously accepted but corrupted the stored event, which could break other consumers reading it back.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-13`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The `,_jsx(_components.code,{children:`--api-key`}),` flag and `,_jsx(_components.code,{children:`HH_API_KEY`}),` environment variable are now deprecated aliases for `,_jsx(_components.code,{children:`--project-api-key`}),` / `,_jsx(_components.code,{children:`HH_PROJECT_API_KEY`}),`. They still work but log a deprecation warning to stderr and will be removed in the next major version. Migrate to the new names.`]}),`\n`]})]})]}),`\n`,_jsx(Update,{label:`June 24th, 2026`,id:`june-24th%2C-2026`,children:_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-8`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-4-0-4`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.4.0`,children:`v1.4.0`})}),_jsx(Heading,{level:`4`,id:`added-4`,children:`Added`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Copilot Studio trace forwarding adapter`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`honeyhive.adapters.copilot_studio.copilot_studio_records_to_spans()`}),` converts Azure Application Insights diagnostic-export records from a Copilot Studio agent into OpenTelemetry spans you can forward to HoneyHive via OTLP. Maps user/agent messages, tool calls, and errors using GenAI semantic conventions, preserves parent/child trace correlation, and groups a conversation into a single HoneyHive session. Set `,_jsx(_components.code,{children:`COPILOT_STUDIO_ADAPTER_KEEP_TOPIC_SPANS=1`}),` to retain internal orchestration spans (dropped by default) and `,_jsx(_components.code,{children:`COPILOT_STUDIO_ADAPTER_DEBUG=1`}),` to attach the raw source record for debugging.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[_jsx(_components.code,{children:`dataset_name`}),` on experiment runs`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`ExperimentRunObject`}),` now includes a `,_jsx(_components.code,{children:`dataset_name`}),` field alongside `,_jsx(_components.code,{children:`dataset_id`}),`, so the linked dataset’s name is available when listing or fetching experiment runs. The field is optional and resolves to `,_jsx(_components.code,{children:`None`}),` for offline (`,_jsx(_components.code,{children:`EXT-*`}),`), deleted, or unset datasets.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`changed-4`,children:`Changed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Event update: `,_jsx(_components.code,{children:`outputs`}),` must be an object`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`UpdateEventRequest.outputs`}),` and `,_jsx(_components.code,{children:`LegacyUpdateEventRequest.outputs`}),` are now typed as `,_jsx(_components.code,{children:`Dict[str, Any]`}),` instead of `,_jsx(_components.code,{children:`Any`}),`. Update calls accept only normalized (object-shaped) outputs; previously an array could slip through and corrupt the stored event. If you were passing a list when updating an event, wrap it as `,_jsx(_components.code,{children:`{\"value\": [...]}`}),`. Event creation and OTEL ingestion continue to normalize arrays automatically.`]}),`\n`]}),`\n`]}),`\n`]})]})}),`\n`,_jsx(Update,{label:`June 18th, 2026`,id:`june-18th%2C-2026`,children:_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-9`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-3-0-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.3.0`,children:`v1.3.0`})}),_jsx(Heading,{level:`4`,id:`added-5`,children:`Added`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Configurable API request timeout`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`HoneyHive(timeout=...)`}),` now sets the per-request timeout, in seconds, for standard API client calls. You can also set `,_jsx(_components.code,{children:`HH_API_TIMEOUT`}),`; the explicit `,_jsx(_components.code,{children:`timeout=`}),` argument takes precedence. The default remains 5 seconds, so existing clients keep the same timeout unless you opt in.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Event lookup by ID`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The generated sync and async event services now include `,_jsx(_components.code,{children:`GET /v1/events/{event_id}`}),` as `,_jsx(_components.code,{children:`getEvent(..., event_id=...)`}),`, returning `,_jsx(_components.code,{children:`GetEventResponse`}),` / `,_jsx(_components.code,{children:`GetEventResponseEvent`}),` models exported from `,_jsx(_components.code,{children:`honeyhive.models`}),`.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Custom tracer export HTTP sessions`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`HoneyHiveTracer.init(requests_session=...)`}),` and `,_jsx(_components.code,{children:`TracerConfig(requests_session=...)`}),` now accept a caller-owned `,_jsx(_components.code,{children:`requests.Session`}),` for OTLP span export. Use this when you need custom proxies, retries, TLS settings, or connection pooling. The SDK does not close a session that you provide.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-4`,children:`Compatibility`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Self-host: `,_jsx(_components.code,{children:`GET /v1/events/{event_id}`}),` requires backend v1.3.0+`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Self-hosted deployments must run a HoneyHive backend on version 1.3.0 or greater before calling `,_jsx(_components.code,{children:`GET /v1/events/{event_id}`}),`. Older self-host backends do not expose this route, so event-by-id calls will fail until the backend is upgraded. HoneyHive Cloud users are unaffected.`]}),`\n`]}),`\n`]}),`\n`]})]})}),`\n`,_jsx(Update,{label:`June 11th, 2026`,id:`june-11th%2C-2026`,children:_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-10`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-2-1`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.2.1`,children:`v1.2.1`})}),_jsx(_components.p,{children:`No customer-facing changes. Internal OpenAPI generator and generated-model cleanup only.`})]})}),`\n`,_jsx(Update,{label:`June 4th, 2026`,id:`june-4th%2C-2026`,children:_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-11`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-2-0-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.2.0`,children:`v1.2.0`})}),_jsx(Heading,{level:`4`,id:`added-6`,children:`Added`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Metric versions API: `,_jsx(_components.code,{children:`client.metric_versions`})]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`client.metric_versions.list(metric_id)`}),`, `,_jsx(_components.code,{children:`.create(metric_id, request)`}),`, and `,_jsx(_components.code,{children:`.deploy(metric_id, version_name)`}),` methods (plus `,_jsx(_components.code,{children:`*_async`}),` variants) for managing a metric’s immutable version history, backed by `,_jsx(_components.code,{children:`/v1/metrics/{metric_id}/versions`}),`. Each metric keeps a history of versions with one deployed at a time; pass `,_jsx(_components.code,{children:`deploy_immediately=True`}),` on create to atomically deploy the new version. Version request/response models are exported from `,_jsx(_components.code,{children:`honeyhive.models`}),`.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixed-4`,children:`Fixed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`OTLP JSON exporter: serialize numeric fields as JSON strings`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Integer span attributes (`,_jsx(_components.code,{children:`intValue`}),`) and uint64 timestamp fields\n(`,_jsx(_components.code,{children:`startTimeUnixNano`}),`, `,_jsx(_components.code,{children:`endTimeUnixNano`}),`, `,_jsx(_components.code,{children:`timeUnixNano`}),`) were emitted as\nraw JSON numbers. The protobuf JSON mapping spec requires these to be JSON\nstrings; values above 2^53 could otherwise lose precision silently through\nfloat64 rounding. Now matches native `,_jsx(_components.code,{children:`opentelemetry-exporter-otlp-proto-http`}),`\nbehavior.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`Decorator input capture: remove truncation of serialized list/dict values`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsx(_components.li,{children:`List and dict span attributes are serialized to strings and\npreviously were truncated to 1000 characters in length. The arbitrary limit\nof 1000 characters has been removed.`}),`\n`]}),`\n`]}),`\n`]})]})}),`\n`,_jsxs(Update,{label:`May 29th, 2026`,id:`may-29th%2C-2026`,children:[_jsxs(`div`,{\"data-filter\":`ts-api-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-api-sdk-6`,children:`TypeScript API SDK`}),_jsx(Heading,{level:`3`,id:`v1-3-0-3`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-api-client/releases/tag/v1.3.0`,children:`v1.3.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-17`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`client.metricVersions`}),` namespace with `,_jsx(_components.code,{children:`list`}),`, `,_jsx(_components.code,{children:`create`}),`, and `,_jsx(_components.code,{children:`deploy`}),` methods for managing snapshot versions of a metric’s definition via `,_jsx(_components.code,{children:`/v1/metrics/{metric_id}/versions`}),` and `,_jsx(_components.code,{children:`/v1/metrics/{metric_id}/versions/{version_name}/deploy`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`New exported request/response types: `,_jsx(_components.code,{children:`GetMetricVersionsRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, `,_jsx(_components.code,{children:`CreateMetricVersionRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, and `,_jsx(_components.code,{children:`DeployMetricVersionRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli-8`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-3-0-4`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.3.0`,children:`v1.3.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-18`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`metric-versions`}),` subcommands for managing snapshot versions of a metric’s definition: `,_jsx(_components.code,{children:`metric-versions list`}),`, `,_jsx(_components.code,{children:`metric-versions create`}),`, and `,_jsx(_components.code,{children:`metric-versions deploy`}),`. Use these to review history, create new draft or immediately-deployed versions, and roll between versions without losing history.`]}),`\n`]})]})]}),`\n`,_jsxs(Update,{label:`May 22nd, 2026`,id:`may-22nd%2C-2026`,children:[_jsxs(`div`,{\"data-filter\":`ts-api-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-api-sdk-7`,children:`TypeScript API SDK`}),_jsx(Heading,{level:`3`,id:`v1-2-1-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-api-client/releases/tag/v1.2.1`,children:`v1.2.1`})}),_jsx(_components.p,{children:`Internal improvements only.`})]}),_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli-9`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-2-1-3`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.2.1`,children:`v1.2.1`})}),_jsx(_components.p,{children:`Internal improvements only.`})]})]}),`\n`,_jsxs(Update,{label:`May 21st, 2026`,id:`may-21st%2C-2026`,children:[_jsxs(`div`,{\"data-filter\":`ts-api-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-api-sdk-8`,children:`TypeScript API SDK`}),_jsx(Heading,{level:`3`,id:`v1-2-0-3`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-api-client/releases/tag/v1.2.0`,children:`v1.2.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-19`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Added a `,_jsx(_components.code,{children:`dataPlaneUrl`}),` client option and `,_jsx(_components.code,{children:`HH_DATA_PLANE_URL`}),` environment variable for configuring the data plane URL. These replace the previous `,_jsx(_components.code,{children:`serverUrl`}),` option and `,_jsx(_components.code,{children:`HH_API_URL`}),` environment variable.`]}),`\n`,_jsxs(_components.li,{children:[`The verbose logging output now labels the resolved URL as `,_jsx(_components.code,{children:`Data plane URL:`}),` (previously `,_jsx(_components.code,{children:`API URL:`}),`).`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-14`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Environment variables set to the empty string (e.g. `,_jsx(_components.code,{children:`HH_API_KEY=`}),`, `,_jsx(_components.code,{children:`HH_DATA_PLANE_URL=`}),`) are now treated as unset and fall back to defaults, rather than being propagated as a literal empty string.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-14`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The `,_jsx(_components.code,{children:`serverUrl`}),` client option and `,_jsx(_components.code,{children:`HH_API_URL`}),` environment variable are deprecated and will be removed in the next major version. They continue to work but log a one-time deprecation warning to stderr on client construction. Migrate to `,_jsx(_components.code,{children:`dataPlaneUrl`}),` / `,_jsx(_components.code,{children:`HH_DATA_PLANE_URL`}),`.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli-10`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-2-0-4`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.2.0`,children:`v1.2.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-20`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`--data-plane-url`}),` flag and `,_jsx(_components.code,{children:`HH_DATA_PLANE_URL`}),` environment variable for pointing the CLI at a specific HoneyHive data plane (e.g. self-hosted or staging deployments). Verbose output now labels this value as `,_jsx(_components.code,{children:`Data plane URL:`}),` instead of `,_jsx(_components.code,{children:`API URL:`}),`.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-15`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The `,_jsx(_components.code,{children:`--base-url`}),` flag and `,_jsx(_components.code,{children:`HH_API_URL`}),` environment variable are deprecated and will be removed in the next major version. They still work as aliases for the new names but now log a deprecation warning to stderr on each invocation. Migrate to `,_jsx(_components.code,{children:`--data-plane-url`}),` / `,_jsx(_components.code,{children:`HH_DATA_PLANE_URL`}),`.`]}),`\n`]})]})]}),`\n`,_jsxs(Update,{label:`May 19th, 2026`,id:`may-19th%2C-2026`,children:[_jsxs(`div`,{\"data-filter\":`ts-api-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-api-sdk-9`,children:`TypeScript API SDK`}),_jsx(Heading,{level:`3`,id:`v1-1-1`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-api-client/releases/tag/v1.1.1`,children:`v1.1.1`})}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-15`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Fixed a bug where `,_jsx(_components.code,{children:`error.message`}),` on thrown API errors was not populated, causing error logs and CLI output to omit the underlying failure detail.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli-11`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-1-1-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.1.1`,children:`v1.1.1`})}),_jsx(Heading,{level:`4`,id:`fixes-%26-improvements-16`,children:`Fixes \u0026 Improvements`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Error output for failed API requests now includes the server’s error message. All CLI commands that hit the API previously printed only `,_jsx(_components.code,{children:`API error \u003cstatus>`}),`; they now print `,_jsx(_components.code,{children:`API error \u003cstatus>: \u003cmessage>`}),` with the actionable detail from the server.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-12`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-1-0-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.1.0`,children:`v1.1.0`})}),_jsx(Heading,{level:`4`,id:`added-7`,children:`Added`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Charts API: `,_jsx(_components.code,{children:`client.charts`}),` CRUD surface`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`client.charts.create()`}),`, `,_jsx(_components.code,{children:`.list()`}),`, `,_jsx(_components.code,{children:`.get(chart_id)`}),`, `,_jsx(_components.code,{children:`.update(chart_id, request)`}),`, and `,_jsx(_components.code,{children:`.delete(chart_id)`}),` methods (plus `,_jsx(_components.code,{children:`*_async`}),` variants) backed by `,_jsx(_components.code,{children:`POST /v1/charts`}),`, `,_jsx(_components.code,{children:`GET /v1/charts`}),`, `,_jsx(_components.code,{children:`GET /v1/charts/{chart_id}`}),`, `,_jsx(_components.code,{children:`PUT /v1/charts/{chart_id}`}),`, and `,_jsx(_components.code,{children:`DELETE /v1/charts/{chart_id}`}),`. Chart request and response models are exported from `,_jsx(_components.code,{children:`honeyhive.models`}),`.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`changed-5`,children:`Changed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:`CrewAI integration example refreshed`}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The CrewAI example (`,_jsx(_components.code,{children:`examples/integrations/crewai_integration.py`}),`) has been rewritten with three realistic customer-support scenarios (single-agent with tool calls, sequential two-agent crew, and `,_jsx(_components.code,{children:`@trace`}),` escalation). Crews are now named for clearer span labels, and teardown properly flushes remaining spans.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`fixed-5`,children:`Fixed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`\n`,_jsx(_components.p,{children:_jsxs(_components.strong,{children:[`Tracer init: `,_jsx(_components.code,{children:`project`}),` / `,_jsx(_components.code,{children:`HH_PROJECT`}),` no longer required`]})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`HoneyHiveTracer.init()`}),` previously required `,_jsx(_components.code,{children:`project`}),` and treated a missing value as degraded mode. Since the backend determines the project from the API key, only `,_jsx(_components.code,{children:`api_key`}),` is actually needed. Callers that followed the v1.0.1 deprecation guidance and removed `,_jsx(_components.code,{children:`project`}),` were silently running in degraded mode — this is now fixed. `,_jsx(_components.code,{children:`project`}),` and `,_jsx(_components.code,{children:`HH_PROJECT`}),` remain accepted for backwards compatibility (deprecated, to be removed in v2.0).`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsx(_components.p,{children:_jsxs(_components.strong,{children:[`Duplicate `,_jsx(_components.code,{children:`model`}),` spans in `,_jsx(_components.code,{children:`evaluate()`})]})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`evaluate()`}),` could occasionally emit two `,_jsx(_components.code,{children:`model`}),` spans for a single LLM call when running multiple datapoints concurrently. The underlying race condition in instrumentation setup has been fixed.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`removed-2`,children:`Removed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[_jsx(_components.code,{children:`cp_base_url=`}),` parameter and `,_jsx(_components.code,{children:`HH_CP_API_URL`}),` env var removed`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`These options were vestigial configuration for an incomplete Control Plane integration that was never wired up or shipped. No customer code could have depended on them. They have been removed to clean up the public API surface. If you happen to pass `,_jsx(_components.code,{children:`cp_base_url=`}),` or set `,_jsx(_components.code,{children:`HH_CP_API_URL`}),`, simply remove them — `,_jsx(_components.code,{children:`HH_API_URL`}),` / `,_jsx(_components.code,{children:`base_url=`}),` is the only base-URL setting.`]}),`\n`]}),`\n`]}),`\n`]})]})]}),`\n`,_jsxs(Update,{label:`May 15th, 2026`,id:`may-15th%2C-2026`,children:[_jsxs(`div`,{\"data-filter\":`ts-api-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-api-sdk-10`,children:`TypeScript API SDK`}),_jsx(Heading,{level:`3`,id:`v1-1-0-3`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-api-client/releases/tag/v1.1.0`,children:`v1.1.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-21`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`client.charts`}),` namespace with `,_jsx(_components.code,{children:`create`}),`, `,_jsx(_components.code,{children:`list`}),`, `,_jsx(_components.code,{children:`get`}),`, `,_jsx(_components.code,{children:`update`}),`, and `,_jsx(_components.code,{children:`delete`}),` methods for managing charts via `,_jsx(_components.code,{children:`/v1/charts`}),` and `,_jsx(_components.code,{children:`/v1/charts/{chart_id}`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`client.experiments.getSummary()`}),` method for `,_jsx(_components.code,{children:`GET /v1/runs/{run_id}/summary`}),`, returning pass/fail results, metric aggregations, per-datapoint results, and the experiment run object.`]}),`\n`,_jsxs(_components.li,{children:[`New `,_jsx(_components.code,{children:`client.sessions.createEventBatch()`}),` method for `,_jsx(_components.code,{children:`POST /v1/sessions/{session_id}/events/batch`}),`, accepting a batch of events scoped to a single session (the `,_jsx(_components.code,{children:`session_id`}),` from the path overrides any value in the event body).`]}),`\n`,_jsxs(_components.li,{children:[`New exported request/response types: `,_jsx(_components.code,{children:`CreateChartRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, `,_jsx(_components.code,{children:`GetChartRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, `,_jsx(_components.code,{children:`GetChartsResponse`}),`, `,_jsx(_components.code,{children:`UpdateChartRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, `,_jsx(_components.code,{children:`DeleteChartRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, `,_jsx(_components.code,{children:`GetExperimentSummaryRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`, and `,_jsx(_components.code,{children:`CreateSessionEventBatchRequest`}),`/`,_jsx(_components.code,{children:`Response`}),`.`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`compatibility-%26-deprecations-16`,children:`Compatibility \u0026 Deprecations`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Bumped `,_jsx(_components.code,{children:`axios`}),` from `,_jsx(_components.code,{children:`1.15.2`}),` to `,_jsx(_components.code,{children:`1.16.0`}),`.`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli-12`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-1-0-4`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.1.0`,children:`v1.1.0`})}),_jsx(Heading,{level:`4`,id:`what%E2%80%99s-new-22`,children:`What’s New`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`charts`}),` subcommands for managing charts via the CLI: `,_jsx(_components.code,{children:`charts list`}),`, `,_jsx(_components.code,{children:`charts create`}),`, `,_jsx(_components.code,{children:`charts get`}),`, `,_jsx(_components.code,{children:`charts update`}),`, and `,_jsx(_components.code,{children:`charts delete`}),`.`]}),`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`sessions create-event-batch`}),` for adding a batch of events to an existing session.`]}),`\n`,_jsxs(_components.li,{children:[`Added `,_jsx(_components.code,{children:`experiments get-summary`}),` for retrieving an experiment run’s evaluation summary, including pass/fail results and metric aggregations.`]}),`\n`]})]})]}),`\n`,_jsxs(Update,{label:`May 11th, 2026`,id:`may-11th%2C-2026`,children:[_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-13`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-0-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.0.2`,children:`v1.0.2`})}),_jsx(Heading,{level:`4`,id:`fixed-6`,children:`Fixed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`\n`,_jsx(_components.p,{children:_jsxs(_components.strong,{children:[`Experiments: client-side evaluator scores now flow end-to-end in `,_jsx(_components.code,{children:`evaluate()`})]})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Scores returned by evaluators in `,_jsx(_components.code,{children:`evaluate()`}),` are now written to the correct user-function chain span via `,_jsx(_components.code,{children:`enrich_span(metrics=…)`}),`, exported over OTLP, and surfaced in server-side run comparison’s per-event `,_jsx(_components.code,{children:`metric_deltas`}),`. Previously, evaluator scores were duplicated into `,_jsx(_components.code,{children:`run.metadata.evaluator_metrics`}),` and onto the per-datapoint session event, but neither path actually populated the per-event surface that run comparison requires — client-side evaluator scoring and comparison did not work end-to-end.`]}),`\n`,_jsx(_components.li,{children:`The two legacy write paths have been removed now that the scores are correctly written to the chain span as the single source of truth.`}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsx(_components.p,{children:_jsxs(_components.strong,{children:[`API Client: `,_jsx(_components.code,{children:`event_id`}),` typed as required on `,_jsx(_components.code,{children:`POST /events`}),` responses`]})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`PostEventResponse.event_id`}),` was incorrectly typed as `,_jsx(_components.code,{children:`Optional[str]`}),` in the OpenAPI spec; the backend always returns the field on a 2xx response. The spec was corrected and the generated SDK now reflects the runtime guarantee, removing the need for defensive `,_jsx(_components.code,{children:`if event.event_id:`}),` guards. No runtime behavior change, but strictly typed mypy codebases with `,_jsx(_components.code,{children:`warn_unreachable = True`}),` or pyright with `,_jsx(_components.code,{children:`reportUnnecessaryComparison`}),` enabled may produce “unnecessary comparison” or “redundant Optional guard” warnings, depending on exact usage patterns.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`added-8`,children:`Added`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[_jsx(_components.code,{children:`FilterFieldType.DATETIME`}),` enum value`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The SDK enum was missing the `,_jsx(_components.code,{children:`\"datetime\"`}),` wire value supported by the backend. Additive — no behavior change for existing callers.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`deprecated-3`,children:`Deprecated`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.strong,{children:_jsx(_components.code,{children:`FilterFieldType.ID`})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsx(_components.code,{children:`ID = \"id\"`}),` was never a valid backend wire value and has always been rejected on the wire. Use `,_jsx(_components.code,{children:`\"string\"`}),` for UUID-shaped fields such as `,_jsx(_components.code,{children:`session_id`}),`. The alias is preserved so existing imports continue to resolve and will be removed in the next major.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`removed-3`,children:`Removed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[_jsx(_components.code,{children:`honeyhive`}),` Python CLI entry point removed from `,_jsx(_components.code,{children:`pyproject.toml`})]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The shipped Python `,_jsx(_components.code,{children:`honeyhive`}),` console script was non-functional (dead code) and shadowed the official TypeScript CLI on `,_jsx(_components.code,{children:`$PATH`}),`. CLI functionality is now provided by the official `,_jsxs(_components.a,{href:`https://docs.honeyhive.ai/v2/cli-reference/getting-started`,children:[_jsx(_components.code,{children:`honeyhive`}),` TypeScript CLI`]}),`; removing the Python script entry point lets `,_jsx(_components.code,{children:`honeyhive`}),` resolve correctly when both packages are installed globally.`]}),`\n`]}),`\n`]}),`\n`]})]}),_jsxs(`div`,{\"data-filter\":`ts-api-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-api-sdk-11`,children:`TypeScript API SDK`}),_jsx(Heading,{level:`3`,id:`v1-0-1`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-api-client/releases/tag/v1.0.1`,children:`v1.0.1`})}),_jsx(_components.p,{children:`Internal improvements only.`})]}),_jsxs(`div`,{\"data-filter\":`ts-api-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`typescript-api-sdk-12`,children:`TypeScript API SDK`}),_jsx(Heading,{level:`3`,id:`v1-0-0-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/typescript-api-client/releases/tag/v1.0.0`,children:`v1.0.0`})}),_jsx(_components.p,{children:`Initial launch.`})]}),_jsxs(`div`,{\"data-filter\":`ts-cli`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`cli-13`,children:`CLI`}),_jsx(Heading,{level:`3`,id:`v1-0-0-3`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/honeyhive-cli/releases/tag/v1.0.0`,children:`v1.0.0`})}),_jsx(_components.p,{children:`Initial launch`})]})]}),`\n`,_jsx(Update,{label:`May 5th, 2026`,id:`may-5th%2C-2026`,children:_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-14`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-0-1-2`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.0.1`,children:`v1.0.1`})}),_jsx(Heading,{level:`4`,id:`fixed-7`,children:`Fixed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[`Default API URL fallback corrected to `,_jsx(_components.code,{children:`https://api.dp1.us.honeyhive.ai`})]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The fallback value for the base API URL in the v1.0.0 release was incorrect. Any callers who relied on the implicit default would fail. The default now points at the host the SDK is built against, though self-hosted / non-default Data Plane deployments will still need to set `,_jsx(_components.code,{children:`HH_API_URL`}),` explicitly. Callers that explicitly set `,_jsx(_components.code,{children:`base_url=`}),` / `,_jsx(_components.code,{children:`server_url=`}),` or `,_jsx(_components.code,{children:`HH_API_URL`}),` are unaffected.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`deprecated-4`,children:`Deprecated`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[_jsx(_components.code,{children:`project`}),` argument and `,_jsx(_components.code,{children:`HH_PROJECT`}),` environment variable`]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`The SDK’s public surface has accepted `,_jsx(_components.code,{children:`project=`}),` on `,_jsx(_components.code,{children:`HoneyHive()`}),` and `,_jsx(_components.code,{children:`HoneyHiveTracer.init()`}),` and the `,_jsx(_components.code,{children:`HH_PROJECT`}),` env var for historical reasons. These arguments and env vars have no influence on the backend and they were maintained purely for backwards compatibility of callers. This release sweeps the stale references out of the example scripts, README, and docstrings, and adds a `,_jsx(_components.code,{children:`DeprecationWarning`}),` at the remaining call sites that previously didn’t emit one. The argument and the `,_jsx(_components.code,{children:`HH_PROJECT`}),` env var alias `,_jsx(_components.strong,{children:`remain`}),` accepted for backwards compatibility and will be removed in v2.0.`]}),`\n`]}),`\n`]}),`\n`]})]})}),`\n`,_jsx(Update,{label:`May 4th, 2026`,id:`may-4th%2C-2026`,children:_jsxs(`div`,{\"data-filter\":`python-sdk`,className:`hh-update-entry`,children:[_jsx(Heading,{level:`2`,id:`python-sdk-15`,children:`Python SDK`}),_jsx(Heading,{level:`3`,id:`v1-0-0-4`,children:_jsx(_components.a,{href:`https://github.com/honeyhiveai/python-sdk/releases/tag/v1.0.0`,children:`v1.0.0`})}),_jsx(_components.p,{children:`First stable GA release of the HoneyHive Python SDK. Public APIs follow semver from this release forward — breaking changes only on major versions, additive on minor and patch.`}),_jsxs(_components.p,{children:[`The changes below are relative to `,_jsx(_components.code,{children:`1.0.0rc22`}),`. For the full picture of what shipped during the release-candidate cycle, see the `,_jsx(_components.code,{children:`1.0.0rc1`}),`–`,_jsx(_components.code,{children:`1.0.0rc22`}),` entries.`]}),_jsx(Heading,{level:`4`,id:`changed-6`,children:`Changed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`\n`,_jsx(_components.p,{children:_jsxs(_components.strong,{children:[`Tracing: `,_jsx(_components.code,{children:`skip_backend_session_creation=True`}),` now works without an explicit `,_jsx(_components.code,{children:`session_id`})]})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Previously the flag only opted out of the init-time backend roundtrip when a `,_jsx(_components.code,{children:`session_id`}),` was also supplied. Setting the flag alone is now supported: the SDK skips the roundtrip, does not mint a UUID, and per-request `,_jsx(_components.code,{children:`create_session(session_id=..., skip_api_call=True)`}),` calls emit spans that ingestion materializes into Session rows on first arrival.`]}),`\n`]}),`\n`]}),`\n`,_jsxs(_components.li,{children:[`\n`,_jsx(_components.p,{children:_jsxs(_components.strong,{children:[`Tracing: `,_jsx(_components.code,{children:`create_session(skip_api_call=True)`}),` propagates `,_jsx(_components.code,{children:`session_name`}),` into baggage`]})}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`When `,_jsx(_components.code,{children:`session_name`}),` is supplied, it now travels with `,_jsx(_components.code,{children:`session_id`}),` across async / threaded boundaries.`]}),`\n`]}),`\n`]}),`\n`]}),_jsx(Heading,{level:`4`,id:`removed-4`,children:`Removed`}),_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[_jsxs(_components.strong,{children:[_jsx(_components.code,{children:`DeleteMetricQuery`}),` removed from `,_jsx(_components.code,{children:`honeyhive.models`})]}),`\n`,_jsxs(_components.ul,{children:[`\n`,_jsxs(_components.li,{children:[`Internal query-params type that was exported but unused — no public method accepted or returned it. The public `,_jsx(_components.code,{children:`client.metrics.delete(id=...)`}),` signature is unchanged.`]}),`\n`]}),`\n`]}),`\n`]})]})})]})}function MDXContent(props={}){const{wrapper:MDXLayout}={..._provideComponents(),...props.components};return MDXLayout?_jsx(MDXLayout,{...props,children:_jsx(_createMdxContent,{...props})}):_createMdxContent(props)}return{FilterButton,applyFilter,Filter,default:MDXContent};function _missingMdxReference(id,component){throw new Error(`Expected `+(component?`component`:`object`)+\" `\"+id+\"` to be defined: you likely forgot to import, pass, or provide it.\")}","frontmatter":{},"scope":{"config":{},"pageMetadata":{"title":"Clients","description":"Browse release notes for the HoneyHive Python SDK, TypeScript API SDK, TypeScript Control Plane SDK, and CLI. Stay current with new features, bug fixes, and breaking API changes.","mode":"wide","icon":"cube","href":"/v2/changelog/clients"}}},"mdxSourceWithNoJs":null,"noJsSplices":[[148,2703,")=>null;const applyFilter=()=>null;const Filter=()=>null;fun"]]},"mdxExtracts":{"tableOfContents":[{"title":"October 30th, 2026","slug":"october-30th%2C-2026","depth":1,"children":[]},{"title":"October 27th, 2026","slug":"october-27th%2C-2026","depth":1,"children":[]},{"title":"October 5th, 2026","slug":"october-5th%2C-2026","depth":1,"children":[]},{"title":"September 29th, 2026","slug":"september-29th%2C-2026","depth":1,"children":[]},{"title":"September 22nd, 2026","slug":"september-22nd%2C-2026","depth":1,"children":[]},{"title":"September 17th, 2026","slug":"september-17th%2C-2026","depth":1,"children":[]},{"title":"August 14th, 2026","slug":"august-14th%2C-2026","depth":1,"children":[]},{"title":"August 4th, 2026","slug":"august-4th%2C-2026","depth":1,"children":[]},{"title":"August 3rd, 2026","slug":"august-3rd%2C-2026","depth":1,"children":[]},{"title":"July 21st, 2026","slug":"july-21st%2C-2026","depth":1,"children":[]},{"title":"July 20th, 2026","slug":"july-20th%2C-2026","depth":1,"children":[]},{"title":"June 26th, 2026","slug":"june-26th%2C-2026","depth":1,"children":[]},{"title":"June 24th, 2026","slug":"june-24th%2C-2026","depth":1,"children":[]},{"title":"June 18th, 2026","slug":"june-18th%2C-2026","depth":1,"children":[]},{"title":"June 11th, 2026","slug":"june-11th%2C-2026","depth":1,"children":[]},{"title":"June 4th, 2026","slug":"june-4th%2C-2026","depth":1,"children":[]},{"title":"May 29th, 2026","slug":"may-29th%2C-2026","depth":1,"children":[]},{"title":"May 22nd, 2026","slug":"may-22nd%2C-2026","depth":1,"children":[]},{"title":"May 21st, 2026","slug":"may-21st%2C-2026","depth":1,"children":[]},{"title":"May 19th, 2026","slug":"may-19th%2C-2026","depth":1,"children":[]},{"title":"May 15th, 2026","slug":"may-15th%2C-2026","depth":1,"children":[]},{"title":"May 11th, 2026","slug":"may-11th%2C-2026","depth":1,"children":[]},{"title":"May 5th, 2026","slug":"may-5th%2C-2026","depth":1,"children":[]},{"title":"May 4th, 2026","slug":"may-4th%2C-2026","depth":1,"children":[]}],"codeExamples":{}},"jsLinks":["/_mintlify/custom/honeyhiveai/295d4d275d20dba2/leadfeeder.js"],"dynamicCss":".mint-flex {\n display: flex\n}\n.mint-inline-flex {\n display: inline-flex\n}\n.mint-cursor-not-allowed {\n cursor: not-allowed\n}\n.mint-items-center {\n align-items: center\n}\n.mint-overflow-x-auto {\n overflow-x: auto\n}\n.mint-whitespace-nowrap {\n white-space: nowrap\n}\n.mint-rounded-l-xl {\n border-top-left-radius: 0.75rem;\n border-bottom-left-radius: 0.75rem\n}\n.mint-rounded-r-xl {\n border-top-right-radius: 0.75rem;\n border-bottom-right-radius: 0.75rem\n}\n.mint-bg-gray-950\\/5 {\n background-color: rgb(3 7 18 / 0.05)\n}\n.mint-px-4 {\n padding-left: 1rem;\n padding-right: 1rem\n}\n.mint-py-1\\.5 {\n padding-top: 0.375rem;\n padding-bottom: 0.375rem\n}\n.mint-text-sm {\n font-size: 0.875rem;\n line-height: 1.25rem\n}\n.mint-font-medium {\n font-weight: 500\n}\n.mint-text-gray-950\\/70 {\n color: rgb(3 7 18 / 0.7)\n}\n.mint-text-white {\n --tw-text-opacity: 1;\n color: rgb(255 255 255 / var(--tw-text-opacity, 1))\n}\n.mint-transition-colors {\n transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;\n transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n transition-duration: 150ms\n}\n.hover\\:mint-bg-gray-950\\/10:hover {\n background-color: rgb(3 7 18 / 0.1)\n}\n.dark\\:mint-bg-white\\/5:is(.dark *) {\n background-color: rgb(255 255 255 / 0.05)\n}\n.dark\\:mint-text-white\\/70:is(.dark *) {\n color: rgb(255 255 255 / 0.7)\n}\n.dark\\:hover\\:mint-bg-white\\/10:hover:is(.dark *) {\n background-color: rgb(255 255 255 / 0.1)\n}","subdomain":"honeyhiveai","siteName":"HoneyHive AI Docs","title":"Clients - HoneyHive AI Docs","relatedPagesConfig":{"enabled":true,"limit":3,"mode":"automatic"},"navMaps":{"firstHrefInVersion":{},"firstHrefInLanguage":{}},"lastModified":"2026-10-30T22:44:43.768Z","originalFileLocation":"v2/changelog/clients.mdx"},"headHtml":"\u003cscript data-mint-head>(function(){var d=document.documentElement,p=null;if(!p){try{p=localStorage.getItem(\"isDarkMode\")}catch(e){}if(p==='true')p='dark';else if(p==='false')p='light';if(p!=='light'\u0026\u0026p!=='dark'\u0026\u0026p!=='system')p=\"light\"}var r=p==='system'?(matchMedia(\"(prefers-color-scheme: dark)\").matches?'dark':'light'):p;d.classList.remove(r==='dark'?'light':'dark');d.classList.add(r);d.setAttribute(\"data-theme-preference\",p)})();\u003c/script>\u003cmeta data-mint-head name=\"description\" content=\"Browse release notes for the HoneyHive Python SDK, TypeScript API SDK, TypeScript Control Plane SDK, and CLI. Stay current with new features, bug fixes, and breaking API changes.\">\u003cmeta data-mint-head name=\"application-name\" content=\"HoneyHive AI Docs\">\u003cmeta data-mint-head name=\"generator\" content=\"Mintlify\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/honeyhiveai/XiZ-DZ6IaFrWYjlm/_generated/favicon/android-chrome-192x192.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"apple-touch-icon\" type=\"image/png\" sizes=\"180x180\" href=\"/mintlify-assets/_mintlify/favicons/honeyhiveai/XiZ-DZ6IaFrWYjlm/_generated/favicon/apple-touch-icon.png\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/honeyhiveai/XiZ-DZ6IaFrWYjlm/_generated/favicon/favicon-16x16.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/honeyhiveai/XiZ-DZ6IaFrWYjlm/_generated/favicon/favicon-32x32.png\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/honeyhiveai/XiZ-DZ6IaFrWYjlm/_generated/favicon/favicon.ico\" media=\"(prefers-color-scheme: light)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"192x192\" href=\"/mintlify-assets/_mintlify/favicons/honeyhiveai/XiZ-DZ6IaFrWYjlm/_generated/favicon-dark/android-chrome-192x192.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href=\"/mintlify-assets/_mintlify/favicons/honeyhiveai/XiZ-DZ6IaFrWYjlm/_generated/favicon-dark/favicon-16x16.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/mintlify-assets/_mintlify/favicons/honeyhiveai/XiZ-DZ6IaFrWYjlm/_generated/favicon-dark/favicon-32x32.png\" media=\"(prefers-color-scheme: dark)\">\u003clink data-mint-head rel=\"shortcut icon\" type=\"image/x-icon\" href=\"/mintlify-assets/_mintlify/favicons/honeyhiveai/XiZ-DZ6IaFrWYjlm/_generated/favicon-dark/favicon.ico\" media=\"(prefers-color-scheme: dark)\">\u003cmeta data-mint-head property=\"og:title\" content=\"Clients - HoneyHive AI Docs\">\u003cmeta data-mint-head property=\"og:description\" content=\"Browse release notes for the HoneyHive Python SDK, TypeScript API SDK, TypeScript Control Plane SDK, and CLI. Stay current with new features, bug fixes, and breaking API changes.\">\u003cmeta data-mint-head property=\"og:site_name\" content=\"HoneyHive AI Docs\">\u003cmeta data-mint-head property=\"og:url\" content=\"https://docs.honeyhive.ai/v2/changelog/clients\">\u003cmeta data-mint-head property=\"og:type\" content=\"website\">\u003cmeta data-mint-head property=\"og:image\" content=\"https://honeyhiveai.mintlify.app/_mintlify/api/og/v2?division=Documentation\u0026amp;title=Clients\u0026amp;description=Browse+release+notes+for+the+HoneyHive+Python+SDK%2C+TypeScript+API+SDK%2C+TypeScript+Control+Plane+SDK%2C+and+CLI.+Stay+current+with+new+features%2C+bug+fixes%2C+and+bre\u0026amp;theme=0dac4d98fc38691c224052c2\u0026amp;signature=972bf9496d76dab1ce7985b2c08fb62dbff73d3c0acd0e1c17b3f9ed91851ba4\">\u003cmeta data-mint-head property=\"og:image:width\" content=\"1200\">\u003cmeta data-mint-head property=\"og:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"twitter:card\" content=\"summary_large_image\">\u003cmeta data-mint-head name=\"twitter:title\" content=\"Clients - HoneyHive AI Docs\">\u003cmeta data-mint-head name=\"twitter:description\" content=\"Browse release notes for the HoneyHive Python SDK, TypeScript API SDK, TypeScript Control Plane SDK, and CLI. Stay current with new features, bug fixes, and breaking API changes.\">\u003cmeta data-mint-head name=\"twitter:image\" content=\"https://honeyhiveai.mintlify.app/_mintlify/api/og/v2?division=Documentation\u0026amp;title=Clients\u0026amp;description=Browse+release+notes+for+the+HoneyHive+Python+SDK%2C+TypeScript+API+SDK%2C+TypeScript+Control+Plane+SDK%2C+and+CLI.+Stay+current+with+new+features%2C+bug+fixes%2C+and+bre\u0026amp;theme=0dac4d98fc38691c224052c2\u0026amp;signature=972bf9496d76dab1ce7985b2c08fb62dbff73d3c0acd0e1c17b3f9ed91851ba4\">\u003cmeta data-mint-head name=\"twitter:image:width\" content=\"1200\">\u003cmeta data-mint-head name=\"twitter:image:height\" content=\"630\">\u003cmeta data-mint-head name=\"msapplication-config\" content=\"/mintlify-assets/_mintlify/favicons/honeyhiveai/XiZ-DZ6IaFrWYjlm/_generated/favicon/browserconfig.xml\">\u003cmeta data-mint-head name=\"apple-mobile-web-app-title\" content=\"HoneyHive AI Docs\">\u003cmeta data-mint-head name=\"msapplication-TileColor\" content=\"#A56311\">\u003clink data-mint-head rel=\"canonical\" href=\"https://docs.honeyhive.ai/v2/changelog/clients\">\u003clink data-mint-head rel=\"alternate\" type=\"application/xml\" href=\"/sitemap.xml\">\u003clink data-mint-head rel=\"alternate\" type=\"text/markdown\" href=\"/v2/changelog/clients.md\">\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@graph\":[{\"@type\":\"Organization\",\"@id\":\"https://docs.honeyhive.ai/#organization\",\"name\":\"HoneyHive AI Docs\",\"url\":\"https://docs.honeyhive.ai\",\"logo\":{\"@type\":\"ImageObject\",\"url\":\"https://mintcdn.com/honeyhiveai/lWy7JAPdMsmL06l1/logo/light.svg?fit=max\u0026auto=format\u0026n=lWy7JAPdMsmL06l1\u0026q=85\u0026s=419c99bb22ce1e89ae8fb4a87c589528\"}},{\"@type\":\"WebSite\",\"@id\":\"https://docs.honeyhive.ai#website\",\"name\":\"HoneyHive AI Docs\",\"url\":\"https://docs.honeyhive.ai\",\"publisher\":{\"@id\":\"https://docs.honeyhive.ai/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https://docs.honeyhive.ai/v2/changelog/clients#webpage\",\"url\":\"https://docs.honeyhive.ai/v2/changelog/clients\",\"name\":\"Clients\",\"description\":\"Browse release notes for the HoneyHive Python SDK, TypeScript API SDK, TypeScript Control Plane SDK, and CLI. Stay current with new features, bug fixes, and breaking API changes.\",\"dateModified\":\"2026-10-30T22:44:43.768Z\",\"isPartOf\":{\"@id\":\"https://docs.honeyhive.ai#website\"}},{\"@type\":[\"Article\",\"TechArticle\"],\"@id\":\"https://docs.honeyhive.ai/v2/changelog/clients#article\",\"headline\":\"Clients\",\"name\":\"Clients\",\"description\":\"Browse release notes for the HoneyHive Python SDK, TypeScript API SDK, TypeScript Control Plane SDK, and CLI. Stay current with new features, bug fixes, and breaking API changes.\",\"url\":\"https://docs.honeyhive.ai/v2/changelog/clients\",\"mainEntityOfPage\":{\"@id\":\"https://docs.honeyhive.ai/v2/changelog/clients#webpage\"},\"dateModified\":\"2026-10-30T22:44:43.768Z\",\"publisher\":{\"@id\":\"https://docs.honeyhive.ai/#organization\"},\"isPartOf\":{\"@id\":\"https://docs.honeyhive.ai#website\"}}]}\u003c/script>\u003cscript data-mint-head type=\"application/ld+json\">{\"@context\":\"https://schema.org\",\"@type\":\"WebSite\",\"name\":\"HoneyHive AI Docs\",\"creator\":{\"@type\":\"Organization\",\"name\":\"Mintlify\",\"url\":\"https://mintlify.com\"}}\u003c/script>","siteName":"HoneyHive AI Docs","nav":{"global":{},"products":[{"product":"v2","icon":"hive","description":"The next generation of HoneyHive.","tabs":[{"tab":"Documentation","icon":"book-open","pages":[{"title":"Documentation","href":"/v2/introduction/what-is-hhai"}]},{"tab":"SDK Reference","icon":"layer-group","pages":[{"title":"SDK Reference","href":"/v2/sdk-reference/overview"}]},{"tab":"CLI Reference","icon":"terminal","pages":[{"title":"CLI Reference","href":"/v2/cli-reference/getting-started"}]},{"tab":"API Reference","icon":"code","pages":[{"title":"API Reference","href":"/v2/api-reference-autogen/sessions/start-a-new-session"}]},{"tab":"Integrations","icon":"plug","pages":[{"title":"Integrations","href":"/v2/integrations/overview"}]},{"tab":"Cookbooks","icon":"utensils","href":"https://github.com/honeyhiveai/cookbook"},{"tab":"Changelog","icon":"megaphone","pages":[{"title":"Product","description":"See what is new in the HoneyHive platform and UI. Browse product updates, feature launches, and major improvements that affect how you build and monitor AI.","mode":"wide","icon":"cube","href":"/v2/changelog/product"},{"title":"Clients","mode":"wide","icon":"cube","href":"/v2/changelog/clients"},{"title":"Self-Hosting","description":"Review Helm chart updates and release notes for self-hosted HoneyHive. Find upgrade guidance, configuration changes, and migration steps for your deployment.","mode":"wide","icon":"server","href":"/v2/changelog/self-hosting"}]}]},{"product":"v1 (legacy)","icon":"server","description":"Deprecating Nov 5, 2026.","pages":[{"title":"v1 (legacy)","href":"/introduction/what-is-hhai"}]}]},"basePath":"","title":"Clients - HoneyHive AI Docs","buildId":"dpl_9VVk5y1P8k1ATeJguVngy9Q3cmbD","deploymentId":"dpl_9VVk5y1P8k1ATeJguVngy9Q3cmbD","dynamics":[],"chunks":[]}