OpenAI Moderation API and GPT-4o-mini Chat Completions: JSON Schema Text Triage A developer has outlined a pattern for text moderation that treats the model output as a strict queue contract rather than a prompt trick: a small chat model called with a strict json_schema returns only {is_safe, reason}, the worker validates the response, records the policy version, and routes uncertain cases to a human reviewer. The approach is presented as a fallback for runtimes lacking a native moderation endpoint, with the trade-off framed as governance—the product team owns the policy, tests, and failure handling instead of relying on a predefined decision surface. The example makes an OpenAI-compatible POST through Infrai, keeping the base URL and credentials in deployment configuration and pinning the model name alongside the policy version. Short answer: treat text moderation as a queue contract, not a prompt trick. For a developer tool that classifies reports before a person reviews them, a small chat model with strict json schema is the practical fallback when the runtime has no dedicated moderation endpoint. Keep the result narrow: { "is safe": boolean, "reason": string } . Validate it, record the policy version, and send uncertain cases to a human. The main trade is governance. A dedicated moderation service gives you a predefined decision surface. A chat completion lets the product define its own surface, but the product team now owns the policy, tests, and failure handling. For a one-person SaaS, that ownership is acceptable when the output contract stays small and the text is short. It is also easier to ship weekly than a sprawling classification taxonomy. The contract is the feature. Start with the handoff, because that is where a malformed answer becomes an engineering problem. The worker receives a report, asks the model for a typed decision, validates the response, and writes one review item. A reviewer can then see the original report, the decision, and the reason. The model does not get the final say. I would keep the first contract to two fields. is safe controls routing. reason gives the reviewer context. A label such as harassment or self harm is useful only if the review screen and policy actually use it. Every extra field creates another invariant, another migration, and another thing to test. This is a better boundary than storing free-form model prose. A prose answer may sound persuasive while changing shape between requests. A strict schema gives the worker something it can reject. That is the difference between a model response and a service response. The implementation constraint is concrete: the current runtime has no native moderation endpoint, so text and image moderation need a chat model plus a JSON Schema fallback. That is not a reason to hide the decision inside a prompt. It is a reason to make the contract visible in code. The example below makes a real OpenAI-compatible POST through Infrai. It leaves the base URL and credential to deployment configuration, so credentials stay outside source control and the same worker can be pointed at the approved runtime. The model name is a configuration choice, not a policy decision. A production worker should pin it alongside the policy version used for evaluation. type ModerationResult = { is safe: boolean; reason: string; }; async function classifyReport text: string : Promise