Microsoft Project Online retires September 30, 2026, migrate to a modern platform before it's too late.Start migration
Back to BlogConnect Power BI to Onplana: The Data Contract That Replaces Your Project Online OData Feed
Migration

Connect Power BI to Onplana: The Data Contract That Replaces Your Project Online OData Feed

Your Project Online OData feed dies at cutover. Connect Power BI to Onplana with a read-scoped API token instead, and let the server do the portfolio rollups.

Onplana TeamSeptember 1, 202611 min read

Microsoft Project Online retires on September 30, 2026, and the OData feed goes with it. If you ran the Project Online inventory checklist, you will have seen the warning it raises against every Power BI workspace attached to your PWA: those connections need repointing, and the feeds behind them are marked as a rebuild rather than a remap. The missing half of that warning is the replacement itself: how to connect Power BI to Onplana, which endpoints carry which data, and where your reports should actually point.

The short version Onplana exposes a REST API, you authenticate with a read-scoped Personal Access Token, and Power BI calls it directly with the token as a Bearer header. There is no OData shim, so this is a rebuild of your queries, not a repoint of a connection string. Most of the work is smaller than it looks, because the portfolio rollups you hand-built in Power Query are already computed server side at GET /api/reports/cross-project. The one thing not to do is build this as a published Maker app. It cannot read project data, and anything you put in it is published to anyone with the link.

The strategy question of which reports to rebuild, in what order, and how far ahead of cutover to start is covered separately in rebuilding Project Online Power BI reports after migration. The mechanics follow.

Why is there no drop-in OData endpoint?

There is no equivalent feed because an auto-generated OData surface over a multi-tenant schema is a standing liability, not because the work was skipped.

Project Online's /ProjectData feed was a read-only OData projection over the reporting database. Power BI could point at it, discover the entity sets, and pull Projects, Tasks, Assignments, and the timephased tables with no code at all. That convenience came from a single-tenant design where the reporting database was already yours.

Generate the same surface across tenants and every column becomes a public contract, every relationship becomes a traversal path, and permission scoping has to be re-derived at the projection layer instead of enforced once at the route. We would rather maintain a smaller, deliberate set of endpoints where the visibility rules are the same ones the rest of the product enforces.

The practical consequence is that the mapping is close enough to be mechanical:

Project Online Onplana
/ProjectData/Projects GET /api/projects
/ProjectData/Tasks GET /api/tasks, or GET /api/projects/:id/tasks
/ProjectData/Assignments, timephased actuals GET /api/timesheets
Portfolio rollups hand-built in Power Query GET /api/reports/cross-project
Saved PWA views GET /api/saved-reports
Enterprise resource pool GET /api/organizations/:id/members

The fourth row saves the most work. A large share of the Power Query in a typical PWA report is doing group-by-and-aggregate that Onplana already computes.

How do you connect Power BI to Onplana?

Three steps: mint a read-scoped token, point Power Query at the API with that token as a Bearer header, and let the server aggregate whatever it can. The diagram below shows where the credential sits in that path, which is the detail that matters most for security.

How a read-scoped token connects Power BI to the Onplana REST API Power BI Service Credential store holds pat_... Never in the .pbix file HTTPS request Authorization: Bearer pat_... Server to server Onplana API Scope check, then role-filtered rows api.onplana.com No browser is involved, so the token is never exposed to a reader The reporting path: the credential stays server side

Step 1: mint a read-only token

Go to Settings, Developer and create a Personal Access Token. Two decisions matter.

Pick read scopes only. For reporting that means:

Scope What it unlocks
PROJECTS_READ Projects, portfolios, cross-project reports, saved reports
TASKS_READ Tasks, sprints, project task lists
TIMESHEETS_READ Logged time, for effort and actual-cost reporting
MEMBERS_READ Organization members, for a people dimension

Never grant a write scope, and never grant WILDCARD, to a token that only reads. A BI tool that can create projects is a BI tool that can damage your portfolio during a misconfigured refresh. The guide to Onplana API tokens covers the wider token model, including how admins audit over-privileged tokens across an organization.

Consider scoping the token to specific projects. A token can be restricted to a named set at creation, so if your executive dashboard covers only the transformation portfolio, a leaked credential cannot read the rest of the estate. That restriction is enforced at every project-access check in the API, not filtered out of the response afterwards.

