Skip to main content

Authentication issues

401 UNAUTHORIZED

Cause: The API key is missing, malformed, or has been revoked. Fix:
  • Confirm the Authorization header uses the exact format: Bearer YOUR_API_KEY
  • Check that the key has not been revoked in the dashboard
  • Ensure the key belongs to an active account
Moderation submissions are multipart/form-data, not JSON. Send each field with -F. A JSON body returns 400 INVALID_REQUEST because the required field is never parsed.

403 ACCOUNT_TERMINATED

Cause: Your account has been terminated, typically after a manual review of policy violations. See account termination. Fix: Contact support at support@omnifence.ai. This state cannot be resolved through the API.

Submission failures

400 INVALID_REQUEST: missing fields

Cause: The required field for the endpoint was missing. Fix: Include the field. For /moderate/image, pass a publicly reachable image.

Using the wrong endpoint for your media type

Cause: Each modality has its own endpoint and its own required field, listed in the table above. Submitting an image to the text endpoint (or vice versa) returns 400 INVALID_REQUEST. Fix: Use the endpoint that matches your media type. /moderate/text takes a text field and screens it against your configured moderation policies. See text moderation.

400 INVALID_REQUEST: “image file upload is currently disabled”

Cause: The /moderate/image endpoint does not accept a direct file upload. It accepts only the image URL field. Fix: Host the image at a publicly reachable URL and pass it as the image field.
When direct file upload is available, the image file is limited to 10 MB. A larger file returns 400 INVALID_REQUEST.

400 INVALID_REQUEST: “video file upload is currently disabled”

Cause: The /moderate/video endpoint does not accept a direct file upload. It accepts only the video URL field. Fix: Host the video at a publicly reachable URL and pass it as the video field.

Video job fails: video too large or unreachable

Cause: The video field points to a file larger than 100 MB, or the API could not download it. The submission still returns 202, but the job later ends with status: "failed". Fix: Host a video under 100 MB at a publicly reachable HTTP or HTTPS URL. The API rejects a URL that resolves to a private or internal address, or that uses a non-HTTP scheme, at submission with 400 INVALID_REQUEST.

Image job fails: image unreachable

Cause: The API could not fetch the image URL. The submission still returns 202, but the job later ends with status: "failed". Fix: Host the image at a publicly reachable HTTP or HTTPS URL. The API rejects a URL that resolves to a private or internal address, or that uses a non-HTTP scheme, at submission with 400 INVALID_REQUEST.

Webhook issues

Webhook is not receiving events

Possible causes and fixes:
  1. No webhook registered: Call POST /api/v1/webhook/register with your URL, or include webhook_url in the moderation request.
  2. Webhook URL is HTTP: The API requires HTTPS. Use an HTTPS endpoint.
  3. Your server timed out: Webhook delivery has a 10-second timeout. Confirm your server responds within that window. See webhook reliability.
  4. Server returned non-2xx: The delivery is retried for 12 attempts over about five hours, and is then abandoned. Check your server logs, and read the result from GET /api/v1/job/{id} for anything that was already abandoned. See webhook reliability.
  5. URL rejected outright: A URL that is malformed, not HTTPS, or resolves to a private or internal address is dropped without any retry. Re-register a valid HTTPS URL.

Webhook fired but the payload looks wrong

Fix: Verify you are reading the correct fields. The webhook payload format depends on the moderation type. See webhooks for the full schema.

Signature verification fails

Every callback carries webhook-id, webhook-timestamp and webhook-signature headers. If your verifier rejects them, work through these in order:
  1. You verified a re-serialised body: The signature covers the exact bytes we sent. Parsing the JSON and re-serialising it reorders keys and changes whitespace, so the check can never pass. Pass the raw body — express.raw({ type: 'application/json' }) in Node, request.data in Flask, not request.get_json().
  2. Wrong secret: The secret is per account and starts with whsec_. Paste the whole value, including that prefix, into your Standard Webhooks library. Reveal the current one under Account → Webhooks.
  3. You rotated and have not redeployed: During the grace window webhook-signature carries two space-delimited v1,<base64> values and a library accepts whichever matches. Split that header on spaces — the comma belongs inside each value and is not a separator.
  4. Your server clock has drifted: Libraries reject a webhook-timestamp more than five minutes from their own clock. We sign each attempt as we send it, so a retry is always fresh; a failure across the board points at your clock, not at the retry. Run NTP.
  5. 403 FORBIDDEN when reading the secret: The webhook-secret endpoints need the webhook:manage scope, which is off by default on API keys. Read the secret from the dashboard instead.
See verifying a webhook for the signed string and a worked example.

Job issues

Job stays queued for a long time

Cause: A moderation job reports queued for its whole run and then flips straight to completed or failed. It never reports processing or percentage progress, so a long queued period is normal work, not a stall. Times vary: text and image moderation typically complete in seconds; long videos and audio clips take longer, because transcription time scales with clip length. Fix: Register a webhook instead of polling tightly. If a job has been queued for more than 10 minutes, resubmit and contact support if the issue persists.

404 JOB_NOT_FOUND

Cause: Either the job ID is incorrect, or the job belongs to a different client account. Jobs are scoped per account. Fix: Confirm the job_id matches one returned by your account’s submission.

is_prohibited is null

Cause: The job has not yet completed (status is queued or processing), or it failed (status is failed). Fix: Wait for status: "completed" before reading is_prohibited. For failed jobs, resubmit the request.

Rate limiting

429 RATE_LIMITED

Cause: You have exceeded your account’s request rate. See rate limiting. Fix: Reduce request frequency or implement exponential backoff. See error recovery for a retry example.

Still stuck?

Contact support at support@omnifence.ai with your job_id and the API response body.