Request a diagnosis

A specialist reads your repositories with you, on a call.

Integrate

Backstage plugin

Render ScaleQuality's measured signals as entity cards in Backstage: health, security, compliance, licenses, test coverage, engineering maturity, AI, people and cost, initiatives, evolution and the technology landscape, for the organization, business unit, team, business area or repository the entity maps to. Each card links back into ScaleQuality for the full story.

What the card shows

Up to eleven cards, each read live for the entity the catalog entry is annotated with. A card appears only where it applies and when your key carries it. The cards are theme-aware, so they stay legible in both Backstage themes.

CardWhere it appliesSourceKey scope
Health: score, level, domains, test coverage and licenses allowedAll fiveDiagnosis of each repositorymaturity:read
Security: open findings by severityAll fivelatest measurement of each repositorymaturity:read
Test coverage, with its historyAll fivemeasured executed linesmaturity:read
Evolution: the score over 180 days, with level bandsAll fivemeasurements keptmaturity:read
Licenses: components by family, against your policyAll fivelicense inventory and your policyevidence:read
Compliance: one regime's controls and the audit dossierAll fivecontrols engineevidence:read
AI: spend by source, people using AI and durabilityOrganization, unit, team and areaprovider readings and ScaleQuality AIdurability:read
People and cost: AI per person, people using AI and people costOrganization, unit, team and areaAI readings, and people times the average salarybusiness:read
Initiatives: real AI cost against the plan, and the projected savingOrganization, team and areaAI Portfoliobusiness:read
Engineering maturity: L1 to L5 and six domainsOrganization, unit and teamassessmentsmaturity:read
Technology landscape, counted by ringOrganization, unit and teamconfigured radarcatalog:read

All five means an organization, a business unit, a team, a business area or a repository.

Note. A card with no measurement yet renders its empty state, never a fake value or a zero. It means nobody has measured that entity yet, and the card says so instead of guessing.

Each card carries its provenance: MEASURED when the number came out of your environment, and DECLARED when somebody configured it (the technology landscape, or a headcount declared for a business area). People cost is the one figure built on a value your company set, its average salary, and the card says so.

For a business area or a repository, Compliance reads the entity's own repositories. The controls that are kept per team come from the teams that own those repositories: the card names them, says how many repositories no team owns, and says they are not applicable when no team owns any. They are never counted as met.

Important. People and cost and Initiatives are never part of a key's default read. A team's people cost over its headcount is its average salary, so those two cards appear only when the key was created with Share people cost and initiatives checked. People cost is hidden below three people, and no card shows one person's figure.

The card reads the public read-only Platform API through the Backstage backend proxy, so your org-scoped key is injected server-side and never reaches the browser.

Before you start

What you need

  • A Backstage app you can edit and redeploy.
  • Permission to create an API key in ScaleQuality, which means owner or admin.
  • A place to keep that key in your Backstage backend environment or secret store.
  • At least one entity measured in ScaleQuality, otherwise every card renders its empty state.

This guide targets @scalequality/backstage-plugin 0.2.2, the version currently published on npm.

Install and configure

Five steps: add the package, create the key, configure the proxy, add the card, and tell Backstage which ScaleQuality entity each catalog entry maps to.

Install the plugin

Add the package to your Backstage frontend app.

# from your Backstage app root
yarn --cwd packages/app add @scalequality/backstage-plugin@^0.2.2

Create an API key in ScaleQuality

The key is scoped to your organization and read-only: it can read summaries for your org and nothing else. Name it after where it runs, so a rotation later is obvious.

  1. In ScaleQuality, open Developers and choose the API tab.
  2. Press Create key and give it a name, for example Backstage production.
  3. Leave Share people cost and initiatives unchecked, unless the portal should show those two cards.
  4. Copy the key. It starts with sq_live_.
Important. The full key is shown once. If it is lost, rotate it on the same page: rotation issues a new secret and stops the old one immediately, so update Backstage before you rotate, not after.

Store it as SCALEQUALITY_API_KEY in the environment of your Backstage backend, or in whatever secret store that backend already reads. It must never be part of the frontend bundle.

Configure the backend proxy

Add the proxy endpoint to your app config. The plugin calls it by the id scalequality.

app-config.yaml
proxy:
  endpoints:
    '/scalequality':
      target: https://app.scalequality.io/v1
      changeOrigin: true
      headers:
        Authorization: 'Bearer ${SCALEQUALITY_API_KEY}'
Note. The proxy is what keeps the key server-side. The browser calls your own Backstage backend, and the backend adds the Authorization header. Pointing the plugin straight at the API would ship the key to every user of the portal.

Add the card to your entity page

Render the card wherever the entity overview lives. It lays its cards out in one, two or three columns according to the width of the grid item you give it.

packages/app/src/components/catalog/EntityPage.tsx
import { EntityScaleQualityCard } from '@scalequality/backstage-plugin';

// inside the entity page grid, e.g. the overview content:
<Grid item md={6}>
  <EntityScaleQualityCard />
</Grid>

Map entities to a ScaleQuality scope

The card needs to know which ScaleQuality entity a catalog entry represents. Annotate the Component or Group with the matching id.

catalog-info.yaml
metadata:
  annotations:
    scalequality.io/repo-id: <project-uuid>   # a repository, or one module of a monorepo
    # or: scalequality.io/team-id: <team-uuid>
    # or: scalequality.io/area-id: <business-area-uuid>
    # or: scalequality.io/bu-id: <bu-uuid>
    # or: scalequality.io/org-id: <org-uuid>

The most specific annotation wins: repository over team, team over business area, then business unit, then organization. A repository id can also be one module of a monorepo. An entity with none of them renders the missing-annotation empty state rather than a number that belongs to somebody else.

