DEV Community

kongkong
kongkong

Posted on

"Make AI Budget Exhaustion a Full-Stack API Contract"

The browser submits a summary job, the worker reaches the provider after the hard spend threshold, and the API call fails. If every layer translates that into generic 500, the UI offers a retry button that can create a loop. Budget exhaustion needs a typed end-to-end contract, not a billing dashboard alone.

OpenAI's July 20 release notes add organization and project spend limits and state that hard limits may cause API responses to fail after the threshold. Treat availability exactly as documented for the applicable product and account. This is the latest verified official signal for this behavior, not news released July 27; I excluded unverified secondary July 27 stories.

Define the provider seam

Do not leak provider text through your application. Normalize only errors you can classify with evidence:

type AiFailure =
  | { kind: "budget_exhausted"; retryable: false; scope: "project" | "organization" | "unknown" }
  | { kind: "rate_limited"; retryable: true; retryAfterMs?: number }
  | { kind: "provider_unavailable"; retryable: true }
  | { kind: "unknown"; retryable: false; incidentId: string };

type JobState =
  | { status: "queued"; id: string }
  | { status: "complete"; id: string; result: string }
  | { status: "blocked_budget"; id: string; resumeToken: string }
  | { status: "failed"; id: string; incidentId: string };
Enter fullscreen mode Exit fullscreen mode

Map budget_exhausted only when the SDK's current structured status/code supports that classification. A guessed string match can mislabel authorization or outage failures. Unknown remains unknown and pages an owner.

Persist before calling

POST /api/summaries must accept an idempotency key, persist {"status":"queued","id":"job_91"}, and only then enqueue work.

Worker pseudocode:

async function execute(job: StoredJob) {
  if (job.status !== "queued") return;
  try {
    const result = await ai.summarize(job.input);
    await store.completeIfQueued(job.id, result);
  } catch (raw) {
    const error = classifyProviderError(raw);
    if (error.kind === "budget_exhausted") {
      await store.blockIfQueued(job.id, makeOpaqueResumeToken());
      return; // no automatic provider retry
    }
    await store.failOrScheduleBoundedRetry(job.id, error);
  }
}
Enter fullscreen mode Exit fullscreen mode

Conditional writes prevent late overwrites; public records exclude secrets and sensitive responses.

Browser behavior

Poll or stream the durable state. For exhaustion, return a stable application response:

Return 409 application/problem+json with type ai-budget-exhausted, job ID, canRetry:false, and any application-estimated retry time clearly labeled. Preserve input, disable immediate retry, offer cancellation, and leave limit decisions to an authorized operator.

Cross-layer test matrix

Case Provider seam Stored state HTTP/UI Expected calls
normal result complete render summary 1
hard limit budget exhausted blocked paused, no retry 1
duplicate click none/new enqueue prevented existing state same job 0 extra
unknown error unknown failed incident reference 1
budget restored result after operator resume complete render once 1 resumed

Normal path: one idempotent submission creates one job, one provider call succeeds, persistence commits the result, and refresh reads the same completed representation.

Failure path: classification detects supported budget evidence, the worker atomically marks blocked_budget, and the UI suppresses retries. If classification is uncertain, the job becomes failed; do not tell users to change billing based on a guess.

Resume uses compare-and-set, an operator audit, and the original idempotency key. Ramp batches gradually.

Delivery checklist

  • Put organization/project selection in server-owned configuration.
  • Persist operation identity and input revision before external work.
  • Classify current structured provider errors with fixture tests.
  • Make budget exhaustion non-retryable at the automated retry layer.
  • Authorize resume and cancellation independently.
  • Test duplicate submit, late response, restored budget, and unknown error.
  • Roll back by disabling new AI admissions while retaining job records.

This cannot predict the threshold call, reset, or availability, and does not replace alerts or financial controls. Pin and test the SDK version.

A separate MonkeyCode trial

A later platform trial could include MonkeyCode, currently described as an open-source AGPL-3.0 AI development platform with an overseas online option, managed server-side cloud development environments, model/task/requirement management, and build/test/preview. It is free to start, but I did not test this contract against it and make no claim about identical errors. Use the official campaign entry only after keeping the provider seam and failure fixtures intact.

Disclosure: This article promotes MonkeyCode using an official campaign link. I’m a MonkeyCode user, not affiliated with the project, and I receive no commission from this link.

AI assistance disclosure: This article was drafted with AI assistance and reviewed against the cited primary sources.

Top comments (0)