The token is shown once, as pat_ followed by a long random string. Copy it into your BI tool's credential store immediately, because it is stored as a hash and cannot be displayed again.

One useful property of the design: the token carries its own organization. The organization is read from the token record when the request authenticates, never from a request header, so there is no organization header to send and a token cannot be aimed at a different tenant.

Step 2: the first Power Query

Everything is a standard bearer token against https://api.onplana.com/api:

let
    Token   = "pat_REPLACE_ME",
    BaseUrl = "https://api.onplana.com/api",

    Fetch = (path as text, page as number) =>
        let
            Response = Web.Contents(
                BaseUrl,
                [
                    RelativePath = path,
                    Query        = [page = Text.From(page), limit = "100"],
                    Headers      = [
                        #"Authorization" = "Bearer " & Token,
                        #"Accept"        = "application/json"
                    ]
                ]
            )
        in
            Json.Document(Response),

    // Page until hasMore goes false. The null sentinel stops the loop
    // AFTER the final page is emitted, so the last page is not lost.
    Gather = (path as text) =>
        let
            Pages = List.Generate(
                () => [p = 1, r = Fetch(path, 1)],
                each [r] <> null,
                each if [r][hasMore]
                     then [p = [p] + 1, r = Fetch(path, [p] + 1)]
                     else [p = [p] + 1, r = null],
                each [r][data]
            )
        in
            List.Combine(Pages),

    Projects = Table.FromRecords(Gather("projects"))
in
    Projects

Two things about the response shape differ from a plain REST list, and both bite if you miss them:

  1. With ?page=, the response is an envelope: { data, total, page, limit, hasMore }. Page until hasMore is false.
  2. Without ?page=, the response is a bare array. That is deliberate backwards compatibility for older integrations. For BI work, always send page and limit so you get the envelope and can page reliably.

Swap "projects" for "tasks" to get the task fact table. The two join on the project id, and task rows carry assignee, status, priority, dates, estimated hours, and progress.

Step 3: let the server do the aggregation

This is the call that replaces the largest block of Power Query in most PWA reports:

GET /api/reports/cross-project?groupBy=portfolio
Authorization: Bearer pat_...

groupBy accepts status, owner, portfolio, month, or score, and every row arrives pre-aggregated:

{
  "groupBy": "portfolio",
  "rows": [
    {
      "dimension": "Digital Transformation",
      "projectCount": 14,
      "taskCount": 1902,
      "avgProgress": 61,
      "budgetSum": 2450000,
      "memberCount": 38,
      "governanceScore": 72,
      "projectScore": 68
    }
  ],
  "meta": { "totalProjects": 41, "generatedAt": "2026-09-01T09:14:22.108Z" }
}

Cross-project reporting is free on every plan, so this endpoint is available regardless of tier. The two governance score fields return null unless the plan includes governance and the caller can review it, which matches what the in-app report shows the same user.

Pull this once for dashboards that only need the rollup. Pull the detail tables for drill-through, not by default.

Step 4: schedule the refresh

Store the token in the Power BI Service as a Web API credential rather than in the query text. The snippet above hardcodes it for readability only; parameterise it and set the credential at dataset level so it never travels inside the .pbix file.

Then set the cadence against the per-minute limits, which vary by plan:

Plan Requests per minute, per token
Free 30
Starter 60
Professional 120
Business 300
Enterprise and above Unlimited

Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset, and a 429 carries Retry-After. A nightly refresh of a few hundred projects at limit=100 uses a handful of requests. If you refresh often across many datasets, mint one token per dataset: the limit is per token rather than per organization, and a per-dataset token also tells you which report misbehaved.

What about Tableau, Looker, and Metabase?

The contract is a bearer token and JSON over HTTPS, so nothing above is specific to Power BI. What changes is how each tool prefers to consume it.

  • Tableau. Use the Web Data Connector, or land the JSON in a warehouse on a schedule and point Tableau at that. Beyond a handful of visuals the warehouse route is better, because Tableau extracts and API pagination do not cooperate at volume.
  • Looker and Looker Studio. Looker expects a warehouse, so run a scheduled job that pulls these endpoints into BigQuery, Snowflake, or Postgres and model it in LookML. Looker Studio can call the API directly through a Community Connector when the model is simple.
  • Metabase. There is no native JSON-API source, so use the same pattern and let Metabase query the landed tables.

