API v1 (legacy)

v1 is maintained for existing integrations. New integrations should use v2. See Migrating from v1 to v2.

  • Content type: application/json
  • Authentication: api-key header (Authentication)
  • v1's OpenAPI document (/openapi.json, /docs) requires the API key.

Endpoints

Method Path Model Input Status
POST /invocations Latest model (bc-v1) Image only Supported
POST /invocations/bc-v1 bc-v1 Image only Supported
POST /invocations/bc-legacy Original mood model Image or video Deprecated (details)
GET /ping Health check

POST /invocations and POST /invocations/bc-v1

Both run the bc-v1 model on a single image and behave identically.

Query parameters

Parameter Type Required Description
threshold number No Threshold applied to the model's discomfort ("pain") probability. If omitted, the model default is used (currently 0.3).

Request body

Field Type Required Description
media_type string No Must be "image" (the default). "video" returns HTTP 400; any other value returns HTTP 422.
media_data object Yes Container for the media
media_data.image string Yes in practice Base64-encoded image. Standard base64 only, no data: prefix. If omitted, the request fails with success: false.
media_data.video, media_data.frames_to_analyze, media_data.top_n_predictions No Ignored by these endpoints (used only by the deprecated legacy endpoint)

Example request

printf '{"media_type":"image","media_data":{"image":"%s"}}' "$(base64 < cat.jpg | tr -d '\n')" > v1_body.json

curl -X POST "$BASE_URL/invocations?threshold=0.3" \
  -H "api-key: $API_KEY" \
  -H "Content-Type: application/json" \
  --data @v1_body.json
response = requests.post(
    f"{BASE_URL}/invocations",
    headers={"api-key": API_KEY},
    params={"threshold": 0.3},          # optional
    json={"media_type": "image", "media_data": {"image": image_b64}},
    timeout=60,
)
result = response.json()
if result["success"]:
    print(result["prediction"], result["probability"])
else:
    print("No prediction:", result["message"])

v1 response schema

Field Type Description
mood_class integer 1 = Unhappy (discomfort), 0 = Happy (comfort), -1 = no prediction
prediction string "Unhappy", "Happy", or "Unknown"
probability number Confidence in the predicted class (not the discomfort probability; see below). 0.0 when there's no prediction.
threshold number | null Threshold applied on success. On failures it is usually the threshold query parameter you sent (or null); don't rely on it when success is false.
message string "Success" or a description of why no prediction was made
success boolean true only when a prediction was made
cat_quality number | null Cat detector confidence (0.0–1.0), when available
cat_detected boolean true only when a cat was detected with confidence ≥ 0.69
additional_data object | null Present on success, see below
additional_data.input_dimensions string Input size as "WIDTHxHEIGHT", e.g. "1028x1028"
additional_data.detected_cat_dimensions string Cropped cat size as "WIDTHxHEIGHT"
additional_data.size_unit string Always "pixels"
additional_data.extraction_time number Seconds spent detecting and cropping the cat
additional_data.inference_time number Seconds spent classifying
additional_data.time_unit string Always "seconds"
additional_data.bounding_box_cat array A list with one box { "x1", "y1", "x2", "y2" } (integer pixels, top-left origin)
additional_data.model string "BC-V1"
version string Fixed response-format date, "2023-11-22"
inference_metadata object | null { "version": "0.0.34", "build_number": "34" } on success; null on failures

If a photo contains several cats, v1's bounding_box_cat may not always correspond to the cat that was scored. v2 fixes this.

v1 response examples

Success

{
  "mood_class": 1,
  "prediction": "Unhappy",
  "probability": 0.32395651936531067,
  "threshold": 0.3,
  "message": "Success",
  "success": true,
  "cat_quality": 0.9662923216819763,
  "cat_detected": true,
  "additional_data": {
    "input_dimensions": "1028x1028",
    "detected_cat_dimensions": "949x939",
    "size_unit": "pixels",
    "extraction_time": 0.7679755687713623,
    "inference_time": 0.024747371673583984,
    "time_unit": "seconds",
    "bounding_box_cat": [ { "x1": 56, "y1": 74, "x2": 1005, "y2": 1013 } ],
    "model": "BC-V1"
  },
  "version": "2023-11-22",
  "inference_metadata": { "version": "0.0.34", "build_number": "34" }
}