The ids come from the catalog endpoint, or from Developers, under IDP, where each widget shows its endpoint for the entity you pick.

curl https://app.scalequality.io/v1/catalog \
  -H "Authorization: Bearer sq_live_..."

API reference

Everything the plugin reads is public, read-only and gated by the API key. The same endpoints back any other internal developer portal, Port included.

EndpointReturns
GET /v1/catalogYour org, its business units, teams, business areas and repositories, with the ids you use in the annotations.
GET /v1/entity/:scope/:idEvery signal the entity is read for and the key carries, in a single response. This is what the card reads.
GET /v1/health/:scope/:idHealth: score, level, domains, test coverage and licenses allowed.
GET /v1/security/:scope/:idOpen security findings by severity.
GET /v1/coverage/:scope/:idTest coverage, with its history.
GET /v1/evolution/:scope/:idThe score over the last 180 days.
GET /v1/licenses/:scope/:idComponents by license family and their status against your policy.
GET /v1/compliance/:scope/:idOne regime's controls by status, the open ones first, and the latest audit dossier. Pass ?regime= for a regime other than the first.
GET /v1/ai/:scope/:idAI spend by source, people using AI and durability.
GET /v1/people-cost/:scope/:idPeople, their monthly cost and AI spend per person. Needs business:read.
GET /v1/initiatives/:scope/:idInitiatives with planned and real cost and the projected saving. Needs business:read.
GET /v1/eng-maturity/:scope/:idEngineering maturity level, with its breakdown.
GET /v1/radar/:scope/:idTechnology landscape, counted by ring.
GET /v1/code-maturity/:scope/:idCode maturity score and sub-scores from the zero-config Diagnosis.
GET /v1/durability/:scope/:idAI durability: survival rate, human baseline and the rework it implies.
GET /v1/summary/:scope/:idOne compact headline for the scope, with provenance and a deep link.
GET /v1/entitiesEvery org, business unit, team, business area and repository with its signals, in one array. Built for bulk sync: compliance, AI, people cost and initiatives are read per entity and are left out of it.

scope is one of org, bu, team, area or repo, and id is the uuid from the catalog. A key only reads inside its own organization: an id from another tenant answers 403 or 404, an id that does not exist answers 404, and any other scope value answers 400. A widget asked for a kind of entity it is not read for also answers 400. A key that does not carry the scope a widget needs answers 403 on that widget's endpoint, and on the entity endpoint that signal is simply left out.

Response shape

Every endpoint answers with the same envelope: the subject, when it was measured, and one entry per signal carrying the value, its unit, a status, its provenance and a deep link back into ScaleQuality.

GET https://app.scalequality.io/v1/entity/team/<team-uuid>
{
  "subject":    { "type": "team", "id": "...", "name": "Payments" },
  "measuredAt": "2026-10-10T09:12:44.108Z",
  "signals": {
    "health":      { "value": 74, "unit": "score", "status": "warn",
                     "provenance": "MEASURED", "data": { ... }, "deepLinkUrl": "..." },
    "security":    { "value": 12, "unit": "count", "status": "warn",
                     "provenance": "MEASURED", "data": { ... }, "deepLinkUrl": "..." },
    "compliance":  { "value": 9,  "unit": "count", "status": "warn",
                     "provenance": "MEASURED", "data": { ... }, "deepLinkUrl": "..." },
    "engMaturity": { "value": 3,  "unit": "level", "status": "warn",
                     "provenance": "MEASURED", "data": { ... }, "deepLinkUrl": "..." },
    "techRadar":   { "value": 42, "unit": "count", "status": "ok",
                     "provenance": "DECLARED", "data": { ... }, "deepLinkUrl": "..." },
    "licenses": { ... }, "coverage": { ... }, "evolution": { ... }, "ai": { ... },
    "peopleCost": { ... }, "initiatives": { ... },
    "durability": { ... }, "codeMaturity": { ... }
  }
}

status is one of ok, warn, risk or unknown. provenance is part of the contract, not decoration: MEASURED means the number came out of your environment, and DECLARED means somebody configured it. A null value means it has not been measured yet, and the card renders its empty state for it.

Troubleshooting

Almost every failed install lands on one of these.

What you seeWhat it meansFix
The card shows a missing-annotation empty stateThe entity carries none of the five ScaleQuality annotations.Add scalequality.io/repo-id, team-id, area-id, bu-id or org-id to the entity and reload the catalog.
401 from the proxyThe key was revoked or rotated, or SCALEQUALITY_API_KEY is not set in the backend environment.Confirm the variable exists in the backend that serves the proxy, and that the key is still active under Developers.
403 for an id that existsThat id belongs to a different organization than the key does.Take the ids from the catalog endpoint called with this key, not from another tenant.
People and cost or Initiatives never appearThe key does not carry business:read, which is never part of a key's default read.Under Developers, API, create a key with Share people cost and initiatives checked, and point the proxy at it.
A card is missing for a repository or a business areaThat card is not read for that kind of entity. Engineering maturity and the technology landscape exist for an organization, a unit or a team.Nothing to fix. The table at the top of this page says where each card applies.
Every card renders its empty stateThe entity resolves, but nothing has been measured for it yet.Connect and measure a repository, complete an assessment, or connect an AI provider for that entity. The cards fill in as measurements land.
The request never leaves BackstageThe proxy id does not match. The plugin calls the endpoint registered as scalequality.Keep the key in app-config exactly as it appears in step 3, slash scalequality.

See it on your own code.

Request a diagnosis. A specialist reads your repositories with you, on a call, and you leave with the first reading in hand.

Trusted AI. Measured, governed and proven.