Skip to main content
APIBug FixComposable IdentityDashboardDeprecationImprovementIntegrationsNQLNew Feature

POST /nql/run will be removed on October 19, 2026

POST /nql/run has been deprecated since August 2026. On October 19, 2026 it stops answering and returns 404. Move your callers to POST /v1/nql/execute before then — the request body is unchanged, but you get a workflow and a run back instead of a job. See the deprecation notice for what differs, and the migration guide for the port.This is the first removal run under our API versioning and breaking change policies, both now published — what counts as a breaking change, how endpoint versions are numbered, and the notice you get before anything stops answering.

Reddit Connector

The Reddit Connector is now in the marketplace under Apps — free, and it installs without an approval step. Its profiles are offered as a delivery destination everywhere the other connectors are: a dataset’s connections, Audience Studio, and Look-alike Studio. Create a profile and click Connect to Reddit to authorize it; a Reddit grant is member-scoped, so one authorization reaches every ad account that account can see. The Opt Out Configuration tab takes a dataset of opted-out users and removes their identifiers from every Reddit Custom Audience your company delivers to. Read more →

A summary header on the dataset details page

Opening a dataset now starts with a summary header that stays put as you move between tabs: name, state, description and tags, all editable in place, plus chips for type, ID, region, query engine and file type, and tiles for record and file counts, last ingest, write mode, snapshot count and expiry. Schema is now the tab you land on. The Overview tab is gone — its editable settings moved to a new Settings tab at the end of the strip, along with lifecycle timestamps, file parsing options, and the extended-stats and data-expiration rows. Read more →

Graph and match report builds run in the background

Build Graph in Graph Studio and Run Report in the match report builder now hand the run to the platform: you get a toast, and the outcome arrives in the Notifications inbox whether or not you stayed on the page. Leaving the builder or reloading no longer loses the run. Start Over is now its own button beside the run action, so you can clear the builder while a build is still going. A run that ends without a dataset now says which way it ended — failed, canceled, terminated, or still going.Two match report fixes come with it. KPI ratios now count identifiers the same way in numerator and denominator, so the figures beneath each card add up to the value on it — regenerate a report before comparing it with an older one. And a report no longer fails outright on an AWS EMR compute pool, where the Demographic & Behavioral Enrichment query used SQL the Spark analyzer rejects.Read more →Dropdowns with thousands of options no longer stall when you open them. A searchable list shows up to 200 matching options at a time and says so under the last row — Showing 200 of 2,400. Type to narrow. — and the search still covers every option, not only the ones on screen.The tab bar’s overflow menu now leads with Close this tab, alongside Close all tabs and Close other tabs, and the tab strip announces that Delete or Backspace closes the focused tab — closing one tab used to mean clicking a ✕ that no keyboard could reach.Two menu fixes: clicking outside a menu, popover, select, autocomplete field or date picker now only closes it, instead of also activating whatever sat under the pointer; and moving from a menu row to the submenu it opens keeps both panels open, at every level of nesting.

Query Builder’s field picker opens instantly on wide schemas

Query Builder’s field picker no longer stalls on sources with several thousand columns — it shows a first batch and adds more as you scroll. Your browser’s find-in-page only reaches the fields loaded so far, so use the picker’s own Search box, which still covers every field in the source. Read more →

The Hugging Face Connector now says why a profile could not be created

Profile creation now names its cause — a token that does not authenticate, is not a user access token, or has no write access — instead of printing “Profile creation failed” twice. Read more →
Bug FixComposable IdentityDashboardData StudioImprovementNew FeaturePlatform

The navigation redesign is complete

Navigation now lives entirely in the rail down the left edge. The rail replaced the old expanding sidebar, carrying everything it used to list behind four icons — Rosetta AI chat, Objects, Builder, and Marketplace — and the last two pieces have followed it there: the Settings menu has moved off the top bar to the foot of the rail, its contents unchanged, and a Notifications inbox now sits beside it. Every row still behaves like a real link, so cmd-click and middle-click open the destination in a new tab.That leaves the top bar to the things that describe your current session: your context selector, model picker, and account menu.

A notifications inbox in the navigation rail

Work you hand to the platform no longer needs you to sit on the page while it finishes. A Notifications inbox now sits at the foot of the navigation rail, carrying a count of what you have not read and listing finished runs newest first, each with its outcome and how long ago it landed. Opening the panel clears the count.The tracking follows the run rather than the page: navigate away and the run is still watched, reload the browser and it is picked up where it left off. Label Studio’s Add Labels write, Data Studio queries, and bulk query uploads all report here. Records are kept for a day and cleared when you sign out.

Addressable Graph Builder

Graph Studio has a new Addressable Graph Builder that turns a resolved identity graph into an audience source dataset — a table activation platforms can match against. A graph tells you which records belong to the same person; it does not tell you how to reach that person. This builder closes that gap.Pick the graph you want to make addressable, then enrich it: fold in datasets whose properties should travel alongside the identity as enrichment attributes, and choose which properties hold the identifiers that reach a person. Those identifiers are used for addressability only — they play no part in the graph-building logic that established identity in the first place. Identifiers are flagged as poor targeting dimensions so they don’t quietly widen the audience table without improving how you can segment it. From there, generate the addressable table and its companion audience table. Read more →

Data Studio queries run in the background

Running a query in Data Studio no longer holds the page. The editor comes back as soon as the platform accepts your statement, so you can write the next query, switch tabs, move to another page, or reload the browser while it works, and the outcome reaches Notifications whether or not you were watching. The case this removes is the worst one: a hand-written CREATE MATERIALIZED VIEW over a large source, which used to sit behind a spinner covering the whole page for as long as the view took to build. While a run is in flight, Preview Results shows a placeholder; when it finishes you get a link to the new dataset, or the reason the run failed.Cancel now releases the page from the run rather than stopping it: the statement is already on the data plane, so it finishes and still reports to Notifications. Forecasts still wait inline, because the estimate is the point of asking and arrives in seconds.

Bulk query uploads now run as a workflow

A bulk query upload in Data Studio is now kicked off as a single workflow rather than executed statement by statement while you wait. Every query in the file is still compiled one by one, and the ones that pass go into one workflow specification, in order, submitted as a single run. The upload panel comes back as soon as that run is accepted, saying how many queries went into it, and the outcome lands in Notifications whether or not you are still on the page.

Label Studio shows which labels are unsaved, and says so before you leave

Picking a label in Label Studio stages it; only Add Labels writes it to the dataset. That distinction used to be invisible, and leaving the page discarded staged picks without a word. Now:
  • Staged picks are marked — a dashed outline and a pencil, with a tooltip reading “Not saved yet — click Add Labels to save this pick.” It reads differently from the tinted ring on an unconfirmed LLM suggestion.
  • Leaving the page warns you, naming Add Labels as the control that saves them.
  • Saving narrates itself — the button names the step it is on, Submitting… then Saving…, updating one notification in place rather than stacking one per stage, and the grid becomes interactive again as soon as the labels land.

Failed actions now report what actually went wrong

When an action failed, the platform frequently answered with a generic “please try again” while discarding the reason it had been given. Those messages now carry the cause through to the notification you see — across dataset and mapping actions, job cancellation, workflows and models, Label Studio, access rules, Rosetta Stone attributes, and the opt-out and profile settings on the Google DV360, PubMatic, Snapchat, TikTok, Trade Desk and Yahoo DSP connectors. Where a failure carries a diagnostic code, that code becomes the message heading with the explanation and suggested fix beneath it, the same treatment NQL execution errors already had.

The context selector drops Database and Schema

Your execution context is now two settings rather than four. The Database and Schema columns are gone, and the pill in the top navigation reads out the data plane and compute pool alone. Neither column was ever selectable — both offered only DEFAULT, and the data plane resolved the database and schema for every operation regardless — so nothing about where your queries run has changed. Read more →

Closing a tab now releases the memory it was holding

A page you opened used to stay in memory for the rest of the browser session whether or not its tab was still open, so ten tabs held over a hundred megabytes between them and closing all ten released none of it. Closing a tab now drops the page it was showing. Background tabs have also stopped polling at full rate — two of them could issue roughly eighty requests a minute between them — and polling now pauses when a tab is off screen and resumes when you return. What you are looking at refreshes exactly as before.

The New Dataset form gets its name field right

Two fixes to the name field. It no longer shows Dataset name is required against a field nobody typed in — the message now appears only when you have typed a name and then cleared it. And a name another dataset already uses is now rejected against the field with Upload blocked, instead of succeeding quietly and leaving you with two datasets listed under the same name and a second whose unique name had been changed to avoid the clash. Names differing only by case count as a collision, and the helper text now reads The name your dataset is listed under. Must be unique.

A comment above CREATE MATERIALIZED VIEW no longer changes how it runs

Data Studio now skips whitespace and comments when deciding whether to submit your NQL verbatim, so a CREATE MATERIALIZED VIEW or EXPLAIN written under a -- or /* */ comment is submitted as written rather than wrapped in a temporary one-day view with a 1,000-row limit — which meant the view you named was never created. A keyword now only counts at the start of the statement, so a query that merely mentions one further down keeps its full action bar.
Bug FixComposable IdentityDashboardImprovementIntegrationsNew FeaturePlatformRosetta StoneWorkflows

RunConversation workflow task

Workflows can now ask an agent a question mid-pipeline. The task runs one turn against the model with the MCP servers you name, waits on a durable timer for the answer, and hands it to later tasks as structured output or prose. Learn more →

Register Snowflake datasets programmatically

One of the most frequent requests from Snowflake customers: register a table or view as a Narrative dataset from your own account, in code, without launching the Native App’s Configuration screen. A new stored procedure does it from a worksheet, the Snowflake SQL API, or a scheduled task — so onboarding a dataset can be scripted and run from the pipeline that already builds the table. A new application role lets you delegate registration without handing over admin rights.The Configuration screen is unchanged and is still the simplest way to register your first dataset. This adds a route rather than replacing one. Learn more →

Opt-out configuration for TikTok, DV360, and PubMatic

The TikTok, Google Display & Video 360 (Customer Match), and PubMatic connector pages each have an Opt Out Configuration tab, matching the shape already used by Magnite, Yahoo DSP, and Snapchat. Connect an opt-out dataset without leaving the connector page.What a removal reaches differs by connector — TikTok covers every audience the profile delivers to, DV360 the Customer Match audiences under one account, and PubMatic every segment under the profile’s Data Provider ID. See the TikTok, Google DV360, and PubMatic references.

Compute pool improvements

  • Set your company’s own default. The data plane page now separates your company’s default pool from the one the plane’s owner set for everyone, and yours takes precedence. Setting it no longer requires owning the plane.
  • Read the effective default from the API. Every compute pool in a response carries a default_source naming which level of the resolution chain made it the default.
  • Distinct capacity per fixed EMR tier. x_small through large now size their Spark containers explicitly, so each step up is a real one — medium runs 3 executors instead of 1, and large runs 7 instead of 3, at the same cost. x_small and small drop to a single node.
  • Dataset detail names the pool. An archived pool, or one owned by someone else on a shared data plane, now shows its name instead of a raw identifier.
