---
title: "Responses and errors"
description: "Every field of an answer, the error envelope with its types, and the headers on every response."
canonical_url: "https://openshield.dev/docs/responses-and-errors"
---
# Responses and errors

> Every field of an answer, the error envelope with its types, and the headers on every response.

## A successful answer

A check answers `200` with this JSON. Every plan gets every field.

<table>
<thead>
  <tr>
    <th>
      Field
    </th>
    
    <th>
      Type
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        status
      </code>
    </td>
    
    <td>
      string
    </td>
    
    <td>
      Always <code>
        success
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        workflow
      </code>
    </td>
    
    <td>
      string
    </td>
    
    <td>
      The workflow the content was checked against.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        workflow_version
      </code>
    </td>
    
    <td>
      integer
    </td>
    
    <td>
      The version of the workflow that decided.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        summary.action
      </code>
    </td>
    
    <td>
      string
    </td>
    
    <td>
      <code>
        reject
      </code>
      
       when a reject threshold is reached, else <code>
        review
      </code>
      
       when a review threshold is, else <code>
        accept
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        summary.reject_prob
      </code>
    </td>
    
    <td>
      number
    </td>
    
    <td>
      The highest score among the rejected categories, 0 when none is.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        summary.reject_reason
      </code>
    </td>
    
    <td>
      array
    </td>
    
    <td>
      Why the content is rejected, highest score first.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        summary.review_reason
      </code>
    </td>
    
    <td>
      array
    </td>
    
    <td>
      Why the content goes to review, highest score first.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        scores
      </code>
    </td>
    
    <td>
      object
    </td>
    
    <td>
      The best score per category the workflow checks, from 0 to 1.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        unchecked
      </code>
    </td>
    
    <td>
      array
    </td>
    
    <td>
      The categories of the workflow this server could not check. Absent when every one was checked.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        time_ms
      </code>
    </td>
    
    <td>
      integer
    </td>
    
    <td>
      The time spent checking, in milliseconds.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        request_id
      </code>
    </td>
    
    <td>
      string
    </td>
    
    <td>
      The id of this request, also in <code>
        X-Request-Id
      </code>
      
       and in the <a href="https://openshield.dev/docs/journal-and-ref">
        journal
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        ref
      </code>
    </td>
    
    <td>
      string or null
    </td>
    
    <td>
      Your own id for the content, as you sent it.
    </td>
  </tr>
</tbody>
</table>

A reason in `reject_reason` or `review_reason` has:

<table>
<thead>
  <tr>
    <th>
      Field
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        id
      </code>
    </td>
    
    <td>
      The category id, such as <code>
        nudity
      </code>
      
       or <code>
        pii_iban
      </code>
      
      . It is stable: map it to your own messages.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        text
      </code>
    </td>
    
    <td>
      A short English title.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        message
      </code>
    </td>
    
    <td>
      A full English sentence that can be shown to a user.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        score
      </code>
    </td>
    
    <td>
      The score the category got.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        threshold
      </code>
    </td>
    
    <td>
      The threshold it reached.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        source
      </code>
    </td>
    
    <td>
      The tool that produced the score.
    </td>
  </tr>
</tbody>
</table>

<tip>

Match on `id`, never on `text` or `message`. They are English defaults and may be reworded.

</tip>

## Errors

An error answers with the HTTP status and this envelope:

```json
{
  "status": "failure",
  "error": {
    "type": "unsupported_media",
    "code": 415,
    "message": "The file is not a JPEG, PNG, WebP or GIF image."
  }
}
```

Switch on `error.type`: it is stable. `error.code` repeats the HTTP status, and `error.message` is English text for you, not for your users.

