diff --git a/apps/api/src/routes/admin-workspace-delete.test.ts b/apps/api/src/routes/admin-workspace-delete.test.ts index a6b54084..ddd7692b 100644 --- a/apps/api/src/routes/admin-workspace-delete.test.ts +++ b/apps/api/src/routes/admin-workspace-delete.test.ts @@ -426,12 +426,23 @@ describe("GET /admin/workspaces/:name", () => { purgeAt: null, selfServe: false, plan: null, + paidSince: null, hasHttpCredentials: false, }); // Unset key policy reads as the permissive default, not as absent. expect(body.keyPolicy).toMatchObject({ autoPrefixBareKeys: true, allowedKeyPrefixes: null }); }); + it("exposes paidSince for a paid workspace", async () => { + const { app, env } = appWith({ + kvRecords: { "ws:acme": { ...RECORD, plan: "pro", paidSince: "2026-01-02T03:04:05.000Z" } }, + }); + const res = await app.request(getRequest("acme"), {}, env); + expect(res.status).toBe(200); + const body = (await res.json()) as { plan: string; paidSince: string | null }; + expect(body).toMatchObject({ plan: "pro", paidSince: "2026-01-02T03:04:05.000Z" }); + }); + it("reports a soft-deleted workspace with its grace window instead of 404ing", async () => { const { app, env } = appWith({ kvRecords: { "ws:acme": RECORD } }); await app.request(deleteRequest("acme"), {}, env); diff --git a/apps/api/src/routes/admin.ts b/apps/api/src/routes/admin.ts index 2e397f37..2e55edee 100644 --- a/apps/api/src/routes/admin.ts +++ b/apps/api/src/routes/admin.ts @@ -558,6 +558,10 @@ export const admin = new Hono<{ Bindings: Env }>() prefix: raw.prefix ?? null, publicBaseUrl: raw.publicBaseUrl ?? null, plan: raw.plan ?? null, + // Billing lifecycle, mirrored from the cookie-authed admin panel's plan + // view so bearer-token operators (ops scripts, CI) see the same + // "customer since" answer. + paidSince: raw.paidSince ?? null, selfServe: raw.selfServe === true, createdAt: raw.createdAt ?? null, createdByUserId: raw.createdByUserId ?? null,