Learn more →

MCP tool improvements

  • Compute pool defaults. The data plane tools render each pool’s default_source, and narrative_datasets_describe reports a dataset’s own default pool — the one that actually wins for jobs against it.
  • Attribute derivation edges. The attribute tools render each attribute’s derivation edges, and now advise mapping a column to the attribute closest to the root of the graph, since one mapping there covers every derived variant.
  • Job timings. The job tools return dequeued_at, attempted_at, attempt_version, and data_plane_id, and explain how to separate queue time from execution time.
Learn more →

Attribute derivation graph endpoint

GET /attributes/{name_or_id}/derivation-graph returns every derivation relationship around an attribute in one call — what it can be derived from, what can be derived from it, the rules connecting them, and each route’s cost and fidelity flags. A consumer deciding what to map can see that mapping an attribute’s canonical form unlocks its derived variants. Learn more →

Match reports cover every identifier type the supplier holds

A Marketplace Match Report used to scan the supplier only for the identifier types you matched on, hiding the rest — including the mobile advertising IDs or compound identifiers that were the reason to evaluate that supplier. The supplier scan is now unfiltered, so the Enrichment card, the Identifier Type Summary, and the ID Enrichment and IDs Per Person KPIs cover everything the supplier holds for your matched people.Your match counts are unchanged, but both KPIs rise. Re-run an existing report to pick up the wider coverage. Learn more →

Label Studio shows the full text you are labeling

String is now the widest column and wraps onto as many lines as the row needs, and long options in the Label dropdown wrap rather than truncate — so values longer than a short phrase are finally readable.

Failed inference jobs name the model

A failed inference job now names the model the run selected and links to its listing, where the catalog reports whether your installed data planes can run it. The previous message gave you a job id and a generic documentation link.

Heavy sessions no longer trip a request limit in the app

The app counted roughly 150 requests per five minutes per network address, so polling screens hit the ceiling on their own and everyone on an office network shared one budget. That limit is gone; API clients are still rate limited at the edge.

Cached mappings are now scored in mapping evaluations

Cached mappings were left out of evaluations entirely, so a dataset that leaned on them looked far less covered than it was. They are now scored alongside value and object mappings. Learn more →

Classifier Studio clears the builder once a training run is queued

Submitting a run used to leave the configuration on screen, so clicking Train Classifier again queued a duplicate. Classifier Studio now returns to its start view once a run is accepted, and clears nothing when the platform rejects one.

Connections keep the profile you picked for the connector

Switching connectors without closing the configuration panel could save the connection against the profile belonging to the connector you started on. A connection is now always saved against the account you chose for it.

Dataset file downloads no longer fail on Iceberg tables written with s3a:// paths

The internal S3 URI parser rejected the Hadoop s3a:// scheme that some Iceberg tables recorded their file locations with. It now treats s3://, s3a://, and s3n:// as the same object.

Ranked, fuzzy search in the command palette

The command palette no longer needs the letters you type to sit next to each other. ds finds Data Studio, dstud finds it too, and spaces are ignored, so it makes no difference whether you type data studio or run the two words together.Results now come back ranked rather than in menu order. A match on a command’s name outranks a match on its description, letters matched at the start of a word count for more than ones landing mid-word, and the shorter of two equally good matches wins — so job puts Go to Jobs at the top instead of a longer destination that merely contains the same letters. Commands you run often, and recently, get a small lift on top of that, enough to separate the ones you use from the ones you never have without pushing a poor match above a good one. The Recent list is drawn from the same history.Destinations also answer to the words people actually type for them: nql or query editor for Data Studio, match reports for Reports, and ai or assistant for Rosetta AI Chat.

Edit tags on a dataset’s detail page

The Tags row on a dataset’s detail page can now be edited in place: the field accepts what you type, and your tags save the way every other row on the page does. Tags the platform sets for itself sit beside the field as plain chips — visible, but not yours to remove.

Enterprise login settings stay reachable for your whole session

If you sign in through Enterprise Login, Settings > Enterprise Login Settings no longer disappears part-way through your session. The platform now remembers that your session is an enterprise one for as long as you stay signed in, rather than inferring it from a shorter-lived login cookie, and it renews the enterprise login behind the page whenever you load the app or return to the tab.When that login has genuinely expired, the page says so and offers a Sign in again button that brings you back to where you were — instead of leaving the organization settings, member management, and SSO panels showing an error with no way forward. The page is also reachable only by the account that signed in through Enterprise Login.See SSO Configuration for the full administrator walkthrough.

One model picker for every AI feature

The model picker in the top bar now chooses the model — and how hard it thinks — for every AI-powered action in the platform, not just for new Rosetta AI Chat conversations. The next message in a chat you already have open, a generated attribute or dataset description, an auto-labeling pass: all of them run on whatever the picker holds. The confirm button reads Apply, and the choice takes effect on the very next call.The picker also offers every model your data plane can run rather than three fixed tiers, listed strongest first. Switching data planes re-derives the list, and a model the new one cannot run gives way to that platform’s default without discarding your choice — switch back and your model returns. The Thinking column still appears only for models that accept a thinking level, so you can never pick a combination the run would reject.Because the model is now chosen in one place, the per-conversation model and thinking controls have been removed from the chat toolbar; the settings popover there still covers MCP servers and tools. In Label Studio, the auto-label panel lists models by name instead of by tier, so you choose exactly which models take part in a consensus pass.

A navigation rail replaces the sidebar

The platform’s navigation has moved into a rail down the left edge of every page, and the old expanding sidebar is gone. Four icons carry everything it used to list:
  • Rosetta AI chat opens a chat tab directly.
  • Objects holds the things you already have — datasets, queries, reports, and access rules; attributes and normalized datasets; models and audiences; and jobs, workflows, and installed apps.
  • Builder holds the studios you work in — Data Studio and Graph Studio, Audience Studio and Lookalike Studio, and the model studios, including Cached Mapping Studio.
  • Marketplace browses data, apps, models, data plane providers, MCP servers, and skills, with your own models and apps one level in.
Hovering, clicking, or arrowing onto an icon opens its menu, and the icon that owns the page you are on is marked as the current one. Jobs and workflows now sit with the objects you inspect rather than in a category of their own, and Cached Mapping Studio sits with the other model studios. The top bar is unchanged: your context selector, model picker, settings, and account menu are all still there.

Connecting an opt-out dataset now asks you to confirm

Connecting a dataset to a connector’s opt-out feed opts every identifier in it out of that connector, and the removal cannot be undone. That step now opens a confirmation naming the dataset and the connector before anything is created, so a dataset picked by mistake costs a click rather than an audience.The confirmation appears on the Opt Out Configuration tab of the Meta, Google DV360, Magnite, PubMatic, Snapchat, and TikTok connectors, and when you connect a dataset to a connector’s opt-out interface from the dataset itself.

PubMatic profiles no longer ask for destination paths

Creating or editing a PubMatic profile no longer asks for a Delivery Destination or a Taxonomy Destination. Both paths were required before you could save, and neither is part of a profile any more — setting up PubMatic now needs only your Data Provider ID and your SFTP credentials.
APIBreaking ChangeBug FixComposable IdentityDashboardImprovementIntegrationsNew FeatureWorkflows

Sample redaction

A dataset’s sample can now carry a redaction: a record of which columns hold personal data, what kind of personal data each one holds, and how each one is masked. Narrative’s privacy model can produce that record for you, or you can write it yourself.

Reading and writing a redaction

Four new endpoints put redactions on the public API. GET, PUT, and DELETE /datasets/{dataset_id}/redaction read, write, and delete the redaction, and GET /datasets/{dataset_id}/sample/redacted returns the sample with the redaction applied. Deleting a redaction leaves the underlying sample untouched.Seven masking strategies are available (keep_email_domain, keep_initials, keep_last_digits, keep_network_prefix, keep_two_decimal_places, keep_year, mask_everything) and nine personal-data categories (account_number, other, private_address, private_date, private_email, private_person, private_phone, private_url, secret). Both sets are closed — a redaction naming anything outside them is refused with a 400. A single redaction may name at most 20,480 columns. Customers may write redactions for their own datasets; only Narrative may record the model_version that produced a redaction. See Redacting dataset samples for the full walkthrough.

Sampling and redacting in one request

POST /v2/datasets/{dataset_id}/request-sample samples a dataset and asks the privacy model to mark which columns hold personal data and how each should be masked, all in one call. The response is the created workflow and the id of its run. When the run completes, read the model’s decisions with GET /datasets/{dataset_id}/redaction and the masked rows with GET /datasets/{dataset_id}/sample/redacted.The /v2 route requires the caller’s own company to own the dataset — a data-plane collaborator gets a 404 — and it triggers a workflow that draws on the owner’s credit limit, so a company over its limit gets a 403. The v1 POST /datasets/{dataset_id}/request-sample endpoint is unchanged: it samples without redacting, admits collaborators, enqueues a job under the owner’s company, and spends no credit.

Redacting from inside a workflow

The new GenerateSampleRedaction task runs the same classification from within a workflow. Chain it after CreateDatasetSample to keep a dataset’s redaction current whenever its sample is regenerated.Specify the target dataset by either datasetId or datasetName (exactly one). The dataset must already have a sample, and the model is shown at most 100 rows of it. The task replaces the stored redaction rather than merging with it, so any redaction previously uploaded through PUT /datasets/{dataset_id}/redaction is overwritten. Narrative’s own columns (_rosetta_stone and other reserved fields) are excluded from classification, so ingestion metadata is never flagged as personal data. The output carries datasetId, columnsExamined, columnsRedacted, and modelVersion — the redaction itself is written directly to the dataset and served by GET /datasets/{dataset_id}/redaction by the time the task returns.

Configure Snapchat opt-outs from the connector page

The Snapchat Connector page now has an Opt Out Configuration tab, matching the shape already used by Magnite and Yahoo DSP. The tab lists the datasets currently feeding opt-outs into Snapchat and lets you connect a new one without leaving the connector page. Only datasets that carry a Snapchat-readable identifier column and aren’t already wired up are offered when you create a connection, and the connection binds to a Snapchat profile for bookkeeping — an opt-out names no ad account and no audience, and the connector resolves a token per segment at delivery time. See the Snapchat Connector reference for the full setup.

Pick a Rosetta Chat model and thinking level from the top bar

The top navigation now includes a Rosetta Chat model picker. Open it and you get a two-column menu: choose a model on the left, and the right column shows only the thinking levels — Default, Off, Low, Medium, or High — that model actually offers. Unsupported model-and-thinking combinations never appear, so you can’t pick a setting the run will reject. Your top-bar choice sets what new Rosetta conversations start from; conversations already open keep the model and thinking level they were created with.Inside a conversation, the chat model bar picks up the same Thinking level control, and it only renders on models that support thinking. “Default” leaves the model’s own behavior alone, “Off” explicitly disables reasoning, and Low / Medium / High map to the provider’s effort levels. The setting rides each run, so you can dial reasoning up on a hard question and back down for the next turn without leaving the chat.