The rule for every tool except Power BI: if the tool prefers a warehouse, give it a warehouse. A nightly job that materialises projects, tasks, and timesheets into three tables is a small amount of code, it gives you history the live API does not retain, and it means no dashboard depends on an API call succeeding at the moment somebody opens it.

What is available on which plan

The analytics story spans several features and they sit on different tiers, so it is worth being precise:

Capability Available from
REST API and Personal Access Tokens Every plan
Cross-project reports and export Every plan
Gantt and baselines Every plan
Custom Dashboard builder, in app Professional
Resource capacity and workload Professional
Portfolios and portfolio RAG rollups Business
Admin-defined dashboard templates Enterprise

The API is not the gated part. A Free organization can feed Power BI from projects, tasks, and cross-project reports today. The higher tiers buy the in-app analytics surface, portfolio structure to group by, and the ability to push a standard dashboard to a role. Current tier detail is on the pricing page.

One nuance catches people out. The Custom Dashboard builder is a viewing surface, not a data source, and its endpoints are deliberately closed to tokens because they write as well as read, and a write there changes what leadership sees. To get the numbers behind a dashboard widget into Power BI, pull them from the underlying endpoints above. The same closure applies to the AI, workflow, and governance endpoints.

Can you build the dashboard as a Maker app instead?

No, and it is worth answering directly because it gets asked. Two independent reasons, either of which is enough on its own.

Maker cannot see project data. A Maker app reads Onplana data through a read binding, and a binding targets exactly two things: a workspace list, or a page. There is no binding for projects, tasks, portfolios, or timesheets. That is not a permission to widen, the entity is simply not addressable from that surface.

A published Maker app is also static and readable by anyone with the link. Publishing builds your app to static assets served from a public address. There is no server, no session, and no secret storage in the bundle, so anything inside it, including anything typed into a config file, can be read by anyone who opens the page or views source. Published apps are not search-indexed by default, which is a useful protection against accidental discovery, but not being indexed is not the same as being private.

Why a BI tool is the right home for an Onplana token and a published Maker app is not Where the credential ends up BI tool, server side Token lives in the credential store Requests run server to server Reaches projects, tasks, timesheets Revocable without a redeploy Readable by: nobody Published Maker app Static bundle, no server Token ships inside the page Cannot reach project data anyway Bindings target lists and pages only Readable by: anyone with the link A token in a published bundle is a leaked credential, not a configured one

That second point makes one specific piece of advice dangerous: do not put a Personal Access Token in a Maker app. A token is a bearer credential carrying the live role of whoever minted it. Inside a static bundle it is a credential handed to the internet, and whoever finds it gets whatever the token can reach. If it carried a write scope, they can change things too.

Onplana does scan the built bundle before publishing and will refuse to publish over a live payment key. But that scanner targets a specific set of high-signal patterns and has no rule for Onplana's own token format. Depending on how the line was written it may warn, and it may say nothing. Treat it as a backstop against one catastrophic mistake, not as a review of your credentials.

If you have already published an app containing a token, revoke it now in Settings, Developer. Revocation applies on the next request. Republishing without the token is not sufficient on its own, because the old bundle may already have been read.

What Maker is genuinely good for is a client-facing or team-facing app over workspace list data: an intake form, a status page, a small internal tool, a customer portal over a list you curate. That is a real capability, covered in building apps with Onplana. It is simply not your analytics layer.

Rebuilding row-level security

PWA security groups do not port across, and recreating them one for one is the most common way this migration runs long.

Start from how Onplana filters. Visibility is enforced at the API, and a token inherits the live organization role of the user who minted it, checked on every request rather than captured at creation. A token minted by someone without org-wide project visibility returns only the projects they own or are a member of, and every reporting endpoint respects that.

That gives you two patterns, and they compose:

  1. One service account per audience. Mint the token as a user whose role matches what that report's audience should see. The API filters, and the Power BI model never needs to know.
  2. Power BI row-level security on top. For finer cuts inside one dataset, build RLS roles on fields the API already returns: project owner, portfolio, or a department custom field.

