Integration guides
PHP
A moderation helper for PHP 8.1 or later with cURL, and no dependency.
This guide needs PHP 8.1 or later and the cURL extension, which ships with most PHP installations.
Set the key
Put the key in the environment of your server, never in the code. For PHP-FPM, set it in the pool's env[OPENMOD_API_KEY] or in your framework's secret store, and read it with getenv().
The helper
moderation.php
<?php
declare(strict_types=1);
const OPENMOD_BASE = 'https://api.openshield.dev';
final class ModerationError extends RuntimeException
{
public function __construct(public readonly int $status, public readonly string $type, string $message, public readonly ?int $retryAfter = null)
{
parent::__construct($message, $status);
}
}
function openmod_send(string $path, string $body, array $headers = []): array
{
$key = getenv('OPENMOD_API_KEY');
if ($key === false || $key === '') {
throw new RuntimeException('OPENMOD_API_KEY is not set');
}
$curl = curl_init(OPENMOD_BASE . $path);
curl_setopt_array($curl, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $body,
CURLOPT_HTTPHEADER => array_merge(['Authorization: Bearer ' . $key], $headers),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HEADER => false,
CURLOPT_TIMEOUT => 10,
]);
$raw = curl_exec($curl);
$status = (int) curl_getinfo($curl, CURLINFO_RESPONSE_CODE);
if ($raw === false) {
throw new RuntimeException('openshield could not be reached: ' . curl_error($curl));
}
$answer = json_decode($raw, true, 512, JSON_THROW_ON_ERROR);
if ($status !== 200) {
$error = $answer['error'] ?? [];
throw new ModerationError($status, $error['type'] ?? 'unknown', $error['message'] ?? 'The check failed');
}
return [
'action' => $answer['summary']['action'],
'reasons' => array_merge($answer['summary']['reject_reason'], $answer['summary']['review_reason']),
'request_id' => $answer['request_id'],
];
}
function openmod_check_text(string $text, ?string $ref = null): array
{
return openmod_send('/v1/text', json_encode(['text' => $text, 'ref' => $ref], JSON_THROW_ON_ERROR), ['Content-Type: application/json']);
}
function openmod_check_image(string $bytes, ?string $ref = null): array
{
return openmod_send('/v1/image' . ($ref === null ? '' : '?ref=' . rawurlencode($ref)), $bytes);
}
Use it, and fail closed
<?php
require 'moderation.php';
function moderate_comment(string $text, string $commentId): string
{
try {
$decision = openmod_check_text($text, $commentId);
} catch (ModerationError $error) {
// The content cannot be accepted as it is: refuse it.
return in_array($error->status, [400, 413, 415], true) ? 'refuse' : 'hold';
} catch (Throwable) {
// A timeout or a network error: keep it hidden until someone looks at it.
return 'hold';
}
return match ($decision['action']) {
'accept' => 'publish',
'review' => 'hold',
default => 'refuse',
};
}
For rejected content, show your user a message chosen from the id of the first reason, in your own words and your own languages. Best practices says more.