Cat found but image quality too low (detector confidence between 0.5 and 0.69)

{
  "mood_class": -1,
  "prediction": "Unknown",
  "probability": 0.0,
  "threshold": null,
  "message": "Cat image quality is too low",
  "success": false,
  "cat_quality": 0.6602507829666138,
  "cat_detected": false,
  "additional_data": null,
  "version": "2023-11-22",
  "inference_metadata": null
}

No cat detected

{
  "mood_class": -1,
  "prediction": "Unknown",
  "probability": 0.0,
  "threshold": null,
  "message": "No cats detected",
  "success": false,
  "cat_quality": null,
  "cat_detected": false,
  "additional_data": null,
  "version": "2023-11-22",
  "inference_metadata": null
}

Understanding probability and threshold in v1

The model produces a discomfort ("pain") probability p between 0 and 1:

  • mood_class = 1 ("Unhappy") when p > threshold, otherwise mood_class = 0 ("Happy").
  • probability is the confidence in the returned class:
    • when mood_class = 1: probability = p
    • when mood_class = 0: probability = 1 − p

So a result of Happy with probability: 0.9 means p = 0.1. To recover the discomfort probability from a v1 response:

p = result["probability"] if result["mood_class"] == 1 else 1 - result["probability"]

Don't compare probability directly with threshold. v2's prediction.score is p itself.

v1 errors

Most problems with v1 requests return HTTP 200 with success: false, mood_class: -1, and a message. Always check success.

Situation HTTP Response
No cat found 200 success: false, message: "No cats detected"
Cat found with low confidence 200 success: false, message: "Cat image quality is too low", cat_quality set
Image isn't a decodable image, or media_data.image missing 200 success: false, message: "Error loading image from base64 string"
Invalid base64 or other processing failure 200 success: false, message starting with "Error: "
media_type: "video" on /invocations 400 {"detail": "The active model (bc-v1) only supports 'image' media type. For video analysis, use /invocations/bc-legacy."}
media_type: "video" on /invocations/bc-v1 400 {"detail": "BC-V1 endpoint only supports 'image' media type. For video analysis, use /invocations/bc-legacy."}
Missing or invalid API key 401 {"detail": "Invalid or missing API key"}
Body missing media_data, media_type not "image"/"video", wrong types, or malformed JSON 422 Framework validation format: {"detail": [{"type": ..., "loc": [...], "msg": ..., ...}]}. May echo the submitted input.
Unexpected server error 500 Plain-text Internal Server Error
Platform overload, deploys, or timeouts 429 / 502 / 503 / 504 May not be JSON

The retry guidance in Errors & retries applies to v1 as well.

Deprecated: POST /invocations/bc-legacy

Deprecated. Uses the original mood model and is not recommended for new or existing workloads. Pending: publish a removal date.

  • Accepts media_type: "image" or "video".
  • Image: same request as POST /invocations (no threshold parameter). The response has the same shape, with threshold always null, additional_data.model set to "BC-Legacy", and inference_metadata set to null.
  • Video: send media_data.video (base64-encoded MP4) with optional frames_to_analyze (default 20) and top_n_predictions (default 5). The response has the same top-level fields except threshold. additional_data contains frames_extracted, top_n_predictions, input_dimensions, detected_cat_dimensions, total_extraction_time, total_prediction_time, bounding_box_cat, size_unit, and time_unit.
  • Neither v1 bc-v1 nor v2 supports video. For video, extract frames and send them as images to v2.

GET /ping

Health check. Requires the API key.

curl -H "api-key: $API_KEY" "$BASE_URL/ping"
{ "status": "healthy", "service": "sylvester-ml" }