Jump to any sidebar destination from the command palette

The command palette (⌘K / Ctrl+K) now offers a Go to… command for every destination in the side navigation, not just a hand-picked seven. Datasets, Models, Rosetta Chat, the studios, settings pages, and everything else the sidebar reaches are all searchable and reachable from the palette. Commands react to your context: switching the data plane from AWS to Snowflake makes Classifier Studio, Label Studio, and Cached Mapping Studio appear in the open palette without closing it or retyping your search. Where two destinations share a leaf label — for example Models under both My Models and Marketplace — the palette shows the full path so you can tell them apart.The four Graph Studio tab shortcuts (Graph Builder, Edge Builder, Match Report Builder, Addressable Graph Builder) and Create New Dataset are no longer in the palette while the underlying page-contributed command API is finished; they will return in a follow-up release.

Label Studio: literal source input, and the target attribute picker renders again

The Source Input step of the Label Studio project builder now supports Literal mode alongside Fields mode. Pick Literal in the mode selector and enter a single string; every row queued for labeling will present that exact text as its source input, with no field selection, concatenation strategy, or preview cards to configure.Typing live-updates the draft: the value you enter is emitted as-is, and the underlying NQL expression is a single-quoted string literal with embedded single quotes doubled (matching the escaping convention used elsewhere in the platform). Backslashes pass through unchanged because NQL does not treat \ as an escape character. An empty literal is allowed and surfaces inline helper text so you know the field is intentionally blank. Switching away from Literal and back does not carry Fields-mode selections into the emitted source input.Separately, a regression introduced in v2026.8.16 that made creating a Label Studio project impossible is fixed. The Target attribute view rendered empty, so no attribute could be selected and the project could not be saved — the picker’s AttributeSelect component crashed during setup because a watch was registered before the groupedAttributeOptions value it read, hitting the temporal dead zone on first evaluation. The declaration order is fixed and the attribute list, allowed values panel, and Create project button work as before.

MCP narrative_classifier_train tool

The MCP server now exposes classifier training as narrative_classifier_train. The tool submits a training run against a dataset that is materialized on a Snowflake data plane and returns the job id so the agent polls completion with narrative_jobs_describe. The trained model is saved to the Snowflake ML Registry in the data plane’s account.Arguments mirror POST /v1/model-training/train-classifier: a dataset_id, a label_column, a model_name, and a feature_inputs array. Each feature entry picks one of text, categorical, numeric, or embedding and takes its own tuning block. The model object picks the algorithm (defaults to logistic regression) from logistic_regression, random_forest, gradient_boosting, svm, or naive_bayes. Column references and every top-level key are validated against the dataset’s schema before the job is enqueued, so most malformed requests fail the tool call rather than the job. See narrative_classifier_train for the full parameter list.

Stop an agent conversation from waiting on a handle

Agent conversations can now drop a claim on a handle without stopping the job behind it. POST /agents/conversations/{id}/waitables/{handle}/cancel wakes a run parked on that handle, tells it the handle reads stopped_waiting, and lets the turn finish normally, so the conversation is free to take its next turn immediately. The job the handle points at keeps running exactly as before — cancellation drops the wait, not the work. Cancelling the same handle twice returns the original cancelled_at, and a later wait_for on the dropped handle reads the abandoned claim rather than waiting again. See Waiting for Work to Finish for the full behavior.

Attributes API: cooccurrence metadata hides private attributes

GET /attributes/{name_or_id} previously embedded each entry in metadata.attribute_cooccurrence as a full attribute record with no viewability check, so an unauthenticated caller could enumerate private attributes — owner company_id and collaborators included — through any public attribute’s cooccurrence list. The endpoint now filters the cooccurrence list by the same view permission as every other attribute read, so entries the caller cannot view no longer appear.Each surviving entry’s attribute field is also slimmed to a fixed shape (id, name, type, description, display_name, tags) for every caller, owners included. company_id and collaborators no longer appear anywhere under metadata.attribute_cooccurrence[].attribute. cooccurrence_pct values are unchanged. Update any consumer that reads owner fields from cooccurrence entries — fetch the attribute directly by id or name instead.

OpenAPI spec corrections across Webhooks, Access Tokens, Attributes, and /whoami

Four public OpenAPI specs were out of step with what the services actually send and receive. All four are now accurate. No production behavior changed in any of them — the spec is the piece that moved — but anyone generating a client from these specs will see type changes. Regenerate your SDK client to pick up the corrections.

Webhooks

The Webhook Event Reference now reflects the corrected shape:
  • Subscription responses use snake_case. The fields formerly documented as companyId, jobIds, jobStates, jobTypes, createdAt, and updatedAt are company_id, job_ids, job_states, job_types, created_at, and updated_at.
  • company_id and app_id are integers, not strings.
  • The three job filters — job_ids, job_states, and job_types — are returned as objects of shape { "values": [...] }, not bare arrays. Requests still take bare arrays.
  • app_id and name are first-class fields on the subscription response.
  • The states enum now includes scheduled.
  • job_types is an open string up to 255 characters, not a closed enum.
  • expires_at on webhook subscriptions no longer claims format: date-time.
The request Narrative POSTs to your subscription URL is now described in the spec for the first time. Each delivery carries an X-Narrative-Secret header holding the subscription’s secret, and the body follows a four-field envelope (id, created_at, type, data). Verify the header before processing the body and key your idempotency store on the envelope id.

Access Tokens

  • The access_token credential now appears only on the response to POST /access-tokens/tokens, where it is actually returned. GET, PUT, and LIST responses no longer advertise a field they never send. The two shapes are now distinct components: CreatedAccessToken (which carries the credential) and AccessTokenMetadata (which does not). The AccessTokenWithoutCredentials component is removed.
  • Validation rules are now documented on the operations: name is 1 to 256 characters, admin cannot be granted, and expires_at must be 1 to 365 whole days out. Expiry is stored with day granularity, so a token requested for 36 hours expires in 24.

/whoami

The /whoami spec carried a stale 21-value copy of the permission resource enum, so generated clients rejected 22 of the resources the endpoint actually returns. Both /whoami and /access-tokens now reference a single shared PermissionResource enum with all 50 values, and the two specs can no longer drift apart. Consumers who previously typed Permission.resource as a bare string will see it become a union of the 50 valid values.The spec also now types last_updated_at and last_updated_by as nullable on the /whoami response. An account that has never been updated sends both keys with a null value, and only created_by allowed a null before. The four field descriptions previously called these company timestamps; they describe the user account and now say so.

Attributes

GET /attributes documents the Page envelope the endpoint returns, GET /attributes/references documents its {records} wrapper, POST /attributes documents its 201 response, and every operation documents its reachable 4xx codes. PUT /attributes/{name_or_id} is documented for the first time.The read operations declare optional bearer auth: a token decides per record whether the response uses the owned shape (with company_id and collaborators) or the shared shape (without them). Request schemas no longer promise defaults the decoder never applies — tags is required. Inclusion and exclusion collaborators send company_ids rather than companies, and self-references are written $this (the previously documented $self is rejected with 400).

Context selector rebuilt for clarity on every screen

The workspace context selector in the top navigation has been rebuilt on the platform’s standard multi-level menu. The flyout now shows all four columns (data plane, compute pool, database, schema) at once instead of clipping the fourth, and the row you selected uses a distinct fill from the row under your pointer so the two are easy to tell apart on both light and dark surfaces. Rows are edge-to-edge and respond to the pointer instantly, the Cancel and Accept buttons are now filled instead of faint outlines against the dark panel, and the Narrative logo renders correctly on the pill and in the data plane column instead of appearing as a blank square.Below 1400px viewport width the selector no longer disappears from the top bar. It compacts to the data plane name and still opens the full four-column panel, so your query context stays visible and reachable at narrower window sizes.Changing your data plane no longer leaves a downstream column standing empty when its previous pick is gone: any single-choice column with nothing selected automatically takes its first row. The pill also refreshes correctly when you switch away from a data plane, so it never keeps showing the previous plane’s name.

Settings menu in the top navigation

The platform’s settings destinations now live in a single Settings menu beside the user menu in the top nav, instead of being scattered through the sidebar. The menu carries five links under two headings: Data Planes and Sources under Infrastructure, and API Keys, Secret Sharing, and Enterprise Login Settings under Access. Enterprise Login Settings only appears for B2B companies. Jobs and Workflows stay in the sidebar, since they are operational views rather than settings. Each row behaves like a real link — cmd-click and middle-click open the destination in a new tab, and hovering previews the URL in the browser’s status bar.

API key permissions drawer now offers every resource in the contract

The Settings > API Keys drawer built its permission matrix from a hand-written list that had drifted to 40 of the API contract’s 50 resources, so a key could not be scoped to the other ten and nothing on screen said so. Rows are now derived from the API’s own schema, and the drawer renders all 50 resources across five categories. Ten resources are newly selectable, including attribute_derivations, internal_reports, and persistent_permissions. See the Permissions reference for the full list.

Edge Builder emits bare attribute names for object-shaped attributes

Edge Builder was labeling object-shaped attributes with their property path, so a customer edge went out as sha256_hashed_email.value while supplier edges carried sha256_hashed_email. Match reports join the two sides on exact string equality of that label, so the join matched nothing and the report showed a legitimate-looking 0% match rate. Edge Builder now writes the bare attribute name for every leaf you pick, and hydration recovers the specific leaf from the mapping’s target_id expression so existing edges load onto the right property.Mappings already saved with a .value label are not rewritten automatically. Re-save them through Edge Builder to restore matching against supplier edges.

Classifier Studio trains on datasets without an ID column

Classifier Studio used to send id_column: "ID" on every train request, so the v1 train-classifier endpoint returned id_column 'ID' references column 'ID' which does not exist in the dataset and the submit failed with a 400 whenever the selected dataset had no literal ID column. Training now only sets id_column when the dataset’s schema actually contains an ID column, and omits the field otherwise, so Classifier Studio submits successfully against any Snowflake dataset regardless of its column names.

Dialogs stay clickable when the page behind them is blocked

Dialogs and drawers now respond to clicks even when they are mounted inside a region that has disabled pointer events. The bug was easiest to hit in Data Studio: when a query failed, the “Something went wrong…” dialog appeared looking normal but its close button and buttons did nothing, because the dialog was a DOM descendant of the loading wrapper that sets pointer-events: none on its contents. The fix restores interactivity on every TackleboxDialog and TackleboxDrawer in the dashboard, so any dialog opened over a busy or blocked region behaves correctly.
APIBreaking ChangeBug FixComposable IdentityDashboardData StudioDeprecationImprovementIntegrationsNQLNew FeatureRosetta Stone

Agents can wait for long-running work

