AI services
Integrate PPE detection
Send a photo of a scene and get back who is wearing what protective equipment, with a bounding box for every person and every item found.
Choose an endpoint
All three routes accept the same request and return the same response shape. Only which equipment types count toward compliance changes.
| Endpoint | Checks for |
|---|---|
POST /v1/ai/ppe-detection/detect | Face and head coverings together. A person is compliant only when they wear both. |
POST /v1/ai/ppe-detection/detect-mask | Face coverings only. |
POST /v1/ai/ppe-detection/detect-helmet | Head coverings only, such as helmets and hard hats. |
Request
Send multipart/form-data with one image:
| Field | Meaning |
|---|---|
image | image is required. JPEG or PNG, up to 5 MB. Up to 15 people are scored. |
Examples
This example calls the general endpoint; the other two are identical requests against a different URL.
curl -X POST https://core.roammoon.com/v1/ai/ppe-detection/detect \
-H "X-API-Key: rm_live_YOUR_KEY" \
-F "image=@site-entrance.jpg"Response
Returns one entry per person found, each with its own bounding box and the equipment detected on it:
{
"requiredEquipmentTypes": ["FACE_COVER", "HEAD_COVER"],
"persons": [
{
"id": 0,
"confidence": 99.8,
"boundingBox": { "left": 0.12, "top": 0.05, "width": 0.31, "height": 0.62 },
"compliant": true,
"bodyParts": [
{
"name": "FACE",
"confidence": 99.9,
"equipment": [
{
"type": "FACE_COVER",
"confidence": 98.5,
"boundingBox": { "left": 0.18, "top": 0.09, "width": 0.14, "height": 0.11 },
"covers": true,
"coversConfidence": 99.1
}
]
}
]
}
]
}persons is an empty array. That is still a successful call and is still charged, because the API did its job and there was simply nobody to report on.
{
"requiredEquipmentTypes": ["FACE_COVER"],
"persons": []
}Fields that matter
| Field | Meaning |
|---|---|
persons | One entry per person Rekognition found in the image, in no particular order. |
persons[].compliant | true when this person wears every equipment type the endpoint checked for, false when at least one is missing, null when it could not be determined, most often because an item's own detection confidence fell under the platform's configured minimum, even though the item still appears in bodyParts. |
persons[].bodyParts | Face and head, shown only when Rekognition actually located that body part. |
bodyParts[].equipment | The protective items detected on that body part. |
equipment[].covers | Whether the item actually covers the body part it was found near. A mask pulled down around the chin is still detected, but covers is false. |
boundingBox | A fraction of the image's own width and height, 0–1, measured from the top-left corner. Draw it yourself on top of the image you already have, since this API never burns boxes into the image. |
Billing
10 credits are charged only when a detection succeeds and a result is returned, on any of the three endpoints. Every failure is refunded, whether the image was rejected or the service itself could not complete the request.
The charge policy is charge-on-success: credit is reserved when the request is accepted and only committed once processing succeeds. A failure refunds the reservation.
Getting good results
- Use the whole scene, not a per-person crop, since bounding boxes are relative to the image you send, so cropping tighter just shrinks what the model has to work with.
- Distant or heavily occluded people are the most common cause of a missing or indeterminate compliant value. Get the camera closer or increase resolution before assuming the equipment isn't there.
- This is equipment detection, not a safety certification. Treat it as a first pass that flags what to check, and keep a human in the loop before it drives an access or disciplinary decision.
Errors
Every error uses the same shape. Branch on code, not on message, because messages can change.
{
"error": {
"code": "INVALID_PPE_DETECTION_REQUEST",
"message": "The \"image\" field must be an image file."
}
}| HTTP | Code | What to do |
|---|---|---|
| 401 | API_KEY_REQUIRED | The X-API-Key header is missing. |
| 401 | API_KEY_INVALID | The key is wrong, disabled or expired. Check it or create a new one. |
| 403 | API_KEY_FORBIDDEN | The key is not permitted to use this service. |
| 400 | INVALID_PPE_DETECTION_REQUEST | The "image" field is missing, empty, or Rekognition could not process it. No credit is charged. |
| 402 | CREDIT_LIMIT_EXCEEDED | The period's credits are exhausted. Upgrade or wait for the next period. |
| 413 | IMAGE_TOO_LARGE | The image exceeds 5 MB. Compress or resize it. |
| 415 | UNSUPPORTED_IMAGE_TYPE | Only JPEG and PNG are supported. |
| 429 | RATE_LIMIT_EXCEEDED | Per-minute request limit exceeded. Slow down. |
| 429 | PPE_DETECTION_SERVICE_BUSY | The detection service is saturated. Retry after a few seconds. |
| 502 | PPE_DETECTION_UPSTREAM_ERROR | The PPE detection model could not complete the request. Retryable. |
| 503 | PPE_DETECTION_SERVICE_MISCONFIGURED | The service is not configured correctly. This is an operator problem, not something a retry fixes. |
Retrying safely
429 and 502 are retryable, so use exponential backoff. Other errors return the same result on retry. Only a successful detection is charged, so retrying after a failure costs nothing extra. The exception is a network timeout: if the first attempt actually succeeded, the retry is charged as a second call.