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.
| Card | Where it applies | Source | Key scope |
|---|---|---|---|
| Health: score, level, domains, test coverage and licenses allowed | All five | Diagnosis of each repository | maturity:read |
| Security: open findings by severity | All five | latest measurement of each repository | maturity:read |
| Test coverage, with its history | All five | measured executed lines | maturity:read |
| Evolution: the score over 180 days, with level bands | All five | measurements kept | maturity:read |
| Licenses: components by family, against your policy | All five | license inventory and your policy | evidence:read |
| Compliance: one regime's controls and the audit dossier | All five | controls engine | evidence:read |
| AI: spend by source, people using AI and durability | Organization, unit, team and area | provider readings and ScaleQuality AI | durability:read |
| People and cost: AI per person, people using AI and people cost | Organization, unit, team and area | AI readings, and people times the average salary | business:read |
| Initiatives: real AI cost against the plan, and the projected saving | Organization, team and area | AI Portfolio | business:read |
| Engineering maturity: L1 to L5 and six domains | Organization, unit and team | assessments | maturity:read |
| Technology landscape, counted by ring | Organization, unit and team | configured radar | catalog:read |
All five means an organization, a business unit, a team, a business area or a repository.
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.
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.
- In ScaleQuality, open Developers and choose the API tab.
- Press Create key and give it a name, for example Backstage production.
- Leave Share people cost and initiatives unchecked, unless the portal should show those two cards.
- Copy the key. It starts with sq_live_.
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.
proxy:
endpoints:
'/scalequality':
target: https://app.scalequality.io/v1
changeOrigin: true
headers:
Authorization: 'Bearer ${SCALEQUALITY_API_KEY}'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.
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.
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.
| Endpoint | Returns |
|---|---|
GET /v1/catalog | Your org, its business units, teams, business areas and repositories, with the ids you use in the annotations. |
GET /v1/entity/:scope/:id | Every signal the entity is read for and the key carries, in a single response. This is what the card reads. |
GET /v1/health/:scope/:id | Health: score, level, domains, test coverage and licenses allowed. |
GET /v1/security/:scope/:id | Open security findings by severity. |
GET /v1/coverage/:scope/:id | Test coverage, with its history. |
GET /v1/evolution/:scope/:id | The score over the last 180 days. |
GET /v1/licenses/:scope/:id | Components by license family and their status against your policy. |
GET /v1/compliance/:scope/:id | One 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/:id | AI spend by source, people using AI and durability. |
GET /v1/people-cost/:scope/:id | People, their monthly cost and AI spend per person. Needs business:read. |
GET /v1/initiatives/:scope/:id | Initiatives with planned and real cost and the projected saving. Needs business:read. |
GET /v1/eng-maturity/:scope/:id | Engineering maturity level, with its breakdown. |
GET /v1/radar/:scope/:id | Technology landscape, counted by ring. |
GET /v1/code-maturity/:scope/:id | Code maturity score and sub-scores from the zero-config Diagnosis. |
GET /v1/durability/:scope/:id | AI durability: survival rate, human baseline and the rework it implies. |
GET /v1/summary/:scope/:id | One compact headline for the scope, with provenance and a deep link. |
GET /v1/entities | Every 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.
{
"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 see | What it means | Fix |
|---|---|---|
| The card shows a missing-annotation empty state | The 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 proxy | The 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 exists | That 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 appear | The 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 area | That 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 state | The 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 Backstage | The 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. |