An agent asked to “run this and tell me when it’s done” used to have no way to wait: it re-checked the job’s status every turn and ran out of turns before the work finished. Agent Conversations now pause instead.
  • Waiting on Narrative work. The agent registers a job with job_monitor, which hands back an opaque handle (wt_…) — a claim ticket for work that outlives the call that started it — then waits on up to 8 handles at once with wait_for, for up to 12 hours. The run is paused until the work finishes or the timeout passes, holding no compute and making no model calls in between, so a four-hour job costs no more to wait for than a four-second one. A failed job is a finished wait, not a failed run. Handles work only in the conversation they were issued in and last 24 hours. While a run is paused, live.messages on GET /agents/runs/{id} shows what it’s waiting for.
  • Pausing between checks. For work Narrative cannot observe, such as a third-party ticket, sleep pauses the run for 1 second to 1 hour as a durable timer that survives a restart, so the agent can re-check without hot-looping. sleep, job_monitor, and wait_for are always available on top of mcp_servers[] and tools[], and their names are reserved.
  • One run at a time. Starting a run while another is still pending or running on the same conversation now returns 409 Run Already In Progress, naming the active run and what it’s waiting on — previously both runs started and the loser died at finalize with ConcurrentConversationAppend after paying for a workflow. Re-posting a client_op_id you’ve already used is still idempotent and returns that run’s row.
  • max_iterations now defaults to 10 (was 3), since starting a job, taking a handle, and waiting on it are three iterations of the budget on their own. Existing conversations keep whatever value they were created with.
Read more →

Conversions API Connectors for Pinterest, TikTok, PubMatic, and Yahoo

Following the Meta Conversions API Connector, you can now send conversion events — purchases, signups, leads, and more — to four more ad platforms for improved attribution and campaign optimization. Each is a second interface of the platform’s existing audience connector, so you can reuse the profiles and accounts you already have.
  • Pinterest — deliver web, app, and offline events per ad account via the pinterest_conversion_event attribute. View documentation →
  • TikTok — deliver offline events to TikTok Offline Event Sets via the tiktok_conversion_event attribute. View documentation →
  • PubMatic — deliver events per advertiser over SFTP following PubMatic’s conversion data contract, via the pubmatic_conversion_event attribute. View documentation →
  • Yahoo — deliver events per pixel to Yahoo DSP via the platform-neutral conversion_event attribute. View documentation →
PII fields are SHA-256 hashed before transmission on every connector — raw customer data is never sent to the destination platform.

Legacy subscription forecast API and job types removed

The legacy /data-shops/subscriptions forecast routes (cost-forecasts, forecasts, and the unauthenticated public/forecasts pair) have been removed, along with the forecast, forecast-internal, and costs job types they enqueued. These endpoints powered retired frontend flows and had no remaining callers. Use the NQL forecast path (nql-forecast via POST /nql/run) to estimate query cost and rows going forward.The nql-forecast job completion payload discriminator is now "nql-forecast" to match the job type. Data planes that still post "forecast" continue to work — both discriminators are accepted.

Cached Mapping builder: built-in Exact match and a unified Models step

The Cached Mapping builder now shows the built-in identity rule every mapping already runs before its ML and LLM stages: a single Exact match rule in the Rules step that resolves any normalized input equal to one of the target attribute’s values straight to that value, with no model call. It’s on by default — remove it when you need every key to go through your models, and restore it from the warning banner the step shows once you do.The ML and LLM steps are now a single Models step. Pick classifiers and Cortex LLM models from one catalog, order them into a waterfall, and set a confidence threshold per stage — incompatible models are greyed out with a hint, and models with a partial-overlap warning stay selectable. A stage caches a value only when its model clears its threshold; a key no stage answers confidently caches as NULL rather than falling back to a nominated vocabulary member, since no member can honestly mean “we couldn’t tell.” Each stage’s threshold input appears as soon as the stage exists, so you can tune it off its 50% / 75% default even before a probe can run. A live Sample run scores every chosen model against up to 100 source keys so you can tune thresholds against real coverage — classifier thresholds are chosen automatically from where the classifier stops agreeing with the LLM, and hand edits stick until you resample. Between the Exact match rule and the optional Models step, attribute, source, and input expression are enough to build a valid mapping.

Classifier training moves to v1, resolved by dataset id

Classifier training now goes through the new POST /v1/model-training/train-classifier endpoint, which takes a dataset_id instead of a raw Snowflake table name and resolves the backing table server-side. Pass dataset_id, model_name, label_column, and feature_inputs (plus optional id_column, model, test_size, stratify, random_state, save_model, debug, model_version, and tags); the endpoint validates every column reference against the dataset’s schema before enqueuing the job and returns a job_id to poll. It only accepts datasets active on a Snowflake data plane and owned by the caller — non-Snowflake datasets and malformed identifiers return 400, datasets shared via data-plane collaboration return 403, and unknown datasets return 404.Classifier Studio now posts to this endpoint. The training job runs on whichever data plane owns the selected dataset, so the Data plane selector is gone from the Finalize step — a model name is the only remaining gate on Train Classifier. The two conditions v1 rejects at submission now surface at dataset selection instead: a non-Snowflake navigation data plane replaces the picker with a message pointing you at a Snowflake plane, and datasets with no backing table are disabled with an explanatory tooltip. Count Vectorizer is no longer offered as a feature type — choose Text (TF-IDF), Categorical, Numeric, or Embedding in the Classifier Studio feature configuration step.

Derivations API

You can now create and manage attribute derivation rules through the new public /derivations endpoints. A derivation rule describes how the value of one Rosetta Stone attribute is computed from another, so a rule from raw_email to sha256_hashed_email might map its value property to SHA2(NORMALIZE_EMAIL($source.value), 256). Once a rule exists, queries can resolve the target attribute from the source without a stored mapping.The endpoints cover the full lifecycle: GET /derivations lists the rules your company can use, GET /derivations/{id} fetches a single rule, POST /derivations creates one, PATCH /derivations/{id} updates fields on an existing rule, and DELETE /derivations/{id} removes it. List results can be filtered with repeated source_attribute_id and target_attribute_id parameters, and the response includes every rule your company owns plus every rule shared with it through collaborators. Reads require a read grant on attribute_derivations; creating, updating, and deleting require an admin grant, and only the owning company can modify or delete a rule. A rule’s source_attribute_id and target_attribute_id are immutable once the rule exists.Every transformation is validated before the rule is written. Each expression is parsed, compiled against a synthetic single-column table built from the source attribute, and type-checked against the target attribute; every required target property must be covered, unknown properties are rejected, and each failure comes back as its own descriptive 400. cached_mapping is not accepted, since there is no stored dataset to substitute $source into. Expressions may use CURRENT_DATE, CURRENT_TIMESTAMP, and RAND, so a rule like birthdate to age in years is correct as of each query that resolves it. If a create or update closes a cycle in the derivation graph the rule is still written, and the create and update responses return a cyclic_derivation warning listing the attributes in the cycle (cycles up to six attributes, up to ten reported).See the Derivations API reference for the request and response shapes.

Filter MCP job searches by workflow

narrative_jobs_search now accepts first-class workflow_id and workflow_run_id inputs, matching the Jobs API additions. Agents can list every job spawned by a workflow (or a single run) directly, instead of composing tag-string filters. narrative_jobs_describe also surfaces workflow_id and workflow_run_id in the metadata block when the underlying job carries them.The tool description for tags is corrected at the same time: multiple tags match any job carrying at least one of them, not all. Read more →

Actionable error messages for Rosetta AI chat

Toast errors from Rosetta AI chat now lead with a stable code and a specific fix instead of a generic “Request failed” line. Seven AGENT_E001 through AGENT_E007 codes cover the failures the chat raises on its own: no data plane selected when sending a message or compressing a conversation, the platform connection not being ready, the auto-resume round cap being hit, and the summarizing chat that compresses a conversation failing or returning nothing. Each toast now reads the same as the equivalent dialog error, with the code as the title and the why plus fix as the body, so an AGENT_E001 toast points at the data plane switcher in the top bar just like NQL_E002 already does in Data Studio.

Grouped multi-select filters on the dataset listing

The dataset listing’s Tags and Dataset Type filters are now grouped, multi-select menus. Tags are split into User Tags and System Tags on the real _nio_ boundary, so user-written tags that start with an underscore (like _debug_only) no longer get misfiled as system tags. Known system tags display their catalog label (for example, Materialized View, Audience) with the raw tag on hover, and shared prefix:value namespaces collapse under an expand row with a selected-count badge. You can now select multiple tags at once (OR semantics) and multiple dataset types, with tag selections still round-tripping through the ?tags= URL parameter.

Single-statement NQL execution: ExecuteNql task, POST /v1/nql/execute, and the MCP tool

You can now run any NQL statement without knowing its kind in advance — useful when the statement is supplied dynamically, for example through ${$workflow.input.statement}. The new ExecuteNql workflow task, the new POST /v1/nql/execute endpoint, and the MCP server’s narrative_nql_execute tool all parse the nql you pass in and dispatch to the matching executor: EXPLAIN runs as ExecuteExplain, INSERT/UPDATE/DELETE run as ExecuteDml, and CREATE MATERIALIZED VIEW runs as CreateMaterializedViewIfNotExists. SELECT and MERGE are rejected (WORKFLOW.EXECUTE_NQL.STATEMENT_IS_NOT_SUPPORTED from the task, 400 from the endpoint).The ExecuteNql task returns a type field naming the task that actually ran and a payload field with its normal output, so downstream workflow steps can branch on the statement kind — see the Task Reference. The POST /v1/nql/execute endpoint wraps the same dispatch for one-off calls: send nql plus an optional data_plane_id, compute_pool_id, and create_as_view flag, and it creates and starts a one-task workflow immediately, returning the workflow plus a run_id you poll like any other run — unlike POST /nql/run, which returns a job document. See Executing NQL via the API and the migration guide if you have existing /nql/run callers.The MCP server exposes this same endpoint as narrative_nql_execute, returning the workflow and run id so you poll with narrative_workflow_runs_list(workflow_id=...) and find the enqueued job with narrative_jobs_search(workflow_run_id=...). The previous tool name, narrative_nql_run, still works and routes transparently to narrative_nql_execute — update your prompts and skills to call it directly.

NQL UDFs work on AWS EMR compute pools

Fixed a bug where NQL queries that called a narrative_id_*, embed_text_*, or UID2 UDF failed during analysis on AWS EMR compute pools. The Spark application running the job never registered these UDFs, so the function name fell through to an external catalog lookup that failed with a permissions error. Compute pools now register the same UDFs as every other NQL executor before accepting work, so these queries run as expected.

Bulk NQL runs attempt every query and log why each failure occurred

Fixed a bug in Data Studio’s bulk query upload where the run stopped at the first unexpected error and reported truncated counts as if the whole file had been processed — a 50-query file could come back “2 validated, 1 failed” with the remaining 47 never attempted. Every query is now attempted regardless of individual failures, and each failure entry in bulk-query-log.jsonl carries the response status, body, and a plain-language NQL_W001 diagnostic; the upload panel also returns to idle after a rejected run so a new file can be picked without a reload.

