Skip to main content

API Changelog: User Panel 2.5.0

User Panel API — 2.4.2 → 2.5.0 (Integrator Migration Guide)

Previous Release: 2.4.2 · New Release: 2.5.0
Audience: external integrators with existing User Panel API integrations.

This guide covers only the changes that affect existing integrations, with before/after examples and migration steps. Please review each section before upgrading.

1. Summary — What Actually Affects You

In plain terms: four of these six items are security hardening — they only break access that shouldn't have worked in the first place (viewing or touching another team's data). If your integration only ever touches its own team's resources, those four change nothing for you. One item (region scoping) is the one to actually worry about: it doesn't error, it just quietly returns less data, so it can hide in production for a while before anyone notices. The deprecation is a heads-up, not a deadline — the old endpoint still works.

Change

Who is affected

Action

Practical impact

Why this was changed

GET /regions (+/regions/sync, /instance/available-regions) returns only your team's regions when team_id is omitted

Non-admin callers that omit team_id expecting the full list

Pass an explicit scope, or expect a narrower list

🔴 High — silent. Still 200 OK, same request, just fewer rows. Nothing will throw or log an error; this can ship quietly broken. Check this one first.

Part of the same team-data-isolation pass as the rows below — regions were the one list still handed out system-wide to non-admins with no membership check at all.

DELETE /pricing-policy/{id} — new 409 cases

Clients that branch on 403 for "policy is assigned"

Handle 409 instead

🟡 Medium. Only affects clients that hard-code the old status code, or expect the record to be physically deleted rather than soft-deleted.

Hard-deleting a policy that a team is still on (or the default policy) would corrupt that team's active billing configuration and orphan historical invoices that reference it. Soft-delete keeps the record for historical billing while removing it from the active list; 409 protects the cases where deleting outright isn't safe.

GET /baremetal/instances(/{id}) now enforces team membership

Anyone relying on cross-team visibility (should not have had it)

Expect 403 for teams you're not a member of; supply a team_id you belong to

🟢 Low — security fix. Only breaks access to teams you weren't supposed to see (this was an IDOR bug). A well-behaved integration that only queries its own teams sees no change.

Closes an IDOR: the handler never checked membership, so any authenticated caller could pass any team_id and read another team's SSH host/port/user. Same defect class fixed elsewhere last cycle (HAI-9474) — this closes the two endpoints that pass missed.

11 instance endpoints now check ownership

Anyone accessing instances outside their active team memberships

Expect 403; re-check access after team removal

🟢 Low — security fix. Same pattern: only breaks stale/cross-team access. Worth a check if a service account was recently removed from a team.

The underlying instance lookup was missing an active-membership filter, so a user removed from a team could still view or send power actions to that team's instances by ID. Same IDOR class as above, found on action/detail endpoints instead of list endpoints.

Legacy GET /api/instance — same ownership check

Same as above, on the legacy endpoint

Same as above

🟢 Low — security fix. Same as above.

Same root cause as the row above (a membership JOIN missing an active-status filter) in a separate, older code path that wasn't caught in the same pass.

PUT /policy/{policy_type}/assign-team deprecated

Anyone calling this endpoint

Migrate to PUT /policy/{policy_type}/update-teams

None yet. Deprecated, not removed — the old endpoint still works this cycle. No urgency, just don't build anything new on it.

assign-team only added one team at a time, so updating multiple teams took several calls and could be left half-applied if one failed partway. update-teams replaces the full assignment set atomically in a single call.

There are no removed endpoints. 106 new endpoints were added (Token Factory, Advanced Networking, billing/metrics) — purely additive.

2. Breaking: Baremetal instance endpoints now enforce team membership

Before: GET /baremetal/instances without team_id returned every team's instances; supplying any team_id on GET /baremetal/instances or GET /baremetal/instances/{id} worked regardless of whether you belonged to that team, including SSH connection details.

After:

GET /baremetal/instances/{id} (id not in caller's teams) → 403 Forbidden GET /baremetal/instances (no team_id) → [] instead of every team's rows

Migration: always pass a team_id you belong to. Expect 403 on instance IDs outside your active team memberships.

3. Breaking: Region endpoints silently scoped to your team

GET /regions, GET /regions/sync, and GET /instance/available-regions keep the same signature — team_id is still optional — but a non-admin caller who omits it now gets only the regions available to their own active team memberships, not the full system-wide list.

Migration: if your integration expects every region regardless of team, that behavior is no longer guaranteed for non-admin credentials. This will not show up as an error — check whether your region list has gotten shorter after upgrading.

4. Breaking: Instance endpoints now check ownership

Affects: instance info, exposed-services, refresh-connections, resize options, datastores, storage-blocks, five pod-resize endpoints, and the legacy GET /api/instance.

Before: a user removed from a team could still view or send power actions to that team's instances by ID.

After: 403 Forbidden for any instance ID outside your active team memberships.

Migration: if a service account was removed from a team, expect it to lose access to that team's instances going forward — this is now enforced, not incidental.

5. Breaking: Pricing policy delete semantics changed

Before:

DELETE /pricing-policy/{id} → 200 OK, or 403 if it couldn't be deleted

After:

DELETE /pricing-policy/{id} → 200 OK (never assigned to a team — hard delete) → 200 OK (assigned in the past — soft delete, kept for historical billing) → 409 Conflict (currently assigned to a team, or is the default policy)

Migration: if you retry or branch on 403 for this endpoint, update that logic to handle 409 instead.

6. Deprecation: team policy assignment

PUT /policy/{policy_type}/assign-team is deprecated. Use:

PUT /policy/{policy_type}/update-teams

which replaces the team's full assignment set atomically in one call.

7. New Capabilities (informational, no migration required)

  • Token Factory — GPU model serving, fine-tuning, and RAG under /team/{team_id}/tf/*, plus admin and public-API surfaces.

  • Advanced Networking — private VLANs, subnets, and Elastic IPs under /team/{team_id}/network/*.

Both are purely additive.