Where PWA had a category permission granting a group access to a set of projects, the Onplana equivalent is project membership plus the permission matrix. Map those before writing any Power Query. Most PMOs find their PWA security model accumulated groups nobody can now justify, and a migration is the cheapest moment to drop them.

The sequence that wastes the least time

If you are migrating with an existing reporting layer, this order works:

  1. Inventory first. Run the Project Online inventory checklist and get the real list of datasets, reports, and scheduled refreshes. In most PWA estates a meaningful share turn out to be unopened for a year, and not rebuilding those is the single largest saving available.
  2. Rebuild the rollups before the detail. Start with GET /api/reports/cross-project. It covers most executive reporting on its own and stands up fastest.
  3. Add the detail tables. Projects and tasks, paged, on a schedule.
  4. Add effort last. Timesheets are the largest table and usually the least urgent, because actuals reporting lags adoption anyway.
  5. Map security to roles rather than porting groups.

Steps 2 and 3 are typically a day. Step 5 takes a week and is nearly always a modelling conversation rather than a technical one.

What to do next

If you are still scoping, run the Project Online inventory checklist first, because it enumerates the OData consumers that need rebuilding and tells you how much of the rebuild is real. If you already know your report inventory, start with the cross-project endpoint and get one executive dashboard live before touching the detail tables. The rest is field mapping, and field mapping is easier to argue about when something is already on screen.

If you need something the API does not expose, tell us. The endpoint set is deliberately maintained rather than auto-generated, which means gaps are decisions we can revisit, and a concrete reporting requirement is the most useful thing you can send. Write to support@onplana.com, or read more about what the platform covers on the features page.

Microsoft's retirement date and supported timeline are published on the Project Online lifecycle page.

Microsoft Project Online™ is a trademark of Microsoft Corporation. Onplana is not affiliated with Microsoft.

Power BIMicrosoft Project OnlineReportingREST APIODataPMOMigration

Frequently asked questions

How do you connect Power BI to Onplana?

Mint a Personal Access Token in Settings, Developer, with read-only scopes, then use Power BI's Web.Contents connector against https://api.onplana.com/api with the token as an Authorization Bearer header. The token is bound to one organization when it is created, so there is no organization header to send.

What replaces the Project Online /ProjectData OData feed?

The Onplana REST API, which is a rebuild of your queries rather than a repoint of a connection string. The entity mapping is close: /ProjectData/Projects becomes GET /api/projects, /ProjectData/Tasks becomes GET /api/tasks, and the portfolio rollups you hand-built in Power Query are already computed at GET /api/reports/cross-project.

Can I build my reporting dashboard as an app in Onplana Maker instead?

No, on two counts, and the second one is a security problem rather than a limitation. Maker read bindings can only target a workspace list or a page, so project and portfolio data is not addressable from that surface at all. A published Maker app is also static and readable by anyone with the link, so any credential inside it is published along with it.

Is it safe to put an API token in a published Maker app?

No. Treat that as a leaked credential from the moment you publish, and revoke it. The publish-time scanner blocks live payment keys and warns on some other credential shapes, but it has no rule for Onplana's own tokens, so it will not reliably stop you. Revocation takes effect on the next request.

Do I need a paid plan to feed Power BI from Onplana?

No. The REST API and API tokens work on every plan, and cross-project reports are free too. The tiers that matter for analytics are the in-app Custom Dashboard builder at Professional, portfolios at Business, and admin-defined dashboard templates at Enterprise, none of which gate the API itself.

How often can Power BI refresh, and what are the rate limits?

Tokens are limited per minute by plan: 30 on Free, 60 on Starter, 120 on Professional, 300 on Business, and unlimited on Enterprise and above. A nightly refresh of a few hundred projects at limit=100 uses a handful of requests, so the ceiling only matters if you refresh every few minutes across many datasets.

How do I rebuild the row-level security that used to come from PWA?

Do not port PWA category permissions one for one. Onplana filters at the API by the live role of whoever minted the token, so a token minted by someone without org-wide project visibility already returns only their projects. Mint one token per reporting audience, then layer Power BI row-level security on the fields the API returns.

Ready to make the switch?

Start your free Onplana account and import your existing projects in minutes.