Snapshot TTL retention drawer keeps recent snapshots

Fixed a bug in the dataset retention policy drawer where the time-based Snapshot TTL option emitted the wrong comparison, retaining only snapshots older than the configured interval and expiring everything younger — including the current snapshot. Because retention enforcement deletes the data files added by expired snapshots, affected datasets lost all data ingested within the interval on every retention run. The drawer now emits snapshot_age <= interval, so time-based Snapshot TTL keeps snapshots newer than the interval as documented in configuring retention policies.

Dataset Actions menu shows a busy state

Fixed a bug where the dataset Actions menu gave no feedback while a network-bound action (Update sample, Delete sample, Update statistics, Copy as Markdown) was running, so a second press could fire the same job multiple times. The button now spins and the menu disables until the request resolves, and on the datasets and audiences listings, the acting row’s ellipsis becomes a spinner while other rows stay usable.

Match Report input scoped to the selected identifier types

Fixed a bug where the Marketplace Match Report builder’s step 1 customer scan included every identifier type in your source graph, not just the ones you picked at Match identifiers — unselected types showed a large input count and 0 matches, reading as a matching failure, and the same unfiltered scan understated kpi_match_rate by dividing matches over ineligible identifiers. New reports now scope the Input panel, Identifier Type Summary, and Match Rate KPI to the selected types; re-run older reports to pick up the fix. See Building a marketplace match report.

MCP attribute tools surface enum and items in schema output

Fixed a gap where narrative_attributes_describe, narrative_attributes_search, and narrative_attribute_create omitted a scalar attribute’s enum values and an array attribute’s items element type from their schema output. All three now include these fields — nested object attributes were unaffected, since their enum values already passed through the forwarded properties JSON. See the MCP server reference.

Rosetta chat keeps the transcript when an inference run fails

Fixed a bug where a failed inference run in Rosetta AI chat wiped the pane down to a red “Inference Job Failed” box, losing the full transcript — and “New conversation” didn’t help, since the page instance was cached against the route path. The transcript now survives the failure with the error rendered inline, the composer stays usable for a follow-up, and “New conversation” opens a genuinely empty chat.

Dataset picker row clicks respect disabled state

Clicking the blank area of a disabled row in the shared dataset picker no longer selects the dataset. Previously the row-blank click bypassed the same guard that disabled the row’s Select button, so a dataset the caller had marked ineligible could still be picked from anywhere else on the row. The fix applies wherever the picker is used, including Classifier Studio and Prompt Studio’s dataset create flow.
APIBreaking ChangeBug FixDashboardImprovementIntegrationsNQLNew FeatureWorkflows

Cortex Code support in the MCP Configuration and skill Installation pickers

Data Collaboration listing detail pages and skill detail pages now offer Cortex Code (Snowflake’s CoCo CLI) alongside their existing harness options — Claude Code, Claude Cowork, Claude Desktop, ChatGPT, Codex, and the Narrative Conversations API on listings; the skills CLI and Claude Code on skills. Each picker preselects Cortex Code when the listing’s active data plane is Snowflake and falls back to the previous default otherwise, with no harness flicker while the data plane resolves. A manual pick sticks even if the data plane later changes.
  • MCP Configuration picker: cortex mcp add -t http narrative-mcp https://mcp.narrative.io/mcp — the -t http flag is required, since it defaults to stdio and would never reach the hosted URL.
  • Skill Installation picker: /plugin install github:narrative-io/narrative-skills-marketplace/plugins/<plugin>#main installs the plugin a skill ships inside directly from GitHub, with no separate /plugin marketplace add step. See Installing skills for the full picker walkthrough.

Secured the external MCP OAuth callback

GET /mcp-connections/callback no longer exchanges the authorization code. It now parks the code against its flow and 303s the browser to the Narrative app’s callback page, which finishes the connection by calling the new POST /mcp-connections/complete with the signed-in user’s own bearer. The exchange only runs when the bearer’s (company_id, user_id) matches the flow’s owner — a mismatch returns 404, so a state can no longer be used to capture another user’s token by luring them through the consent screen. See the MCP Connections reference for the updated lifecycle.The redirect from the callback carries only state on success and, on failure, an error whitelisted to RFC 6749 §4.1.2.1 codes plus invalid_state, unknown_state, and invalid_callback — an unrecognized code from the authorization server is reported as server_error rather than reflected.

Filter jobs by workflow on the Jobs API

The GET /jobs endpoint now accepts workflow_id and workflow_run_id query parameters, so you can list every job enqueued by a given workflow or a single run without scanning tags. Job responses also gain matching workflow_id and workflow_run_id fields, which are populated for jobs enqueued by a workflow and omitted otherwise. Read more →

Removed deprecated Rosetta Stone API endpoints

The deprecated Rosetta Stone mapping-suggestion endpoints and their supporting job type have been removed from the public API. Calls to the following are now rejected:
  • POST /datasets/{id}/suggest-mappings — the batch mapping-suggestion request against a dataset.
  • The /rosetta-stone/label-dataset and /rosetta-stone/label-sample labeling endpoints.
  • The datasets_suggest_mappings value in the job_types filter for POST /webhooks (webhook subscriptions).
If you still have webhook subscriptions filtered to datasets_suggest_mappings, remove that value — the enum no longer accepts it and requests including it will fail validation. The AI mapping-suggestion experience in the dashboard (Rosetta Stone → Normalized Datasets → NormalizeStart Analysis) is unaffected and continues to work through the current Rosetta Stone service. See Accepting AI Mapping Suggestions for the supported flow.

Build and schedule a Cached Mapping from the studio

The Cached Mappings studio can now build a mapping end to end. A new Build Cache action creates the cached mapping, activates the backend-provisioned cache dataset, and submits a population workflow that runs the rules, ML, and LLM tiers as a single waterfall. A toast confirms the build and links straight to the running workflow. If anything after mapping creation fails, the mapping is rolled back so a half-provisioned cache never breaks attribute queries at compile time.A new Cache Refresh step picks the cadence the population workflow re-runs on. Schedules run in UTC, default to daily, and @predefined aliases are expanded to the 5-field cron form the workflow DSL accepts. Choosing “One-time” means the cache fills once at build and only re-runs when triggered manually. A refresh-spend card sums the LLM stages’ row caps and warns when the schedule is hourly or faster, since frequent runs with an LLM tier are the runaway-bill shape to avoid.Refreshes are incremental: the waterfall’s keys CTE anti-joins the cache dataset so only keys not yet cached enter a tier. A scheduled run picks up new keys and retries still-unresolved ones instead of re-billing the whole waterfall.Two smaller changes come with this release:
  • Rule matching is now case-insensitive. A rule for lab fires on Lab and LAB because rule arms compile to WHEN LOWER(input_0) = 'lab'. The rule editor still flags surrounding whitespace with a Trim all repair.
  • Narrative admins get a Preview Workflow action that renders the population workflow YAML before Build Cache runs, and if the draft is incomplete the dialog reports the reason.
See Mapping types for the underlying cached_mapping schema.

Exclude collaborators when configuring a compute pool

The compute pool create and edit drawers now offer Exclude alongside All companies, Only my company, and Include. You can grant a compute pool to everyone in the data plane except a named set of companies without dropping to the API. Opening an already-excluding pool in the edit drawer now preselects Exclude with its company list intact — previously the form silently coerced the exclusion back to “only my company” on save.The backend has enforced the exclusion collaborator variant since it shipped in July; this change surfaces it in the UI.

Rebuilt S3 Connector settings page

The Amazon S3 Connector settings page has been rebuilt as a native part of the Narrative dashboard, replacing the embedded legacy experience. Existing /s3-connector/settings links keep working, and Launch from the marketplace now opens the profiles list directly.The new page tightens the profile create wizard with a three-step flow — profile info, bucket policy and ownership tag with a mandatory connection test, and file format — and translates the eight discriminated access-test error codes into actionable copy so you know exactly why a connection was rejected. Bucket policy and ownership tag fetches that fail now surface an inline error with a retry action instead of spinning indefinitely, and editing the bucket name no longer invalidates and re-mints the ownership tag on every keystroke.

NQL query included in dataset markdown exports

The Copy as Markdown action on materialized-view datasets now includes the dataset’s NQL under a ## NQL Query heading, as a fenced SQL block, in both the extended and full formats. The exported query is the reconstructed CREATE MATERIALIZED VIEW statement — the same query shown on the read-only NQL tab and opened by Open in Data Studio — so it runs as written. The concise format is unchanged, and the section is omitted for datasets that no query produced (uploads, subscription deliveries, and connector-fed datasets).

Collaborator picker holds Include or Exclude until you choose companies

Choosing Include or Exclude in the collaborator picker before selecting any companies no longer snaps the control back to “All companies” or “Only my company”. The picker now holds the chosen type until you pick a company, switch to an unconstrained type, or the form loads a different collaborator. Clearing the last company from an existing Include or Exclude also keeps the type instead of jumping to another option.The fix applies everywhere the picker is used — access rules, query templates, query views, datasets, audiences, and compute pools.

Dataset sample requests now honor the selected compute pool

Requesting a dataset sample from the dashboard previously posted an empty body to POST /datasets/{dataset_id}/request-sample, so the compute pool picked in the context selector was ignored and the job fell back to the dataset’s or data plane’s default pool. The dashboard now sends the selected pool as compute_pool_id on the request, giving you control over what a sample costs and how long it takes. The fix applies everywhere a sample is triggered from the dashboard, including the dataset “Update sample” action, audience metrics, and match report pages. See Compute pools for how pool selection resolves against dataset and data plane defaults.

Partial BETWEEN ranges in query templates now surface as a validation error

When you run a query template from the Data Studio catalog and fill in only one side of a BETWEEN range placeholder — the minimum but not the maximum, or vice versa — Data Studio now blocks execution with an inline NQL_E007 diagnostic explaining which bound is missing and how to clear it. Previously the unfilled bound was silently dropped during placeholder substitution, quietly changing the query you thought you were running. Leaving both bounds empty (skipping an optional range) and templates whose author hard-coded one bound (BETWEEN 5 AND {{max_age}}) are unaffected. Read more →

Extended thinking for Model Inference and Agent Conversations