<table>
<thead>
  <tr>
    <th>
      <code>
        error.type
      </code>
    </th>
    
    <th>
      Status
    </th>
    
    <th>
      When
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        unauthorized
      </code>
    </td>
    
    <td>
      401
    </td>
    
    <td>
      The key is missing, malformed, invalid, revoked or expired.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        forbidden
      </code>
    </td>
    
    <td>
      403
    </td>
    
    <td>
      The key may not check this kind of content or use this workflow, or the request came from a browser.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        organization_suspended
      </code>
    </td>
    
    <td>
      403
    </td>
    
    <td>
      An operator suspended the organization. Write to <a href="mailto:contact@sway.events">
        contact@sway.events
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        bad_request
      </code>
    </td>
    
    <td>
      400
    </td>
    
    <td>
      An empty body, a text that is not JSON or has no <code>
        text
      </code>
      
      , a refused <code>
        ref
      </code>
      
      , or a form that lacks a field.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        unknown_workflow
      </code>
    </td>
    
    <td>
      400
    </td>
    
    <td>
      There is no workflow of that name.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        corrupt_media
      </code>
    </td>
    
    <td>
      400
    </td>
    
    <td>
      The file does not decode.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        url_refused
      </code>
    </td>
    
    <td>
      400
    </td>
    
    <td>
      The <code>
        url
      </code>
      
       of an image is not public, too large, too slow or not an image.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        unsupported_media
      </code>
    </td>
    
    <td>
      415
    </td>
    
    <td>
      Not a JPEG, PNG, WebP or GIF image, or a text sent without <code>
        Content-Type: application/json
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        body_too_large
      </code>
    </td>
    
    <td>
      413
    </td>
    
    <td>
      A request body over 15 MB.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        image_too_large
      </code>
    </td>
    
    <td>
      413
    </td>
    
    <td>
      More than 50 megapixels (12 on the free plan), or a side over 16,384 pixels.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        text_too_long
      </code>
    </td>
    
    <td>
      413
    </td>
    
    <td>
      More than 20,000 characters (5,000 on the free plan).
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        rate_limited
      </code>
    </td>
    
    <td>
      429
    </td>
    
    <td>
      Too many requests: wait <code>
        Retry-After
      </code>
      
       seconds.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        too_many_concurrent
      </code>
    </td>
    
    <td>
      429
    </td>
    
    <td>
      Too many checks at once: wait <code>
        Retry-After
      </code>
      
       seconds.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        quota_exceeded
      </code>
    </td>
    
    <td>
      429
    </td>
    
    <td>
      The free plan's included checks are used, or the spending cap is reached. See <a href="https://openshield.dev/docs/limits-and-quotas">
        limits and quotas
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        request_timeout
      </code>
    </td>
    
    <td>
      408
    </td>
    
    <td>
      A SightEngine-compatible form took more than 10 seconds to arrive.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        workflow_unavailable
      </code>
    </td>
    
    <td>
      503
    </td>
    
    <td>
      The workflow has no rule for this kind of content, or none this server can check.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        busy
      </code>
    </td>
    
    <td>
      503
    </td>
    
    <td>
      The server is busy, or the share of it the free plans use is: retry after <code>
        Retry-After
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        internal
      </code>
    </td>
    
    <td>
      500
    </td>
    
    <td>
      A bug on our side. Quote the <code>
        request_id
      </code>
      
       when you write to us.
    </td>
  </tr>
</tbody>
</table>

## What to do with an error

- **400, 413, 415**: the request is wrong or the content cannot be accepted. Do not retry; refuse the content or fix the call.
- **401, 403**: a configuration problem. Log it, without the key, and alert your team.
- **408**: send the form again once, from a faster connection if you can.
- **429, 503 with Retry-After**: wait that many seconds, and retry at most twice.
- **500, a timeout or a network error**: fail closed, as [best practices](https://openshield.dev/docs/best-practices) explains.

## Headers on every response

<table>
<thead>
  <tr>
    <th>
      Header
    </th>
    
    <th>
      Description
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        X-Request-Id
      </code>
    </td>
    
    <td>
      The id of the request. Quote it in a support message.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        RateLimit-Limit
      </code>
    </td>
    
    <td>
      The burst size of your plan: how many requests the bucket holds.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        RateLimit-Remaining
      </code>
    </td>
    
    <td>
      How many requests are left in the bucket.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        RateLimit-Reset
      </code>
    </td>
    
    <td>
      Seconds until the bucket is full again.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        X-Quota-Used
      </code>
    </td>
    
    <td>
      Checks counted in the current period.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        X-Quota-Limit
      </code>
    </td>
    
    <td>
      Checks the period allows before <code>
        quota_exceeded
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        Retry-After
      </code>
    </td>
    
    <td>
      Seconds to wait, on <code>
        429
      </code>
      
       and <code>
        503
      </code>
      
      .
    </td>
  </tr>
</tbody>
</table>


## Sitemap

See the full [sitemap](https://openshield.dev/sitemap.md) for all pages.
