The key lives on the server and never reaches the bundle — the request flow, the system prompt constraints, and the two-pass slot alignment.
The key lives on the server and never reaches the bundle. That is the entire reason the server process exists.
AiChat takes a brief. Cmd/Ctrl+Enter submits; a new request aborts any in flight.draftSlides POSTs { brief, structure: { name, shape, slots } } to /api/draft.claude-opus-5 through messages.parse() with a zod output format. The system prompt enforces: finished copy not placeholders, one idea per slide, hook under 90 characters, body under 220, stay in the brief’s voice, add no claims the brief does not contain, no hashtags or emoji.stop_reason === "refusal" is checked before reading and mapped to 422.alignToSlots runs two passes: first match drafts to slots by role, then fill what is left positionally. One pass swallowed the CTA whenever the model answered out of order — the three point slots sharing an id is exactly what breaks a naive match.