Skip to main content
Add Content cards for every widget source

Goals, calculated metrics, custom data and static values are their own cards

The Add Content panel used to list goals, calculated metrics, custom data and static values inside the data-source picker, alongside real platforms. Each is now its own card, and a widget added from one arrives already bound to it — so the data-source picker is hidden rather than offering a source you’d never pick.New widgets preview demo data. A widget you’ve just added shows sample data shaped like the thing it’s about to hold, instead of an empty frame:
  • a calculated metric trends across the report’s date range, because a formula is always evaluated over time
  • a goal draws as a gauge, not a series
  • custom data shows a label column plus several value columns in mixed formats, the way an imported sheet arrives
  • a static value renders the typed-in number
The demo disappears as soon as the widget has a real source and a fetch lands.
MCP: typed widget tools and series read-back

One tool per widget type, and a way to read the numbers back

Adding a widget over MCP went through a single generic tool. There is now one add_ and one update_ tool per widget type — title, text, image, button, embed, data, calculated metric, custom data, goal and static value — each taking only the fields its type actually has.Widget data can now be read back. Writes and refreshes stay fire-and-forget: the server fetches after the widget is saved. Previously an assistant had no way to see the result, because the editor learns about it over a realtime channel an MCP client can’t subscribe to. The new get_widget_data returns each widget’s state plus its settled numbers, waiting up to 15 seconds for in-flight fetches and returning up to 200 rows per widget — enough to draw a chart, and bounded so a poll loop terminates.Every tool also now carries a title and read-only / destructive annotations, so a client can show what a tool will do before running it. The server exposes 188 tools across 29 groups.
Data sources now live on the client

Connect a data source without opening a report

Data sources were reachable only from inside the report editor, which left a new client with nothing connected and nowhere obvious to start. Each client now has its own Data sources page at /{client_id}/data-sources.It lists the whole catalog as cards, connected platforms first, each reading Connect or Connected depending on what this client already has. Search matches a platform’s name or category, and a Request a data source button files the ones we don’t support yet. Clicking any card opens that platform’s connections in a slide-over — the same connect flow the report editor runs — where you link, unlink, edit or refresh a profile.A client with nothing linked now lands on this page instead of an empty Reports list. Anyone who can see the client can see the page; connecting still requires edit access to that client.
Report actions in the header

The report header has an actions menu

Viewing a report gave you no way to act on it without going back to the list. The header now carries a menu with the same actions as the list row — duplicate, save as template, archive — built from one shared definition so the two menus can’t drift apart.
Google Ads and Google Local Services on v23
Both moved from v21, which Google is retiring. Ad-level fields were updated for v23’s breaking changes, so ad-level widgets keep returning the same columns.Keyword rows also aggregate correctly now. A single-dimension keyword widget listed the same keyword once per underlying match type, which split its numbers and made comparison periods format wrong. Matching keywords are combined into one row.
Projects are now Reports

Projects are now Reports

The thing you build inside a client is a report everywhere — the UI, the app URLs, the REST API, and the MCP tools. This is a breaking change for anything integrating with Oviond.There is no /v1/projects alias. It was removed rather than deprecated, so a call to the old path returns 404. The field rename applies to every endpoint that carried project_id, including GET /v1/automations/{report_id}/history and the PDF endpoints.The MCP tools were renamed to matchcreate_report, get_report, list_reports, update_report, duplicate_report, archive_report, archive_reports, refresh_report, change_report_date_range, add_report_section, create_report_from_template, create_client_with_report and audit_report_health.Two things deliberately did not change: database column names, so anything reading Supabase directly is unaffected; and the stored source_type / resource-type value project, which is still what a widget and an activity-log row carry. The report type is called Multi-Page Report or Dashboard.
Legacy project merge tags removed

{{project_*}} merge tags no longer resolve

Email subjects and bodies accepted both {{project_name}} and {{report_name}} while the rename was in progress. The project_* aliases are gone. The supported tags are {{report_name}}, {{report_type}}, {{report_date_range}} and {{report_url}}, alongside the {{client_*}} and {{account_name}} tags, which are unchanged.Automations and templates saved from the app were already using the current names. Anything that composed a subject line by hand — over the API, or pasted in — needs the tag renamed, or it will send the literal text.
Fixes across data sources and the editor

