Skip to content

Commit 0775482

Browse files
fix: idempotent-by-name note no longer misleads free-tier users (#9)
The idempotent path's note said "Delete it via DELETE /api/me/resources/{token} to provision a new one with this name" but handleDeleteResource is paid-tier only — free users hitting DELETE get 403 paid_tier_only. Advertising an endpoint the caller can't use is a worse UX than not advertising one. Branch on existing.tier: - paid: keep the DELETE instruction (correct, they can use it) - non-paid: point them at /pricing.html with a note about the 24h auto-expiry as the actual free-tier lifecycle. Same change in handleNewDB and handleNewWebhook.
1 parent 3aba2ba commit 0775482

1 file changed

Lines changed: 16 additions & 2 deletions

File tree

internal/server/handlers.go

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,15 @@ func (s *server) handleNewDB(w http.ResponseWriter, r *http.Request) {
8989
"connection_url": existing.connectionURL,
9090
"tier": existing.tier,
9191
"limits": map[string]any{"storage_mb": s.cfg.Postgres.StorageMB, "connections": s.cfg.Postgres.ConnLimit},
92-
"note": fmt.Sprintf("Returning your existing %q database. Delete it via DELETE /api/me/resources/%s to provision a new one with this name.", name, existing.token),
92+
}
93+
// Paid users can DELETE; free users can't (DELETE returns 403
94+
// paid_tier_only), so don't suggest it on the free-tier path —
95+
// that would send them to an endpoint they can't use. Steer them
96+
// to upgrade instead.
97+
if existing.tier == "paid" {
98+
resp["note"] = fmt.Sprintf("Returning your existing %q database. Delete it via DELETE /api/me/resources/%s to provision a new one with this name.", name, existing.token)
99+
} else {
100+
resp["note"] = fmt.Sprintf("Returning your existing %q database. Free-tier resources auto-expire in 24h; upgrade to Developer for manual delete + re-provision: %s/pricing.html", name, s.marketingURL)
93101
}
94102
if existing.expiresAt.Valid {
95103
resp["expires_at"] = existing.expiresAt.Time
@@ -252,7 +260,13 @@ func (s *server) handleNewWebhook(w http.ResponseWriter, r *http.Request) {
252260
"receive_url": existing.connectionURL,
253261
"tier": existing.tier,
254262
"limits": map[string]any{"requests_stored": s.cfg.Limits.WebhookMaxStored},
255-
"note": fmt.Sprintf("Returning your existing %q webhook. Delete it via DELETE /api/me/resources/%s to provision a new one with this name.", name, existing.token),
263+
}
264+
// Same caveat as handleNewDB: don't point free-tier users at a
265+
// DELETE endpoint they'd 403 on.
266+
if existing.tier == "paid" {
267+
resp["note"] = fmt.Sprintf("Returning your existing %q webhook. Delete it via DELETE /api/me/resources/%s to provision a new one with this name.", name, existing.token)
268+
} else {
269+
resp["note"] = fmt.Sprintf("Returning your existing %q webhook. Free-tier resources auto-expire in 24h; upgrade to Developer for manual delete + re-provision: %s/pricing.html", name, s.marketingURL)
256270
}
257271
if existing.expiresAt.Valid {
258272
resp["expires_at"] = existing.expiresAt.Time

0 commit comments

Comments
 (0)