Genace

AI generation API server-side keys: put the spending boundary on your backend

A shared generation key gives your backend access to an account’s generation operations. If a browser receives that key, the browser can make requests outside your product’s interface. Keep the credential in the server environment and expose application operations that check your own user, allowance, and job ownership rules before calling Genace.

1. Define the trust boundary

The upstream API expects an Authorization header beginning with Bearer . Its authentication helper validates the key and identifies the Genace user and key record. It does not know which customer is signed into your own SaaS application. When several local users share one server key, you must keep that distinction in your application database.

Use a server-only helper like this to construct outbound headers. Keep it in a module that your framework does not bundle into client code.

export function generationHeaders() {
  const key = process.env.GENACE_API_KEY?.trim();
  if (!key) throw new Error('Generation credentials are not configured');
  if (key.startsWith('Bearer ')) {
    throw new Error('Store the raw key without a Bearer prefix');
  }
  return {
    Authorization: `Bearer ${key}`,
    'Content-Type': 'application/json',
  };
}

export function publicJobView(job: {
  id: string;
  state: string;
  outputs?: unknown[] | null;
  error?: { code?: string } | null;
}) {
  return {
    id: job.id,
    state: job.state,
    outputs: job.state === 'succeeded' ? job.outputs ?? [] : null,
    errorCode: job.error?.code ?? null,
  };
}

The public projection is an example application decision. You may need additional safe fields, but the browser never needs the Authorization header. Keep authentication configuration separate from job result serialization.

2. Authorize before creating remote work

In your submission handler, require the local session, check the user’s allowance, validate the supported request fields, and then submit once. Save a row containing your local user ID, the remote job_id, model choice, and accepted credit reservation before returning the result.

Protect this handler against your application’s relevant request forgery and abuse risks using the framework’s existing controls. A hidden button or frontend quota display cannot enforce the spending boundary because callers can send HTTP requests directly.

3. Check ownership again during observation

A status handler should load your local mapping and confirm the current user owns that operation. Only then call GET /api/v1/jobs/{id} with the server key. Genace’s own job route checks the remote account owner, but that check cannot separate users who share your backend account.

Do not treat a random-looking job ID as authorization. Also avoid making your status route a general proxy that accepts arbitrary paths or URLs. Construct the fixed upstream origin and a validated job path in server code.

4. Handle logs and rotation deliberately

Log operation IDs, status codes, and safe error codes. Do not log all request headers, process environment values, or complete credential objects. An API error may contain an operationally useful message, but review what your application returns to end users before forwarding arbitrary exception text.

If a key is exposed, revoke or replace it through your account’s supported key management flow and update every runtime that uses it. A deployment environment change may require a restart or redeployment. Check for old workers that still hold the previous process environment.

Common mistakes and verification

Avoid client-visible environment prefixes, serialized server props containing credentials, and environment files copied into public directories. Do not confuse your application login session with the separate bearer key used for upstream requests.

Verify built browser assets contain no real key, an unauthenticated local request never reaches Genace, and user A cannot query user B’s mapped job. Verify logs redact headers and failed job responses expose a suitable state without assuming outputs exist.

Follow the Next.js route example, the Express endpoint, and environment variable handling for concrete implementations of this boundary.

Continue with code and integration

Where these facts come from

  • Editorial keyword hypothesis; search volume not measured
  • codebase: src/ai/api/auth.ts
  • codebase: src/app/api/v1/jobs/[id]/route.ts
  • codebase: src/ai/api/wrap.ts