Fixes

Data sources
  • BrightLocal returned only the first page of accounts, so later ones couldn’t be linked. Every page is now fetched.
  • Pinterest Ads dimension values were formatted wrong in tables and charts.
  • Mailchimp and Ahrefs metric formatting corrected; Klaviyo now retries when it hits a rate limit instead of failing the widget, and duplicate flow names are consolidated.
  • Refreshed logos for BrightLocal, Advanced Web Ranking and SERP Watch.
Editor
  • Goals: the builder’s display settings rendered incorrectly, and filters and advanced settings are no longer required to save a goal.
  • Table widgets can now be sized down to a 50px minimum column width, from 100px.
  • On an empty dashboard, dropping a widget anywhere on the canvas now places it at the top of the page — the drop area only covered part of the canvas before.
  • Currency pickers — add client, client settings, company settings and the per-widget override — share one grouped list, searchable by currency code, country or symbol. The widget override clears back to the client’s currency.
Exports
  • Report, client and template thumbnails are captured at half scale and encoded lossily. They look the same at display size and the stored files are a fraction of the size. PDF rendering is unchanged.
Airtable and Omnisend

Two new data sources

Airtable and Omnisend can be connected like any other platform, per client, and reported on with the usual widgets.
Resend a pending invite

Resend an invite

A team member who never received their invite — or let the link go stale — had to be removed and re-invited. You can now resend the invite from the users list, or over the API with POST /v1/users/{id}/resend-invite.The new link replaces the old one, which stops working. Resending is refused for someone who has already accepted, since they have a session. The users list marks anyone who hasn’t accepted yet as pending.
Datasource discovery endpoints moved

Datasource discovery lives under /v1/datasources

The endpoints that describe what Oviond can report on have moved from /v1/data/* to /v1/datasources/*, alongside the connection endpoints they belong with. POST /v1/data/query and POST /v1/data/resource are unchanged.GET /{datasource_id}/describe replaces the separate /metrics and /dimensions calls: one request returns a datasource’s data views, the advanced settings each view requires, its default view, and its metric and dimension catalogs with the flags that decide what can be combined. Pass ?data_view=ACCOUNT to scope the response to one view.Testing a connection has changed shape as well. POST /v1/data/test, which took a datasource_id and a client_id, is replaced by POST /v1/datasources/test, which takes the connection itself:
Connection ids come from GET /v1/datasources/auths. The result is recorded on the connection, so its status in the app reflects the test.See Querying data for the full walkthrough.
Goals and calculated metrics are validated

Goal and calculated-metric queries are now validated

A goal’s query and a calculated metric’s formula were accepted without inspection, so either could name a datasource, metric or dimension that doesn’t exist and nothing would report a problem until the widget rendered empty.Both are now validated on create and update, against the same catalog the report builder uses. An unknown identifier is rejected with a 400 naming the field and how to find valid values.This also fixes creating a calculated metric over the API or MCP, which could not succeed: a formula is an array of tokens, and the schema in front of it rejected arrays outright. A formula is now described properly — an array of METRIC, NUMBER and OPERATOR tokens, where a METRIC token carries the same query a widget does.Existing goals and calculated metrics are unaffected; the validation runs on write.
Enumerated fields tightened

More fields validated against a fixed set

Several fields that accepted any string now accept only their documented values, so a typo returns a clear 400 instead of being stored and quietly resolving to the wrong thing:
  • Date rangesdate_range.text (the preset) and date_range.compare (the comparison mode) on projects, widgets, templates and automations. An unrecognised comparison mode previously produced no comparison at all.
  • Metric display — a metric’s symbol (number format) and summary (how the total aggregates). Both long and short spellings stay valid, so percent and %, currency and $ all continue to work.
  • Widget type, and the notification channel on channel routes (slack, discord).
One breaking change. A metric or dimension on a widget is now always an object, never a bare id — on POST /v1/widgets and on the MCP widget tools alike:
Only value is required; Oviond fills in the rest from the catalog, and label renames the field on the widget. Filters are unchanged. Widgets saved from the app were already in this shape, so only integrations that sent ["clicks"] need updating.Beyond that, no changes are needed for integrations already sending valid values.