OpenAI 86 's Usage Policies (effective 29 October 2025) forbid, among other things, harassment, sexual content involving minors and using someone's likeness, "including their photorealistic image or voice, without their consent in ways that could confuse authenticity." Every prompt and image is filtered; moderation: 'low' only loosens the standard filter. Screen your users' prompts and uploads first with the free omni-moderation-latest model:
import fs from 'node:fs';
import OpenAI from 'openai';
const png = fs.readFileSync('nestor-kitchen.png').toString('base64');
const { results: [result] } = await new OpenAI().moderations.create({
model: 'omni-moderation-latest', // free; reads text and images
input: [{ type: 'text', text: 'Nestor the bluebird reads a cookbook in a kitchen' },
{ type: 'image_url', image_url: { url: `data:image/png;base64,${png}` } }],
});
const top = Object.entries(result.category_scores).sort((a, b) => b[1] - a[1]).slice(0, 3);
console.log('flagged:', result.flagged, '| top scores:',
top.map(([name, score]) => `${name} ${score.toExponential(1)}`).join(', '));Output
flagged: false | top scores: violence 7.1e-6, self-harm 1.5e-6, violence/graphic 1.4e-6
<!doctype html>
<style>
body { margin: 0; padding: 8px; background: #fafaf7; font: 11px system-ui, sans-serif; color: #263238; }
svg { width: 100%; max-width: 600px; display: block; }
#verdict { font-size: 13px; font-weight: bold; }
</style>
<script src="https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js"></script>
<p>Input: <select id="input">
<option value="0">text + image: "Nestor the bluebird reads a cookbook in a kitchen"</option>
<option value="1">text: "a thriller cover: a knife on a kitchen table"</option>
<option value="2">text: "a realistic photo of a named celebrity endorsing BookNest"</option>
</select></p>
<p id="verdict"></p>
<svg viewBox="0 0 600 220" font-size="11"></svg>
<p>Illustrative scores, not a real API response. Screen users' prompts and uploads with the free moderation model
before spending on generation; likeness without consent is forbidden by the usage policies even when no score is high.</p>
<script>
const categories = ['harassment', 'hate', 'self-harm', 'sexual', 'violence', 'violence/graphic', 'illicit'];
const samples = [
[2e-6, 1e-6, 3e-7, 4e-6, 8e-6, 2e-6, 5e-6],
[3e-5, 1e-5, 2e-4, 1e-5, 0.21, 0.04, 0.003],
[0.002, 1e-5, 1e-6, 3e-4, 1e-5, 1e-6, 0.012],
];
const svg = d3.select('svg');
const x = d3.scaleLog([1e-7, 1], [120, 580]), y = d3.scaleBand(categories, [10, 190]).padding(0.25);
svg.append('g').attr('transform', 'translate(0,190)').call(d3.axisBottom(x).ticks(7, '.0e'));
svg.selectAll('text.cat').data(categories).join('text').attr('class', 'cat').attr('x', 114).attr('text-anchor', 'end')
.attr('y', c => y(c) + y.bandwidth() / 2).attr('dy', '0.35em').text(c => c);
const threshold = 0.5;
svg.append('line').attr('x1', x(threshold)).attr('x2', x(threshold)).attr('y1', 5).attr('y2', 190).attr('stroke', '#b5452f').attr('stroke-dasharray', '4 3');
svg.append('text').attr('x', x(threshold) - 4).attr('y', 12).attr('text-anchor', 'end').attr('fill', '#b5452f').text('flag threshold (illustrative)');
function draw(i) {
const scores = samples[i];
svg.selectAll('rect.s').data(scores).join('rect').attr('class', 's').attr('x', x(1e-7)).attr('y', (d, k) => y(categories[k]))
.attr('height', y.bandwidth()).attr('fill', d => d > threshold ? '#b5452f' : d > 0.01 ? '#e09a10' : '#3f7d3a')
.transition().duration(500).attr('width', d => x(d) - x(1e-7));
const top = d3.greatest(scores.map((s, k) => [categories[k], s]), d => d[1]);
const flagged = scores.some(s => s > threshold);
d3.select('#verdict').style('color', flagged ? '#b5452f' : i === 2 ? '#e09a10' : '#3f7d3a')
.text(`flagged: ${flagged} | top: ${top[0]} ${top[1].toExponential(1)}` + (i === 2 ? ' | but refuse it: a real person\'s likeness' : ''));
}
d3.select('#input').on('change', e => draw(+e.target.value));
draw(0);
</script>