Model Inference and agent conversations can now ask the model to reason before it answers. Set inference_config.thinking on a Model Inference call, or thinking on a conversation’s defaults (at creation) or config_override (per run). The field takes one of three shapes: the bare string "disabled" (explicit off), { "type": "effort", "level": "low|medium|high" } for models that take an effort scale, or { "type": "budget", "budget_tokens": 4096 } for models that take a token budget — the floor is 1024 and the budget must be smaller than max_tokens, since thinking is spent from the same output budget as the answer. Omit the field on a Model Inference call to get the model’s own default behavior; in an agent conversation the platform fills in the model’s top default for you (effort: high, or a 1024-token budget on budget-only models).Support is per-model and per-data-plane, so the same setting can be accepted on one data plane and refused on another. A setting the target model does not accept returns a 400 rather than being silently downgraded: agent conversations reject the run with Thinking Setting Not Accepted, or Thinking Not Supported for models that are not offered thinking at all. Forcing a tool call — tool_choice of either any or specific_tool — cannot be combined with thinking, and a run that asks for both is refused as Thinking With Forced Tool Choice.Reasoning comes back on the assistant turn as reasoning content blocks — the working as words plus a provider signature, or a signature alone when the model withholds the text — or as redacted_reasoning blocks when the provider encrypts it. Both shapes can be replayed verbatim on a follow-up turn so the model continues exactly where it left off, or dropped if you only want the answer. When rendering a conversation, use each message’s final_answer_from field: it names the blocks that make up the reply, so a model narrating its working as ordinary text before answering does not show up as two messages.anthropic.claude-opus-5.0 supports extended thinking on Bedrock data planes. See Supported Models for per-model support, Running Model Inference for request examples, and Agent Conversations for the full content-block reference.

ExecuteExplain workflow task

You can now forecast an NQL query from within a workflow using the new ExecuteExplain task. The task runs an EXPLAIN statement without executing the underlying query, and outputs rows (estimated row count) and cost (estimated cost in micro-cents USD). Downstream tasks can read those values via export.as or ${…} expressions to gate expensive work — for example, only running ExecuteDml if the forecasted row count exceeds a threshold. See the task reference for parameters and an example.
APIBug FixDashboardImprovementNQLNew FeaturePlatformRosetta StoneWorkflows

Claude Opus 5.0 available for Model Inference

You can now target anthropic.claude-opus-5.0 when running structured inference through Narrative’s Model Inference API. Opus 5.0 is the newest Claude Opus generation and is available on Bedrock data planes; Snowflake Cortex support will follow.Opus 5.0 has extended reasoning enabled by default at the provider, but the platform does not yet support thinking (no request config, no separate billing, no reasoning content block on a conversation turn), so Narrative sends an explicit “thinking disabled” switch on every request. The model uses the forced-tool-use structured-output path — output_format_schema conformance is enforced by client-side validation — and, like Sonnet 5.0 and Opus 4.7 / 4.8, it does not accept temperature or top_p (both are silently dropped from your request).See Supported Models for the full model list and capability details.

Rosetta AI Chat now defaults to Claude Opus 5.0

The premium tier of Rosetta AI Chat — and the summarizer that keeps long conversations coherent — now run on anthropic.claude-opus-5.0, up from Opus 4.8. New chats show Claude Opus 5.0 as the default in the model picker, and existing conversations keep rendering their original model name so history stays accurate.Model Inference is unaffected — it runs on its own model catalog.

Compute pool create form pre-fills idle and job timeouts

The “Create compute pool” form on AWS EMR data planes now starts with the platform defaults for idle timeout (900 seconds — 15 minutes) and job execution timeout (14400 seconds — 4 hours) already filled in, matching the values the API applies when either field is omitted. Each field’s helper text names the default it started from alongside the raw seconds, so it is obvious what 900 or 14400 translates to. Override either field before submitting to pin your own value — -1 still disables idle-termination.The edit form is unchanged: an empty field there continues to mean “operator default” for idle and “no limit” for job execution, and the hints keep saying so. See Compute pools for the full field ranges and semantics.

Structured CAST in Query Builder

CAST is now a first-class construct in Query Builder instead of an opaque raw NQL passthrough. Pick CAST from Add function wherever you can edit an expression, choose the value to cast, then pick a target type from a select that only offers types the slot accepts and the engine can actually convert the value’s type to. Cast expressions round-trip losslessly between Query Builder and the NQL tab, and a cast’s target type flows into the builder’s type inference so casts participate in filter operand and function argument constraint propagation.Numeric functions — ROUND, ABS, CEIL, and FLOOR — also tightened up: the field picker now offers only numeric columns and the literal editor only integer or decimal values, so mistyped arguments are caught in the builder instead of at server validation. Function cards now show the inferred return type (→ TYPE) in the header so type gating is visible at a glance.See the Query Builder functions guide for the full walkthrough.

Rules step in the Cached Mapping builder

The Cached Mapping builder now has a Rules step for exact-match mappings from an input value to one of the target attribute’s values. Rules are the first tier of the mapping waterfall — a matching rule always wins over the ML and LLM tiers that follow — and the step is optional, so keys with no rule fall through to the later tiers.The row editor flags rules that would quietly do nothing: duplicate inputs, inputs that can never match the normalized key (with one-click repair), and outputs that reference values the attribute no longer has. As with every step in the builder, the Rules step previews the NQL it emits, so you can see exactly what your rules become in the generated query, and changing the target attribute clears any existing rules rather than leaving them pointing at values that no longer exist.See Mapping types for how cached mappings are represented in the API.

Configure ML classifier stages in the Cached Mapping builder

The Cached Mapping builder now includes an ML step — the tier that runs between the deterministic Rules step and the LLM step in the mapping waterfall. You can pick one or more trained classifiers from My Models, order them into stages, and set a confidence threshold (plus an optional version pin) per stage. An earlier stage’s confident prediction wins over a later one’s, so ordering encodes your preference between models.The classifier picker tells you up front whether a model can actually resolve the target attribute: it surfaces class and input counts, warns when a model’s vocabulary only partially overlaps the attribute’s enum, and disables only the models that could never produce a valid value or whose input signature can’t be called from the source dataset. Changing the attribute or dataset resets the ML tier along with the rest of the waterfall.The Cached Mappings entry in the side navigation is now gated to Snowflake data planes, since ML stages call classifiers that only train there.

Label Studio Auto-label no longer offers o4-mini

The Auto-label model picker in Label Studio no longer lists openai.o4-mini. The model was withdrawn from both Snowflake Cortex and Bedrock, so a run that selected it returned no consensus and left rows with empty label dropdowns — the pass looked like it silently did nothing. Retired models are now filtered by resolved model rather than by tier, so Bedrock’s economy tier (which resolves to Claude Haiku 4.5) still appears in the picker.

Workflows list filters by data plane server-side

The Workflows list now filters by the selected data plane on the server, so companies with more than 100 workflows see the full, correctly scoped list. Previously the list fetched up to 100 workflows and filtered them in the browser, which silently truncated results — a company with 1,218 active workflows could see just a handful on a given plane.Switching data planes refetches with the new plane’s id, and pagination now follows next_page until it runs out instead of stopping at a fixed cap.

Filter workflows by data plane

GET /workflows now accepts an optional data_plane_id query parameter that narrows the response to workflows configured on a single data plane. Pagination and other query behavior are unchanged; omit the parameter to keep the previous behavior and return workflows across every data plane you can see.See the workflow orchestration guide for how the platform scopes workflows to a data plane.

Label Studio: Train Model handoff now trains end-to-end

Fixed a bug where projects handed off from Label Studio to Classifier Studio failed to train with invalid identifier 'LABEL'. The classifier trainer references its input columns with unquoted SQL identifiers (which Snowflake folds to upper case), but Label Studio was materializing its label dataset with case-sensitive lower-case columns, so no reference resolved. Label Studio now creates the label dataset with upper-case ID, INPUT_STRING, LABEL, SOURCE, and LABELED_AT columns and seeds a sequential ID row identifier via ROW_NUMBER(), and the Train Model handoff pre-fills Classifier Studio with the upper-case LABEL and INPUT_STRING names. Existing projects created before this fix should be re-created to pick up the new schema.

Bug fixes across the dashboard

  • Workflow runs table pagination — Fixed a bug where the runs table sent the wrong pagination parameters and the server silently returned page one for every request. The table showed up to 10 distinct runs duplicated across 100 rows and issued ~10 redundant requests per load (and ~120 requests per minute while any run was still executing). Pagination now advances correctly and the row collisions are gone.
  • Model repository label for Narrative-hosted models — Fixed a blank repository label on Narrative-hosted models in the models list. The dashboard was matching the HuggingFace organization name instead of the wire discriminator returned by the API.
  • View quickview never resolved — Fixed a bug where the view quickview would never load its content because the store compared UUID view identifiers as numbers.
  • Connection detail errors on connections without settings — Fixed a TypeError thrown when opening a connection detail drawer for a TradeDesk, Facebook, Google Ads, DV360, or DV360-CMU connection that had been saved without any quick settings. The drawer now renders correctly when quick_settings is absent.
  • Access-token create and edit sidebar — Fixed a bug where the access-token sidebar used the wrong permission shape, breaking the create and edit flows.
  • Data planes status filter — Removed a dead Inactive option from the data planes status filter. Only Active and Archived are ever emitted by the platform, and the filter now offers only those two options.

POST /v2/connections bug fixes

Closed a set of gaps between the v1 and v2 connections APIs that made historical backfills and app-token access misbehave on POST /v2/connections.
  • Historical backfills now run. The endpoint enqueued the backfill with a job type that the dataset data plane did not recognize, so every historical backfill triggered through POST /v2/connections silently failed with UnknownJob while the connection itself returned 201. The job is now stamped with the correct type and the backfill runs.
  • Maintenance window recorded on create. The endpoint enqueued the backfill without deriving a maintenance-window predicate, so backfills ran unbounded and the first recurring delivery re-covered the same range from the connection’s createdAt. The window is now derived (open-ended and capped at compile time for a new connection) and recorded after enqueue, matching the v1 behavior. Datasets without an active nio_last_modified_at mapping still get no predicate and no window, same as v1.
  • App client credentials denied on writes. App tokens could create or archive v2 connections whenever the installation and dataset both belonged to Narrative because the grant resolved to company 1. App tokens are now rejected on v2 connection create and archive, matching v1.

Actionable error messages for NQL query execution

Data Studio’s error dialog now names what went wrong and what to do about it when an NQL query fails. Each failure in the query execution path carries a stable code (NQL_E001 through NQL_E006) shown as the label in the error dialog, with a plain-language explanation of the cause and a fix that points at a specific place in the product. Previously these errors reached the dialog as empty objects and fell back to a generic “please try again” message, leaving no signal about missing dataset names, unselected data planes, workflow-build failures, or access-rule creation issues.
APIBug FixDashboardImprovementIntegrationsNQLNew Feature

Snapchat Connector

Deliver audiences from Narrative to Snapchat Ads Manager as Custom Audiences for campaign targeting. The Snapchat Connector is now available everywhere other ad-platform connectors are — install it from Installed Apps, then choose Snapchat when you finalize an audience in Audience Studio or set up a connection from a dataset’s details page, alongside Meta, TikTok, Pinterest, and The Trade Desk.Create a profile for each Snapchat Business organization, complete Snapchat’s OAuth flow, and Narrative discovers the ad accounts available under that organization — refresh or archive profiles as they change.Any dataset with at least one supported identifier is eligible: email (raw or SHA-256 hashed), phone number (raw or SHA-256 hashed), a mobile advertising ID (Apple IDFA or Android Advertising ID), or Narrative ID.See the Snapchat Connector reference to get started.

Classifiers tab in My Models

