Files
descrybe/docs/plan-permissions/05-api.md
T
greeneclipse 8580c996c3 Initial commit of Descrybe v2 without local scratch artifacts.
Drop one-shot tmp/axe scripts and agent i18n scratch so the Gitea tree is deployable.
2026-08-09 22:47:43 +02:00

4.9 KiB

05 — Plan permissions API (agent 5/10)

Session/dashboard HTTP surface for plan feature permissions. Aligns with 03-permission-contract.md. Public /api/v1 OpenAPI is unchanged (API-key product API); schemas below are the session contract for admin UI + dashboard gating.

Backend storage + resolve live in billing per 04-backend-model.md / contract §2 / §6 (plans.features JSONB + platform_feature_gates). Migration: apps/api/sql/schema/026_plan_features.sql. Apply with make migrate / .\scripts\migrate.ps1 before relying on writes.

Handlers call: CapabilitiesForCompany, GetPlanFeatures, SetPlanFeatures, EnableAllPlanFeatures, GetFeatureGates, SetFeatureGates, SetSectionGate (and agent-4 aliases EnableAllForPlan / SetGlobalFeature / AssertFeature).


Endpoint list + auth

Method Path Auth Purpose
GET /api/billing/capabilities Session + company Effective features = plan ∩ global
GET /api/billing/credits Session + company Existing credits overview plus features, disabled_features, feature_etag
GET /api/auth/me Session Embeds credits overview (same feature fields when credits load)
GET /api/admin/plans Session + platform admin Plans include features (overrides) + resolved_features (plan_allows)
POST /api/admin/plans Session + platform admin Upsert plan; optional features replaces overrides when present
GET /api/admin/plans/{planID}/features Session + platform admin Plan feature editor payload
PUT /api/admin/plans/{planID}/features Session + platform admin Replace overrides { "features": { "key": true } }
POST /api/admin/plans/{planID}/features/enable-all Session + platform admin Set every registry key true (custom packages)
GET /api/admin/feature-gates Session + platform admin Global section + feature master switches
PUT /api/admin/feature-gates Session + platform admin Partial upsert { "sections": {...}, "features": {...} }
PUT /api/admin/feature-gates/sections/{section} Session + platform admin Enable/disable one section for ALL plans

Mutations require CSRF (dashboard session stack) + platform admin for /api/admin/*.

Unknown feature keys / sections → 400 { "error": "…" } via billing.ClientError.


Response shapes

GET /api/billing/capabilities

{
  "plan_id": 3,
  "plan_name": "Starter",
  "is_custom": false,
  "has_active_plan": true,
  "features": { "catalog.products": true, "settings.api_keys": true },
  "sections": { "catalog": true, "marketing": true },
  "disabled_features": ["integrations.ai.byok"],
  "feature_etag": "sha256:…",
  "entitlements": {
    "plan_name": "Starter",
    "is_free_plan": false,
    "is_paid_plan": true,
    "can_use_ai": true,
    "can_use_eprel": true
  }
}

GET|PUT /api/admin/plans/{planID}/features

{
  "plan_id": 3,
  "plan_name": "Starter",
  "is_custom": false,
  "features": { "settings.api_keys": true },
  "resolved_features": { "catalog.products": true, "integrations.ai.byok": false }
}
  • features = stored sparse overrides ({} if none)
  • resolved_features = full registry via plan_allows only (globals ignored)

GET|PUT /api/admin/feature-gates

{
  "sections": { "shell": true, "marketing": false },
  "features": { "capability.byok": false }
}

Missing gate rows default enabled (migration-safe).

PUT /api/admin/feature-gates/sections/{section}

{ "enabled": false }

Returns full FeatureGatesView.


OpenAPI (session fragment)

See docs/plan-permissions/05-openapi-fragment.yaml for OpenAPI 3.1 path/components suitable for admin/dashboard clients. Not mounted on /api/v1/openapi.yaml.


Files touched (agent 5)

File Intent
apps/api/internal/httpapi/plan_features_handlers.go Handlers
apps/api/internal/httpapi/server.go Route mount
apps/api/internal/billing/plan_features.go Service + DTOs (shared with agent 4)
apps/api/internal/billing/feature_catalog.go Registry from 01-feature-keys.json
apps/api/internal/billing/service.go Plan/CreditsOverview/ListPlans/UpsertPlan
apps/api/internal/billing/client_errors.go Client-facing feature errors
apps/api/sql/schema/026_plan_features.sql Additive migration

Manual verify

  1. .\scripts\migrate.ps1 (or make migrate)
  2. Platform admin: GET /api/admin/feature-gates → all sections true
  3. PUT /api/admin/feature-gates/sections/marketing { "enabled": false }
  4. Tenant session: GET /api/billing/capabilities → marketing keys in disabled_features
  5. POST /api/admin/plans/{id}/features/enable-all then GET → all resolved_features true
  6. Non-admin PUT /api/admin/feature-gates → 403