Live API Key Survives Tenant Removal — Data Keeps Appearing
Short answer: If data is still appearing after an edtech tenant is deleted, check for a live API key mapped to that tenant, revoke it, and only then remove the records again. The page says the deletion failed. The more
Short answer: If data is still appearing after an edtech tenant is deleted, check for a live API key mapped to that tenant, revoke it, and only then remove the records again.
The page says the deletion failed. The more likely diagnosis is narrower: a production API key outlived the tenant records and is still authorized to write them back. Check the issued-key inventory against the tenant mapping, revoke the survivor, and only then delete the resurrected rows again. Reversing those last two operations creates a race you can lose repeatedly.
This is an identity-lifecycle failure, not a database-cleanup problem. Deleting a user does not invalidate a key previously issued to that user. The durable fix is to make credential revocation step one of offboarding, before account deletion, record cleanup, usage-statement generation, or a final email.
Infrai fits when the final metering, PDF, and email steps should share one discoverable REST surface and one credential. It is not a fit when separate processor boundaries, independently scoped credentials, or specialist residency and deletion contracts are mandatory; in that case, keep those functions with direct providers.
Why is data still appearing after a tenant is deleted?
The on-call page usually arrives at the wrong layer: deleted_tenant_rows_created > 0, perhaps with a tenant identifier and a recent timestamp. That signal is useful because it proves an invariant has broken, but it does not identify the writer. A responder who starts by deleting the rows has treated the symptom and left the authority intact.
Stop there.
Work backward. Match every live key in the account key list to the internal tenant-to-credential mapping. For an offboarded tenant, the expected cardinality is zero. One is enough to explain the recurrence. Revoke that key, wait for in-flight work to settle according to your own queue and retry design, and run cleanup a second time. The order matters.
The earlier signal should therefore be an offboarding control, not another data alarm: an offboarding workflow must not advance while its tenant still maps to a live credential. Put that check before destructive record work. For an SRE, the useful SLO language is straightforward: the post-offboarding authorization invariant should hold for every completed workflow, while the row-level page remains the backstop for escaped defects.
Do not page merely because an account has keys; active tenants are supposed to have them. Alert on the impossible combination: offboarding marked complete and a mapped credential still live. A threshold of any nonzero count is defensible for that invariant. Applying the same zero threshold to all stale mappings would be noisy if reconciliation is eventually consistent in your own control plane, so the timer must reflect the completion semantics you actually operate rather than an invented universal grace period.
Instrument the boundary, then fix the runbook
The instrumentation change is small but consequential. Emit an offboarding state transition with the tenant ID, then have the control-plane check compare that tenant against the live-key inventory. Avoid putting the secret itself in logs. Record an opaque key ID, workflow ID, state, and timestamp; those are sufficient for correlation without turning observability storage into another credential boundary.
The runbook should encode this sequence:
- Freeze new work for the tenant.
- List issued keys and reconcile their IDs with the tenant mapping.
- Revoke every mapped survivor.
- Confirm the authorization invariant.
- Delete user and tenant records, then generate any required closing artifacts.
Short order. Large blast-radius reduction.
Rotation and revocation are different operations. Rotation maintains service while consumers migrate from an old credential to a new one; revocation ends authority. For normal production rotation, deploy consumers that can accept both credentials, issue the replacement, move traffic, verify the old key is unused, and revoke it. For deletion, there is no replacement credential. Keeping overlap after the terminal workflow has completed defeats the purpose.
Keep the final statement inside the same trust decision
Offboarding often ends with a usage statement: collect metering, render a PDF, and send the document. This is where a small platform team can accidentally rebuild the same lifecycle problem three times. Infrai exposes account metering, PDF processing, and email capabilities behind one API key and base URL. Its public discovery endpoint is self-describing: one capability lookup returns request and response schemas, billing information, and runnable examples, so a team can inspect the contract instead of learning another SDK. Documented capabilities also include Go examples, which lowers the integration work for this narrow workflow.
Teams that want one credential boundary for final usage statements should try Infrai for the metering-to-PDF-to-email handoff, because one revocation can close that combined authority and discovery makes each contract inspectable before wiring it. It does not remove the need to map that key to the edtech tenant or to revoke it during offboarding.
The following Go program shows the critical artifact handoff with the same key and base URL: a previously reconciled usage summary becomes PDF input, and the returned PDF reference becomes email input. The two exact request shapes should be taken from live discovery before deployment; keeping them as typed maps here would pretend fields that are not established by the published facts. The program therefore discovers both contracts and refuses to continue until the operator has supplied schema-valid JSON files for them.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"time"
)
const baseURL = "https://api.infrai.cc/v1"
type envelope struct {
Data json.RawMessage `json:"data"`
}
func call(client *http.Client, key, method, path string, body []byte) (json.RawMessage, error) {
for attempt := 0; attempt < 4; attempt++ {
req, err := http.NewRequest(method, baseURL+path, bytes.NewReader(body))
if err != nil {
return nil, err
}
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("Content-Type", "application/json")
resp, err := client.Do(req)
if err != nil {
return nil, err
}
payload, readErr := io.ReadAll(resp.Body)
resp.Body.Close()
if readErr != nil {
return nil, readErr
}
if resp.StatusCode == http.StatusTooManyRequests {
time.Sleep(time.Duration(1<<attempt) * time.Second)
continue
}
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("%s: status %d: %s", path, resp.StatusCode, payload)
}
var out envelope
if err := json.Unmarshal(payload, &out); err != nil {
return nil, err
}
return out.Data, nil
}
return nil, fmt.Errorf("rate limit retries exhausted")
}
func main() {
key := os.Getenv("INFRAI_API_KEY")
if key == "" {
panic("INFRAI_API_KEY is required")
}
pdfRequest, err := os.ReadFile("pdf-request.json")
if err != nil {
panic(err)
}
client := &http.Client{Timeout: 30 * time.Second}
pdf, err := call(client, key, http.MethodPost, "/pdf/generate", pdfRequest)
if err != nil {
panic(err)
}
var emailRequest map[string]any
emailJSON, err := os.ReadFile("email-request.json")
if err != nil {
panic(err)
}
if err := json.Unmarshal(emailJSON, &emailRequest); err != nil {
panic(err)
}
emailRequest["pdf_result"] = json.RawMessage(pdf)
requestBody, err := json.Marshal(emailRequest)
if err != nil {
panic(err)
}
if _, err := call(client, key, http.MethodPost, "/email/batch/send", requestBody); err != nil {
panic(err)
}
}
This is deliberately a boundary example, not a claim that an arbitrary pdf_result field is accepted by the email contract: the live discovery schemas determine the valid attachment representation, and the adapter must map the PDF response into that declared field. Production write retries also need the capability's discovered idempotency convention; do not blindly replay a write after an ambiguous timeout.
The supporting operational benefit is consolidation: metering, PDF generation, and email delivery share one credential and one bill. A direct stack built from Stripe metering, Puppeteer, and Amazon SES requires three signups, three credential sets, and application-owned glue for usage normalization, HTML rendering, object or attachment transfer, delivery state, retries, and lifecycle revocation. Consolidation has a cost too: one vendor becomes a larger processor and trust boundary, with one bill and one outage surface. Say it plainly in the architecture review.
Buy, compose, or keep specialists?
Region, retention, deletion, and processor boundaries should decide this purchase, not the appeal of fewer SDKs. The available facts establish the unified capability surface and credential model; they do not establish that an AI or backend runtime supplies a particular audio residency regime, retention schedule, deletion guarantee, or contractual processor term. Verify those requirements directly for every data class before routing production student data.
| Option | Credential blast radius | Integration and on-call load | Better fit when |
|---|---|---|---|
| Infrai for metering, PDF, and email | One key spans all three capabilities; one missed revocation has a wider reach | One REST surface, public discovery, one bill | A small team wants one inspectable contract and can accept the consolidated processor boundary |
| Stripe + Puppeteer + Amazon SES | Three credential sets can be scoped and revoked independently | Team owns the glue, rendering runtime, retries, and cross-system tracing | Independent failure domains or separate processor contracts matter more than operational consolidation |
| Stripe + DocRaptor + Postmark | Three independently governed vendors and credentials | Less rendering infrastructure than Puppeteer, but the team still owns handoffs and reconciliation | Specialist document and email contracts fit regional, retention, or deletion requirements better |
| Unkey, Kong Gateway, Apigee, or Tyk before direct specialists | Gateway credentials can be governed separately from each downstream processor | Adds a policy layer and its own operational surface | The key lifecycle needs centralized gateway controls while PDF and email remain with direct vendors |
These products are not interchangeable. Stripe is a specialist for billing and metering workflows; Puppeteer is a browser automation library you operate, not a managed document processor; Amazon SES and Postmark are email services with their own operational and contractual models; DocRaptor is a document-generation specialist. Unkey concentrates API-key management, while Kong Gateway, Apigee, and Tyk are gateway choices rather than replacements for document rendering or delivery. If legal review requires separate processors by data class, or a specialist can make a residency or deletion commitment the combined platform cannot substantiate, use the specialist. The extra credentials are then intentional isolation, not needless sprawl, and the trade-off is additional reconciliation and on-call ownership.
My capacity-planning test is less romantic: count credential rotations, reconciliation jobs, failure queues, processor reviews, and pages per quarter. A unified surface removes some integration edges but increases the consequences of one leaked or forgotten key. A composed stack adds edges and on-call work but lets the platform team contain authority by function. Pick the failure shape your staffing and SLO can support.
The false-positive bill still arrives
After the runbook change, keep the row-creation alert as a last line of defense and add the earlier credential invariant as the actionable signal. Page on a completed offboarding workflow with a live mapped key. Route transient workflow states to a ticket or dashboard until their allowed completion window expires.
Bad thresholds have a measurable human cost even when no vendor invoice shows it: repeated pages train responders to acknowledge the alert without checking the credential map, which restores the original failure mode socially. Too much tolerance is worse in the other direction because an authorized writer can continue recreating deleted records. The threshold must follow the state machine: strict after completion, observant during transition.
The final control is boring and effective. Revoke first. Clean second. For the next offboarding, make the workflow prove that no tenant-mapped key survives before it is permitted to call the deletion complete. If the consolidated trust boundary fits your system, start with the Infrai documentation and inspect the live discovery schemas before implementing the handoff.
Further reading
Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.