My Models now has a Classifiers tab that lists the trained classifiers in your selected data plane alongside the existing LLMs tab. Each row rolls up a classifier’s training runs into a single entry so you can see its current status, accuracy and macro-F1 metrics, number of classes, run count, and last-trained timestamp at a glance.Filter by status (Succeeded, In progress, Failed), sort by name, status, runs, or trained time, and search by name or job ID. In-flight runs and completed runs that reported a training error are surfaced as In progress and Failed respectively, so you can spot broken jobs without opening the Jobs page. Switching data planes automatically re-scopes the list.See the Classifier Studio reference for how the listing fits into the training workflow.

exclusion collaborator permission for compute pools, data planes, and attributes

The collaborator model gains a fourth variant — exclusion — that grants access to everyone except a named set of companies. Compute pool collaborators previously supported only all, none, and inclusion; they now match the rest of the platform. Data planes and attributes, which already advertised exclusion in their schemas, now enforce it end-to-end.
The new variant is accepted on POST/PATCH for compute pools (use), data planes (participants and manage_compute_pools), and attributes, and is enforced by the visibility queries behind every read path. For attributes, an exclusion grant is treated as similarly broad to all and is restricted to the Narrative company; anonymous callers are never granted access via exclusion.

Clearer failures from POST /mcp-connections, with an open-tools hint

Failed attempts to connect an external MCP server now return the same RFC 7807 shape as the rest of the Agent Conversations API: a stable type linking to the matching error page (Auto-Connect Unavailable, OAuth Flow Failed, Unsupported PKCE Method, and so on), a short detail, and a log_id you can hand to support to look up the underlying OAuth or HTTP cause.When a connection can’t be established, the platform now also probes the server unauthenticated. If it serves tools with no auth, the error tells you so — for example, “3 tool(s) are available without authorization — add the URL to mcp_servers[] with no connection_id.” Some servers (Mintlify-hosted docs MCPs, for instance) are open by default and only enable Dynamic Client Registration for gated content, so a connect failure is often a signal to use the server open. Read more →

MCP server: refresh schedules and correct NQL for materialized-view datasets

Two changes to narrative_datasets_describe for materialized-view datasets — reconnect your client to pick them up:
  • New refresh_schedule_config section. Pass refresh_schedule_config in the include= set to render the view’s active refresh schedule alongside the other dataset fields: schedule (cron expression), status, and, when the upstream API returns them, schedule_zone_id, stats_enabled, created_at, and updated_at.
  • nql now populated for materialized views. It previously rendered _not set_ for every materialized-view dataset because the tool read a top-level nql field that has never existed on GET /datasets/{id}. It now reads the query from materialized_view_config.nql and shows the actual view definition.
Read more →

Functions in Query Builder

Add functions to any expression in Query Builder — output columns, filter operands, and join conditions — without dropping into raw NQL. Click Add function wherever you can edit an expression to pick from a searchable catalog of 19 supported functions, including UPPER, LOWER, concat, concat_ws, SUBSTRING, length, REPLACE, regexp_extract, ROUND, ABS, CEIL, FLOOR, greatest, COALESCE, date_trunc, to_timestamp, to_json, parse_json, and object_remove_nulls.Arguments are structurally typed: literal-first parameters (like SUBSTRING start/length or ROUND digits) open a bare number or select input, while date_trunc’s unit argument renders as a picker. Variadic functions like concat_ws grow one slot at a time, and Query Builder infers argument types across sibling operands so a COALESCE inside a string slot only accepts string-typed inputs. Function outputs require an alias — Add and Save stay disabled until you name the column.See the Query Builder functions guide for a walkthrough.

Datasets API preserves your _nio* tags on update

PUT /datasets/{id} no longer strips every _nio*-prefixed tag from your request. Tag protection is now scoped to five named, platform-managed system tags — _nio_view, _nio_inference, _nio_cached_mapping, _nio_materialized_view, and _nio_refresh_use_workflow — which remain immutable and are silently ignored if included in add or remove lists. Every other tag, including custom _nio* tags, now round-trips through the API like any user tag. The same fix applies to the MCP narrative_dataset_update tool.

Cached mappings render correctly across the dataset UI

Datasets that use cached_mapping mappings no longer break the mappings experience in the dashboard. The mappings table now flattens cached-mapping rows with the attribute name, input expressions, and cache dataset reference instead of leaving them blank, and opening one shows a read-only drawer with the cache dataset id instead of crashing or spinning on preview. Cached-mapped attributes also appear in NQL autocomplete under _rosetta_stone.* like value-mapped attributes do, and copying a dataset’s mappings as markdown now labels cached mappings as cached_mapping instead of mislabeling them as object_mapping.

Data Planes API returns collaborator data planes

The Data Planes list and get-by-id endpoints now return data planes on which you are a collaborator — either a participant or a compute-pool manager — alongside the ones you own, unblocking compute-pool management on shared data planes such as the Narrative-owned us-east-1 data plane surfaced under Settings → Data Planes. Data planes you don’t own come back as the shared response variant, which omits owner-only fields (account id, collaborators list) but still carries what you need to operate it; write operations (archive, heartbeat, default-compute-pool, add/remove/set collaborators) remain owner-only.

Snowflake dataset connector compatibility

Fixed connector compatibility checks for datasets that live on a Snowflake data plane: Snowflake returns column names in uppercase, but connector interface policies match against lowercase field names, so eligible Snowflake datasets were previously rejected. Narrative now lowercases top-level column names when sending a Snowflake dataset’s schema to connectors, so eligible interfaces surface correctly and connections start without a case-mismatch failure.

MCP server: create Rosetta Stone attributes from an AI assistant

The Narrative MCP server can now create new Rosetta Stone attributes without leaving the AI assistant. A new narrative_attribute_create tool wraps POST /attributes and accepts name, display_name, and a type_definition — either a scalar (string, long, double, boolean, timestamptz, with optional enum and Spark SQL validations) or a nested object / array type. Optional description and tags are supported. Attributes are created private to the owning company; sharing stays a UI/API concern. The tool description steers agents toward catalog reuse — search with narrative_attributes_search first and only create when defining the type contract for a downstream system. Reconnect your MCP client to pick up the new tool. Read more →

Input Expression step in the Cached Mapping builder

The Cached Mapping builder now has an Input Expression step for shaping how each raw dataset row becomes the mapping input and cache key. You can pick one or more fields from the raw dataset, compose a string template with click-to-insert {{ property }} slots, or drop into an NQL expression against the source dataset. Each mode keeps its own draft as you switch between them, and only the active mode is committed on save; changing the raw dataset upstream clears any prior expression so the builder never carries stale references.

Harden external MCP servers with URL validation, SSRF egress guard, and actionable auth-required errors

External MCP servers in mcp_servers[] are now hardened on three fronts. Each url is validated up front when the conversation or run is created: non-https or malformed URLs are rejected with a new Invalid MCP Server URL error (HTTP 400) instead of failing late at discovery. Outbound calls to external MCP servers (and their OAuth authorization servers) now go through an egress guard that refuses hosts resolving to loopback, private, link-local (including the 169.254.169.254 metadata IP), IPv6 unique-local, unspecified, or multicast addresses — blocking SSRF against internal targets. And when a server submitted with no connection_id actually requires authorization, run-start discovery classifies the 401 and fails the run with guidance to connect it via POST /mcp-connections, naming your existing connection id for that URL if you already have one. Read more →
APIBug FixDashboardData StudioImprovementIntegrationsNQLNew Feature

Claude Sonnet 5.0 and Opus 4.7 / 4.8 in Model Inference

Model Inference now supports three new Anthropic models: anthropic.claude-sonnet-5.0, anthropic.claude-opus-4.7, and anthropic.claude-opus-4.8. They are accepted wherever existing Claude models work, including the runModelInference SDK call and the RunModelInference workflow task. These generations use the platform’s forced-tool-use structured-output path, and because they reject temperature and top_p, the platform drops those sampling parameters from the request. Read more →

Connect external OAuth-protected MCP servers with Dynamic Client Registration

Agent conversations can now call third-party MCP servers that require user authorization, not just Narrative-owned or public ones. A new /mcp-connections API drives the OAuth 2.1 handshake end to end: it discovers the server’s metadata, performs Dynamic Client Registration (RFC 7591), and runs authorization-code + PKCE against the external authorization server. POST /mcp-connections returns an authorization_url; after the user consents, the callback exchanges the code and flips the connection to connected.Once connected, reference it from an agent run by adding connection_id to the matching mcp_servers[] entry — the platform resolves and refreshes the bearer token server-side on every call, and the token never appears in the request payload, effective_config, or run history. Connections are scoped per user, so peers in the same company cannot see or use each other’s. Read more →

Structured output: validation, preserved failures, and agent retries

Model Inference and Agent Conversations now handle structured-output validation consistently, so a bad response is recoverable instead of an opaque failure:
  • Model Inference validates and preserves misses. Every response is validated against the job’s output_format_schema. On success, structured_output is populated as before. On a validation miss the job still completesstructured_output is null and a new failed_structured_output field carries both the raw JSON the model emitted and the validator’s explanation, so you can inspect it and decide how to handle it. This covers both the native structured-output path and the forced-tool-use path (Sonnet 5.0, Opus 4.7 / 4.8).
  • Agent runs retry invalid output. When a final answer from an Agent Conversation fails validation, the run feeds the non-conforming output and the validator’s explanation back to the model and retries — up to 10 times, on a budget separate from max_iterations. If every retry still fails, the run ends with AgentLoopStructuredOutputRetriesExhausted and the last output is kept for inspection.
Read more →

Slash-command picker for skills in Rosetta AI Chat

The Rosetta AI Chat message bar now treats / as a first-class trigger alongside @. Type / — or click the new / toolbar button — to open a searchable skill picker, each row showing the skill’s icon, title, and description. Picking one inserts a /slug reference token that renders inline like an @dataset:name mention and round-trips through the wire format, so the agent sees the exact slash command you’d type by hand. Detection is word-boundary aware, so / inside URLs, dates, and file paths stays plain text, and empty-state quick actions now seed real skill commands like /generate-rosetta-stone-mappings. Read more →

Rosetta AI Chat: see the agent’s current plan

When Rosetta is working through a multi-step task, its plan now surfaces as a compact Plan · done/total tab pinned to the top of the chat’s message bar. Click it to expand a read-only checklist showing every planned step, which one is in progress, and which are done — a per-status glyph ( pending, spinner in-progress, done) makes progress readable at a glance. The tab only appears once the agent has actually planned work, so short conversations stay uncluttered. This release also fixes a pre-existing defect where sync client tools briefly flashed their raw JSON arguments in the transcript while auto-resolving.

MCP server: create dataset-to-connector connections from an AI assistant

The Narrative MCP server can now route dataset data to a destination connector without leaving the AI assistant. A new narrative_connection_create tool wraps the connections API, backed by four discovery tools: narrative_installations_list, narrative_app_profiles_list, narrative_dataset_get_compatible_interfaces, and narrative_app_interfaces_list. The recommended order — installations → profiles → compatibility → quick settings → create — is documented as the connection creation flow. Reconnect your MCP client to pick up the new tools. Read more →

