HTTP Endpoints
PowerPortalsPro publishes a JSON-over-HTTP surface under /api/* that any single-page application can call — the in-box Blazor WebAssembly client uses it to back IPowerPortalsProService and IAuthService, but the same endpoints are equally usable from a React, Vue, or vanilla-JS front end hosted alongside (or even cross-origin from) the server.
Who this is for
If you're building a Blazor app and consuming the framework via
IPowerPortalsProService, you do not need to call these endpoints directly — the client implementation does it for you. This page documents the surface for teams writing a non-Blazor SPA, or wiring up a custom HTTP client, against the same server.
Enabling the endpoints
UsePowerPortalsProWebServer wires the data endpoints (table CRUD, FetchXML, metadata, files, localization, admin). MapAuthEndpoints<TUser> wires the SPA-facing auth surface — call it explicitly if your host needs cookie-auth from a SPA.
// Program.cs — server pipeline
app.UsePowerPortalsProWebServer();
app.MapAuthEndpoints<PortalUser>();
Both calls are no-ops when their feature isn't in use, so wire them once in Program.cs regardless of which interactivity mode the host runs.
The Routes type — single source of truth
PowerPortalsPro.Web.Common.Routes exposes every endpoint path as a strongly-typed property. Both the in-box client and any C#-based external SPA should reference these constants instead of hand-writing strings, so a server-side rename surfaces as a compile error rather than a runtime 404. JavaScript/TypeScript clients will of course need to inline the paths, but the C# side of Routes remains the canonical reference for what those paths are.
// Anywhere — both PowerPortalsPro.Web.Client and external C# SPAs.
var loginUrl = Routes.Api.Auth.Login; // "/api/auth/login"
var meUrl = Routes.Api.Auth.Me; // "/api/auth/me"
var createUrl = Routes.Api.Tables.GetCreateRoute("contact");
var fetchUrl = Routes.Api.GetRetrieveMultipleRoute(fetchXml);
Routes.Api covers the data and admin endpoints; Routes.Api.Auth covers sign-in / sign-up; Routes.Api.Auth.Manage covers the signed-in user's account-management operations.
Data endpoints
Backed by UsePowerPortalsProWebServer. Every read and write applies the consumer's ITablePermissionHandler / ITableRecordPermissionHandler interceptors, plus any IFetchXmlBuilderInterceptor registered for the table.
POST /api/table/{table},GET /api/table/{table}/{id},PATCH /api/table/{table}/{id},DELETE /api/table/{table}/{id}— single-record CRUD. The?columns=query parameter on GET narrows the projection.GET /api/retrieveMultiple?fetchXml=…— runs an arbitrary FetchXML query. UseRoutes.Api.GetRetrieveMultipleRoute(fetchXml)to encode the URL correctly.POST /api/executeMultiple?returnResponses=true|false— body is a JSON array ofOrganizationRequest. Wrapping multiple ops in one call gives them transactional semantics; if any single request fails the whole batch rolls back.GET /api/tableMetadata/{table},GET /api/viewMetadata/{viewIdOrTable}— column / relationship / view metadata. Cached server-side, so repeat calls are cheap.GET /api/files/{table}/{recordId}/{column}?includeData=true|falsefor single-file read;POST /api/files/{table}/{column}/batch?includeData=…with a JSON array of record IDs for batch reads (used by the FileGrid's Download Selected button).GET /api/localizedStrings/{culture}for a full dump;POST /api/localizedStrings/{culture}/keyswith a JSON list of keys / prefixes for narrow pre-fetches.POST /api/caches/cleardrops every server-side cache;POST /api/caches/{cacheName}/cleardrops a single named cache;GET /api/cacheslists the registered cache names. All three are gated by[Authorize(Roles = "SystemAdmin")]; only signed-in admins can call them.
Authentication endpoints
Backed by MapAuthEndpoints<TUser>. Cookie-based: a successful sign-in sets the standard ASP.NET Core Identity application cookie, and every subsequent call (data or auth) authenticates by presenting that cookie back to the server.
POST /api/auth/login,POST /api/auth/login/2fa,POST /api/auth/logout— credentials sign-in plus a second-factor follow-up if the account has 2FA enabled.POST /api/auth/register,POST /api/auth/forgot-password,POST /api/auth/reset-password,POST /api/auth/confirm-email,POST /api/auth/resend-email-confirmation— full lifecycle for local accounts.GET /api/auth/external-providers,GET /api/auth/external-login?provider=…,POST /api/auth/external-login/confirm,POST /api/auth/external-login/select— OAuth (Microsoft, Google, etc.). Theexternal-loginendpoint must be navigated to (not fetched) so the browser follows the OAuth redirect chain.GET /api/auth/me— snapshot of the current principal: id, name, email, roles, backing table (contactvs.systemuser). Returns an anonymous shape (isAuthenticated: false) when no cookie is present rather than 401, so a SPA can call it on first paint without branching on status code./api/auth/manage/*— signed-in account management: profile, password, email change, 2FA enrollment / disable / recovery codes, linked external logins. Mirrors the operations the framework's classic/Account/ManageRazor pages performed viaUserManager.GET /api/auth/manage/personal-data,POST /api/auth/manage/personal-data/delete— GDPR-style export and account deletion.
Authentication model — cookies, not tokens
Auth is browser-cookie based. There is no JWT issuance step. A SPA calls /api/auth/login, the server sets the .AspNetCore.Identity.Application cookie on the response, and the browser attaches it on every subsequent request — including data calls under /api/table/*. From JavaScript this means fetch(..., { credentials: 'include' }) on every call (or axios.defaults.withCredentials = true); without it the cookie is dropped and the server returns 401.
// React / Vue / plain fetch — credentials: 'include' is required so the
// browser sends and stores the auth cookie issued by /api/auth/login.
const me = await fetch('/api/auth/me', { credentials: 'include' })
.then(r => r.json());
if (me.isAuthenticated) {
console.log(me.userName, me.roles);
}
Cross-origin SPAs
If the SPA and server are on different origins, the server must send
Access-Control-Allow-Origin: <spa-origin>(not*) andAccess-Control-Allow-Credentials: true, and the SPA must usecredentials: 'include'. Same-origin hosting (the SPA served from the same site as the API) avoids the issue entirely.
Error responses
Failed calls return RFC 9457 application/problem+json. The in-box client implementation rehydrates the original CLR exception type from the problem details so server-side throws surface as the same exception on the client; for non-.NET SPAs, the type / title / detail / status fields are the standard handle.
See also
Related documentation:
IPowerPortalsProService — the C# wrapper around these endpoints — what your Blazor components inject when they don't need to issue raw HTTP themselves.SystemUser sign-in — background on why/api/auth/mereportstableName: "systemuser"for some principals andtableName: "contact"for others.
