Skip to main content
This guide is for developers whose app embeds Upsolve through the <UpsolveDashboard /> React component and manages dashboards through the v1 Dashboard Management API (/v1/api/dashboard/*). It shows how to move that integration to v2 without changing how your app works:
  • the component becomes an iframe,
  • the v1 dashboard endpoints become their v2 equivalents,
  • and the IDs you already hold keep working.
Your Upsolve team runs the data migration for your organization. It turns each v1 workspace into a v2 application, and every dashboard you created, including those created through /v1/api/dashboard/create, into a dashboard of that application. Nothing on your side has to happen at the same moment: you can switch the iframe first and the API calls later.

How v1 concepts map to v2

When a user edits a shared dashboard, v2 gives them their own copy of it, and that copy replaces the shared one in their list. The list endpoint and the iframe both apply this rule, so a user always sees one entry per dashboard.

What happens to your IDs

The migration records which v2 dashboard every v1 dashboard became. Because of that:
  • The iframe accepts v1 dashboard IDs as well as v2 ones.
  • List Application Dashboards returns legacyDashboardId next to each dashboard’s v2 id.
  • List Applications returns legacyWorkspaceId next to each application’s applicationId.
  • Look Up Dashboard turns any dashboard ID, v1 or v2, into { applicationId, dashboardId }.
If your app discovers dashboards at runtime with list-workspaceslist-workspace-dashboards, you have nothing to rewrite. Point those calls at the v2 endpoints (step 3) and use the IDs they return. If you stored IDs, they keep working in the iframe as they are.

Step 1: Tokens (usually no change)

Keep calling POST /v1/api/tenant/register-tenant from your backend. After your organization’s migration is finalized, it returns a project-user JWT for the same end user instead of a tenant JWT. Pass that token to the iframe as jwt and to the v2 API as Authorization: Bearer <token>. If you are starting fresh, or want to move off register-tenant, use the v2 token flow:
  1. Register Organization
  2. Register User
  3. Get Project User Token
See Backend Setup for the full flow.
A tenant JWT issued before the migration is still accepted by the v2 API. It is translated to the project user the migration created for that tenant. The iframe needs a project-user JWT.

Step 2: Replace <UpsolveDashboard /> with an iframe

Before

After: you only have the dashboard ID

/share/dashboard/{dashboardId} looks up the dashboard’s application for you and opens it. It accepts the v1 ID you already pass to <UpsolveDashboard />.
Pointing at the application directly saves the lookup step:
With dashboardId, the embed shows exactly that dashboard, with no sidebar or tabs, just as <UpsolveDashboard /> did. Leave dashboardId off to embed the whole application with its own navigation. A dashboard ID the token cannot see renders “Dashboard not found.” rather than silently opening a different dashboard.

Props → URL parameters

Changing the iframe src reloads it. Keep the src stable across renders (for example, memoize the token) so the dashboard doesn’t reload unnecessarily. See Frontend Setup.

Step 3: Dynamic listing

If your app builds its own navigation from the v1 list endpoints, swap them one for one.

Before

After

Step 4: Dashboard writes

Who you authenticate as decides where the dashboard lives, just as in v1:
  • API key: the dashboard is shared with every user of the application. This is the v1 “global dashboard”.
  • Project-user token: the dashboard is created in that user’s space. This is the v1 “tenant dashboard”.
One rule is new. A project-user token cannot edit a shared dashboard, because the change would reach every other user. Such a call answers 409 SHARED_DASHBOARD_READ_ONLY. Clone the dashboard first and edit the copy; the copy lands in the user’s own space.
With a user-scoped API key, pass organizationId: in the query string on GET and DELETE, and in the JSON body on POST.

What v1 does once your migration is finalized

The writes stop because a dashboard written through v1 after the migration would land where v2 never reads it: it would return an ID and then never appear in any embed. The lists keep answering so that an integration that only lists and embeds keeps working throughout. Recommended order:
  1. Swap the component for the iframe (step 2).
  2. Point the list calls at v2 (step 3).
  3. Point the write calls at v2 (step 4).
The first two steps work before and after finalize. The third is required by finalize.

Troubleshooting