MCP server: create cached mappings from an AI assistant

The narrative_mapping_create tool now accepts cached_mapping bodies alongside object_mapping and value_mapping, so AI assistants can create every mapping shape Rosetta Stone supports without dropping into the UI or API. Pass type: "cached_mapping" and one or more input_expressions (NQL expressions over the source dataset’s columns that form the join key); the cache dataset is created automatically. Cached mappings are not permitted on the opt-out attributes. Reconnect your MCP client to pick up the updated schema. Read more →

Cancel a running query in Data Studio

You can now cancel an in-flight query in Data Studio. While a query runs, the action bar shows an interactive Cancel button in place of the previous passive “Please wait…” indicator — clicking it aborts the run and returns the editor to an idle state. If the backend response lands after you cancel, Data Studio discards it so a stale result or error can’t overwrite the editor.

Modernized Access Rules list and new detail page

The Access Rules page in My Data now renders through the shared object browser, so search, sort, pagination, and loading state match datasets and other listings. The share type drives a leading icon column, and the tabs are relabeled My Access Rules and Access Rules Shared With Me. Clicking a rule’s name now opens a dedicated detail page at /my-data/access-rules/{id} — with the same Details, Collaborators, Schema, and Mappings sections plus Edit, Archive, Copy JSON, and Copy as Markdown actions — instead of expanding a row inline. On the My Access Rules tab, each rule’s associated datasets are shown by name and linked to their detail pages. Read more →

New scheduled job state and attempt fields on job responses

The Jobs API’s state enum now includes a scheduled value that sits between pending and running, representing a job that has been claimed for execution and handed off to an executor. Job responses also gain attempted_at (the timestamp of the current attempt, which advances on each retry) and attempt_version (the 1-indexed attempt number), so you can distinguish an initial run from a retry without inferring it from created_at and updated_at. Webhook subscriptions can filter on scheduled alongside the existing states. Read more →

CREATE MATERIALIZED VIEW reports unsupported output types as 422

CREATE MATERIALIZED VIEW queries whose output columns resolve to a type NQL cannot persist (such as DECIMAL, BINARY, INTERVAL, or MAP) now fail with a 422 Unsupported Type Error naming the offending column and a suggested CAST on /nql/run, /nql/validate, and /nql/compile — instead of passing validation and then returning an opaque 500 at run time. Nested types report the innermost unsupported type, so ARRAY<DECIMAL> correctly points at DECIMAL. Read more →

NQL now quotes additional reserved keywords in generated queries

The NQL identifier printer previously missed some Calcite reserved keywords — including uuid — when auto-quoting column names in generated queries, causing queries against datasets with those column names to fail to parse. The keyword list is now aligned with Calcite’s parser metadata, so builders and other query generators emit valid NQL for every reserved keyword while leaving non-keyword columns unquoted. Read more →

Sample data cells distinguish doubles from integers

Dataset sample cells now render whole-number DOUBLE and FLOAT values with a trailing .0 (for example 68.0), making them visually distinct from INT and BIGINT values; fractional, exponent-form, and integer schema types are unchanged.

MCP server: RFC 9728-compliant protected-resource metadata

The MCP server now serves OAuth protected-resource metadata at /.well-known/oauth-protected-resource/mcp with the correct path-aware resource value (per RFC 9728 §3.1), so RFC 9728-aware clients like VS Code connect through the primary OAuth flow instead of falling back after a resource-mismatch error; the legacy root path is preserved for clients that request it directly. Read more →
APIBreaking ChangeBug FixComposable IdentityDashboardData StudioImprovementIntegrationsNew FeaturePlatformWorkflows

Graph Builder now runs on AWS data planes

The Graph Builder in Graph Studio now builds identity graphs on AWS data planes in addition to Snowflake. Under the hood, step 2 of the build runs the platform’s LabelConnectedComponents workflow task — the same algorithm as before, but executed as a series of independent NQL jobs instead of a single Snowflake UDF. This removes the monolithic-query timeout that previously stalled large graph builds and enables graph creation for AWS-hosted data. Read more →

Split identity components on exclusive attributes

Identity graphs can now split resolved components that disagree on an attribute that should be exclusive to one identity — for example, an SSN token or an exact date of birth — targeting known overmerge cases without dropping legitimate connections.
  • Graph Builder UI. The Algorithm parameters step now includes an Exclusive attributes selector. Pick one or more first-party target ID types that must hold a single value per identity; after the connected-components pass converges, any component whose vertices disagree on a declared attribute is split apart. Leaving the field empty preserves the previous behavior.
  • Workflow DSL. The LabelConnectedComponents task gains a matching exclusiveAttributeCols parameter. It is opt-in and defaults to an empty list, so existing workflows are unaffected until they declare it.
Records without a value in the declared column deterministically follow their edges into the fragment they remain connected to. Read more →

Exchange an app token for an installation-scoped access token

Apps using client-credentials authentication can now mint installation-scoped access tokens directly, without a user-bound token from the installing company. POST /installations/{id}/token accepts an app client-credentials token (obtained from POST /oauth/token with grant_type=client_credentials) and returns a token carrying the permissions granted to the app at installation — use it when an app acts on behalf of an installing company outside any user session, such as a scheduled sync. To support the flow, GET /installations and GET /installations/{id} also accept app tokens; with an app token, GET /installations lists installations across every company that installed the app, optionally narrowed with a company_id query parameter.

Dataset API: two breaking changes

  • Sample endpoints require a pre-computed sample. GET /datasets/{id}/sample, GET /datasets/{id}/sample/mappings, and mapping-creation validation no longer scan the underlying dataset files on the fly — they are served exclusively from the sample store. When no sample exists yet they now return a 400 No Sample Available error; call POST /datasets/{id}/request-sample to generate one first.
  • Datasets listing drops refresh schedules. GET /datasets now returns an nql-only materialized_view_config for each materialized-view dataset; the refresh_schedule_config field has been removed from the listing response. For a specific dataset’s cron expression, timezone, and next-run timestamp, call GET /datasets/{id}.

Compress a long Rosetta chat into a fresh conversation

Long Rosetta AI chats used to grow an unbounded context window with no way to trim it. The chat header’s overflow menu now includes a Compress conversation action that summarizes the current conversation and opens a fresh chat pre-seeded with that summary, so you carry the useful context forward without the weight of the full transcript. It’s available once a chat has at least one assistant reply and is disabled while a run or another compression is in flight; each compression leaves a short Conversation summary entry in Chat History.The command palette (⌘K / Ctrl+K) is now a general-purpose navigation surface. Open it from anywhere and jump straight to the Graph Studio tabs — Graph Builder, Edge Builder, Match Report Builder, and Addressable Graph Builder — as well as Datasets, Graph Studio Help, or Create New Dataset, without clicking through the sidebar. Your five most recently used commands surface under a Recent section, and arrow-key navigation wraps at the ends of the list and keeps the highlighted command in view.

Compute pool improvements

Three updates to compute pools:
  • Create pools from the data plane page. A Create Compute Pool button on a data plane’s dashboard page opens a side drawer to provision an AWS EMR pool — name, size, collaboration policy, and optional idle and job-execution timeouts — without leaving the page. The Edit drawer now shares the same form and validation. (Snowflake pools are still registered through the Snowflake Native App.)
  • Instance-storage size variants. Every EMR pool size now has a *_storage sibling (x_small_storage6x_large_storage) that runs workers on rXgd instances with local NVMe SSD. Use them for shuffle- or scratch-heavy jobs that would otherwise spill to EBS and fail with “No space left on device”.
  • Timeout defaults now applied on update. PUT /data-planes/{id}/compute-pools/{poolId} now applies the same 15-minute idle and 4-hour job-execution defaults as create when the request omits them — previously a size-only update could silently drop the job-execution cap.
Read more →

S3 Connector column sort control for CSV deliveries

You can now control column ordering in CSV deliveries from the S3 Connector. Toggle Sort columns alphabetically on any S3 dataset connection to either keep the connector’s alphabetical default or preserve the delivering dataset’s schema column order. Existing connections keep their current behavior — the setting defaults to enabled to match the previous connector default. Read more →

Secret Sharing tightens confidentiality guarantees

Secret Sharing now enforces its one-time promise end to end. Creating a share link requires an authenticated Narrative session, and each secret is capped at 4,096 bytes. Retrieval is now atomic, so a link can only ever be read by a single recipient even under concurrent access; every subsequent read returns a 404. Retrieving a shared secret still requires no account, so you can continue to send links to recipients outside of Narrative. Read more →

MCP server: update dataset metadata from an AI assistant

The Narrative MCP server now exposes a narrative_dataset_update tool, letting AI assistants curate dataset metadata in-conversation. It wraps PUT /datasets/:id and edits only curator-safe fields — display_name, description, and tags (via add/remove semantics) — leaving structural fields such as name, schema, and write mode untouched. Omitted fields are left unchanged, and platform-managed _nio system tags are filtered out. pending and active datasets accept updates; archived datasets reject them. Reconnect your MCP client to pick it up. Read more →

MCP server: richer describe output for data planes and mappings

Two improvements to the Narrative MCP server’s describe tools — reconnect your client to pick them up:
  • Compute pool config on data plane tools. narrative_data_planes_list and narrative_data_planes_describe now return status, size, idle_timeout_seconds, and a derived always_on flag for each compute pool, with guidance that small always-on pools are ideal for lightweight jobs such as narrative_dataset_request_sample.
  • Mapping expression dependencies. narrative_datasets_describe now instructs assistants to use each mapping expression’s dependencies.properties field to see which dataset columns feed a mapped attribute, without re-parsing the NQL.
Read more →

Jump from a job to its dataset details

The Jobs table’s per-row menu now includes an Open in Dataset Details action at the top of the Open group, taking you straight to the dataset a job wrote to instead of hunting for it in My Data. It sits alongside the existing View Sample, View Statistics, and Open in Data Studio entries, and only appears for job types that resolve to a dataset.

Open in Data Studio now shows the current materialized view definition

Opening a materialized view dataset from Open in Data Studio now preloads the reconstructed CREATE MATERIALIZED VIEW statement that reflects the dataset’s current refresh schedule, description, tags, and write mode — the same query shown on the dataset’s read-only NQL tab. Previously the editor loaded the raw stored query, which could be stale relative to the dataset’s current metadata. Read more →

Real provider error messages in connector dialogs

Connector dialogs now surface the actual error returned by the provider when a save, connect, or delete action fails. Previously the Facebook, TikTok, Pinterest, PubMatic, and Yahoo DSP profile dialogs — along with Pinterest app invites and The Trade Desk taxonomy operations — replaced the response body with a generic “Failed to…” message; failures now propagate the provider’s real reason so you can act on it without checking network logs.

Previous Release Notes

Q2 2026

April – June 2026

Q1 2026

January – March 2026

Q4 2025

December 2025