Best practices
Call the API from your servers only
A key is a secret. Call openshield from your backend, and give your front end the decision, not the key. The API answers a request that carries a browser Origin header with 403 forbidden, so a call from a browser fails anyway, and a key sent to a browser is exposed.
Fail closed
A check can fail: a timeout, a network error, a 500 or a 503. Decide what happens to the content when it does, and make the safe choice the default: hold it until a person has looked at it, or until a retry has succeeded. Publishing content that was never checked is the case a moderation service exists to prevent.
- Set a timeout on the call, 10 seconds is a good start.
- Retry
429and503after theRetry-Afterheader, at most twice. - Do not retry
400,413and415: the content cannot be accepted as it is. Refuse it.
Act on the three outcomes
accept: publish.review: hide the content from other users until a person approves it. If your product has no moderation queue, add the smallest one that fits: a flag on the row and a screen to list the flagged ones.reject: refuse the content and tell the user why.
Choose your messages by category id
Each reason has an id, such as nudity or pii_email. It is stable. Map it to messages you write, in your user's language:
const messages: Record<string, string> = {
nudity: "This image can't be used here.",
pii_email: "Please don't share email addresses in comments.",
}
const message = messages[decision.rejectReasons[0]?.id ?? ""] ?? "This content can't be published."
The English text and message of a reason are fallbacks. Do not match on them: they can be reworded.
Send what you need to send
- Downscale large photos to about 1,280 pixels on the longer side: the upload is smaller and faster. Keep the full size of screenshots, where small text or a QR code matters.
- Send each image once. Cache the decision with your own record of the content.
- Check what other people see. Posts, comments, messages, profile pictures, display names, bios, listings and reviews: anything a user can publish.
Keep a trail
- Send a
ref, your own opaque id for the content, so you can find its decision in the journal. Do not put personal data in it. - Log the
request_idof every answer next to your own record.
Keys and workflows
- Give each service its own key, with the narrowest scope and workflows it needs.
- Rotate keys with a grace delay, so a deploy never has a gap.
- Use one workflow per product surface when the rules differ: stricter for a children's section, more lenient for a private chat.
Test without the network
Stub the HTTP call in your tests, and assert what your code does for accept, review, reject and each kind of error. Do not call the real API from a test suite: it counts against your quota.