Control-plane API reference
All routes are served by api-next. Authentication varies per endpoint and is
called out in each section.
Endpoint summary
| Endpoint | Method | Auth | Purpose |
|---|---|---|---|
/license/keys | POST | Admin bearer | Mint a license key |
/license/activate | POST | Key in body | Exchange a key for an activation token |
/license/jwks | GET | Public | Publish the JWT verification key |
/telemetry/download | POST | Registry bearer | Record a package-download event |
/boxes/deploy | POST | License key in body | Register box domains |
/boxes/deploy/status | GET | License-key bearer | Check deployment status |
/boxes/resolve | GET | Public | Resolve a host to a backend (planned) |
Licensing
POST /license/keys
Mint a new license key. Admin-only (bearer secret).
Request — MintLicenseDto:
{
"customerId": "cus_123",
"tier": "growth",
"expiresAt": "2027-01-01T00:00:00.000Z"
}| Field | Type | Required | Notes |
|---|---|---|---|
customerId | string | yes | Opaque customer identifier |
tier | 'basic' | 'growth' | 'enterprise' | yes | Determines entitled packages |
expiresAt | ISO date string | no | Omit for non-expiring keys |
Response — the plaintext license key, returned once. Store it securely; it cannot be retrieved again.
POST /license/activate
A box exchanges its license key for an activation token plus its entitled package set. No prior auth — the key is the credential.
Request — ActivateDto:
{ "key": "pk_live_..." }Response — ActivationResult:
{
"tier": "growth",
"customerId": "cus_123",
"token": "<RS256 JWT>",
"expiresIn": 3600,
"packages": ["@withpotter/box-core", "@withpotter/product", "..."],
"registryUrl": "https://registry.withpotter.com"
}| Field | Type | Notes |
|---|---|---|
token | string (JWT) | RS256, short-lived; used as the registry token |
packages | string[] | Dependency-closed entitled set for the tier |
registryUrl | string | Gated registry the CLI installs from |
expiresIn | number | Token TTL in seconds (default 3600) |
See Licensing for the JWT claim shape.
GET /license/jwks
Publish the public key used to verify activation tokens. Consumed by the gated registry.
Response:
{
"algorithm": "RS256",
"issuer": "withpotter-control-plane",
"publicKey": "-----BEGIN PUBLIC KEY-----\n..."
}POST /telemetry/download
The gated registry reports each package download. Registry-auth only; fire-and-forget.
Request — TelemetryDownloadDto:
{
"customerId": "cus_123",
"tier": "growth",
"packageName": "@withpotter/product",
"version": "1.7.3",
"outcome": "allowed"
}| Field | Type | Notes |
|---|---|---|
outcome | 'allowed' | 'denied' | Whether the registry served the package |
version | string | Optional |
Response: 202 Accepted → { "accepted": true }.
Deployment
POST /boxes/deploy
Register a box’s domains. Authenticated with the license key in the body.
Request — DeployBoxDto:
{
"key": "pk_live_...",
"name": "acme-bank",
"backendUrl": "https://api.acme-bank.com",
"frontendUrl": "https://acme-bank.com",
"domains": ["acme-bank.com", "*.shop.acme-bank.com"]
}Response — DeployBoxResult:
{
"registered": ["acme-bank.com", "*.shop.acme-bank.com"],
"nameservers": ["ns1.vercel-dns.com", "ns2.vercel-dns.com"]
}The vendor delegates the returned nameservers at their registrar. See Deployment.
GET /boxes/deploy/status
Check a box’s deployment status. License key passed as a bearer token in the
Authorization header; the box name is a query parameter.
GET /boxes/deploy/status?name=acme-bank
Authorization: Bearer pk_live_...Host resolution (planned)
GET /boxes/resolve
This endpoint is planned and not yet implemented in api-next. The storefront
render app already calls it (with a client-side stub fallback) to map a public
host to the bank engine backend that serves it. See
Storefront render.
GET /boxes/resolve?host=shop.acme.comIntended response:
{
"backendUrl": "https://api.acme-bank.com",
"match": "exact",
"tenantSlug": "acme-flagship"
}| Field | Type | Notes |
|---|---|---|
match | 'exact' | 'wildcard' | wildcard carries tenantSlug; exact requires a backend lookup |
tenantSlug | string | Present for wildcard matches |