{"openapi":"3.1.0","info":{"title":"Muzzle Biometric Verification API","description":"Cattle muzzle biometric verification API. Send 3 reference photos of a known animal and 1 probe photo to verify identity. Uses a triplet loss siamese ensemble of 3 models for robust verification with out-of-domain detection.\n\n## Authentication\nAll `/predict` and `/domain-check` requests require an `X-API-Key` header. Use the Authorize button below to set your API key.\n\n## Rate Limiting\nRequests to `/predict` and `/domain-check` are limited to 30 per 60s per IP. Exceeded requests return HTTP 429 with a `Retry-After` header.","contact":{"name":"Muzzle Biometric Support","email":""},"license":{"name":"Proprietary"},"version":"2.1.0"},"paths":{"/health":{"get":{"tags":["Health"],"summary":"Health","description":"Check API health and model loading status.\n\nReturns whether the service is operational and which ML models are loaded.\nUse this endpoint to verify the API is ready before sending verification requests.","operationId":"health_health_get","responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/HealthResponse"}}}}}}},"/predict":{"post":{"tags":["Verification"],"summary":"Predict Endpoint","operationId":"predict_endpoint_predict_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/PredictRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PredictResponse"}}}},"401":{"description":"API Key invalid or missing"},"422":{"description":"Invalid payload (missing images, oversized, wrong format)"},"429":{"description":"Rate limit exceeded"},"500":{"description":"Internal processing error"},"503":{"description":"Models not loaded"}},"security":[{"ApiKey":[]}]}},"/predict/file":{"post":{"tags":["Verification"],"summary":"Predict File Endpoint","description":"Verify animal identity using image file uploads (JPEG or PNG).\n\nSend 3 reference photos and 1 probe photo as multipart/form-data.\nFiles are decoded, resized to 56x56, normalized, and embedded.\nSame decision logic and response format as POST /predict.","operationId":"predict_file_endpoint_predict_file_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_predict_file_endpoint_predict_file_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/PredictResponse"}}}},"401":{"description":"API Key invalid or missing"},"422":{"description":"Invalid file (wrong format, oversized, missing)"},"429":{"description":"Rate limit exceeded"},"500":{"description":"Internal processing error"},"503":{"description":"Models not loaded"}},"security":[{"ApiKey":[]}]}},"/domain-check":{"post":{"tags":["Domain Check"],"summary":"Domain Check Endpoint","operationId":"domain_check_endpoint_domain_check_post","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainCheckRequest"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainCheckResponse"}}}},"401":{"description":"API Key invalid or missing"},"422":{"description":"Invalid payload (missing image, oversized, wrong format)"},"429":{"description":"Rate limit exceeded"},"500":{"description":"Internal processing error"},"503":{"description":"Models not loaded"}},"security":[{"ApiKey":[]}]}},"/domain-check/file":{"post":{"tags":["Domain Check"],"summary":"Domain Check File Endpoint","description":"Check if a single image file is a valid muzzle photo.\n\nSend 1 photo as multipart/form-data.\nSame validation logic and response format as POST /domain-check.","operationId":"domain_check_file_endpoint_domain_check_file_post","requestBody":{"content":{"multipart/form-data":{"schema":{"$ref":"#/components/schemas/Body_domain_check_file_endpoint_domain_check_file_post"}}},"required":true},"responses":{"200":{"description":"Successful Response","content":{"application/json":{"schema":{"$ref":"#/components/schemas/DomainCheckResponse"}}}},"401":{"description":"API Key invalid or missing"},"422":{"description":"Invalid file (wrong format, oversized, missing)"},"429":{"description":"Rate limit exceeded"},"500":{"description":"Internal processing error"},"503":{"description":"Models not loaded"}},"security":[{"ApiKey":[]}]}}},"components":{"schemas":{"Body_domain_check_file_endpoint_domain_check_file_post":{"properties":{"photo":{"type":"string","contentMediaType":"application/octet-stream","title":"Photo","description":"Muzzle image to validate (JPEG or PNG)"}},"type":"object","required":["photo"],"title":"Body_domain_check_file_endpoint_domain_check_file_post"},"Body_predict_file_endpoint_predict_file_post":{"properties":{"reference_photo_1":{"type":"string","contentMediaType":"application/octet-stream","title":"Reference Photo 1","description":"First reference muzzle image (JPEG or PNG)"},"reference_photo_2":{"type":"string","contentMediaType":"application/octet-stream","title":"Reference Photo 2","description":"Second reference muzzle image (JPEG or PNG)"},"reference_photo_3":{"type":"string","contentMediaType":"application/octet-stream","title":"Reference Photo 3","description":"Third reference muzzle image (JPEG or PNG)"},"probe_photo":{"type":"string","contentMediaType":"application/octet-stream","title":"Probe Photo","description":"Probe muzzle image to verify (JPEG or PNG)"}},"type":"object","required":["reference_photo_1","reference_photo_2","reference_photo_3","probe_photo"],"title":"Body_predict_file_endpoint_predict_file_post"},"CriterionResult":{"properties":{"metric":{"type":"string","title":"Metric","description":"Metric name (e.g. 'cosine_similarity', 'euclidean_distance')"},"value":{"type":"number","title":"Value","description":"Observed value"},"threshold":{"type":"number","title":"Threshold","description":"Required threshold"},"passed":{"type":"boolean","title":"Passed","description":"Whether the criterion met the threshold"},"description":{"type":"string","title":"Description","description":"Human-readable explanation of this criterion"}},"type":"object","required":["metric","value","threshold","passed","description"],"title":"CriterionResult","examples":[{"description":"Cosine similarity between probe and reference template is 0.15, below the required 0.62","metric":"cosine_similarity","passed":false,"threshold":0.62,"value":0.15}]},"Decision":{"properties":{"verdict":{"type":"string","title":"Verdict","description":"Human-readable verdict: 'MATCH', 'NO_MATCH', or 'INVALID_INPUT'"},"score":{"type":"number","title":"Score","description":"Ensemble cosine similarity between probe and reference template -- the primary match metric"},"confidence":{"type":"number","title":"Confidence","description":"Overall confidence score derived from euclidean distance"},"confidence_label":{"type":"string","title":"Confidence Label","description":"Confidence label: NOT_THE_ANIMAL, FAIR, GOOD, or EXCELLENT"},"explanation":{"type":"string","title":"Explanation","description":"Plain-language explanation of the result and why it was reached"}},"type":"object","required":["verdict","score","confidence","confidence_label","explanation"],"title":"Decision","examples":[{"confidence":0.54,"confidence_label":"FAIR","explanation":"The probe photo matches the reference animal with FAIR confidence. All decision criteria passed.","score":0.88,"verdict":"MATCH"},{"confidence":0.0,"confidence_label":"NOT_THE_ANIMAL","explanation":"The probe photo does not match the reference animal. Cosine similarity (0.15) is far below the required threshold (0.62).","score":0.15,"verdict":"NO_MATCH"}]},"DomainCheckDecision":{"properties":{"verdict":{"type":"string","title":"Verdict","description":"Validation verdict: 'VALID_MUZZLE' or 'INVALID_INPUT'"},"explanation":{"type":"string","title":"Explanation","description":"Plain-language explanation of the validation result"}},"type":"object","required":["verdict","explanation"],"title":"DomainCheckDecision","examples":[{"explanation":"The image appears to be a valid muzzle photo. All quality criteria passed.","verdict":"VALID_MUZZLE"},{"explanation":"The image does not appear to be a valid muzzle photo. Failed criteria: embedding_collapse, low_variance.","verdict":"INVALID_INPUT"}]},"DomainCheckRequest":{"properties":{"photo":{"type":"string","title":"Photo","description":"Base64-encoded muzzle image to validate"}},"type":"object","required":["photo"],"title":"DomainCheckRequest","examples":[{"photo":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="}]},"DomainCheckResponse":{"properties":{"decision":{"$ref":"#/components/schemas/DomainCheckDecision","description":"Validation verdict and explanation"},"embedding_variance":{"type":"number","title":"Embedding Variance","description":"Variance of ensemble embedding values"},"cross_model_consistency":{"type":"number","title":"Cross Model Consistency","description":"Mean pairwise cosine similarity between individual model embeddings"},"summary":{"$ref":"#/components/schemas/DomainCheckSummary","description":"Detailed criteria breakdown"}},"type":"object","required":["decision","embedding_variance","cross_model_consistency","summary"],"title":"DomainCheckResponse","examples":[{"cross_model_consistency":0.89,"decision":{"explanation":"The image appears to be a valid muzzle photo. All quality criteria passed.","verdict":"VALID_MUZZLE"},"embedding_variance":0.031,"summary":{"criteria":[{"description":"Embedding variance (0.031) indicates meaningful content (>0.005)","metric":"embedding_variance","passed":true,"threshold":0.005,"value":0.031},{"description":"Pairwise similarity across 3 models is consistent (0.89 > 0.60)","metric":"cross_model_consistency","passed":true,"threshold":0.6,"value":0.89}]}}]},"DomainCheckSummary":{"properties":{"criteria":{"items":{"$ref":"#/components/schemas/CriterionResult"},"type":"array","title":"Criteria","description":"List of validation criteria showing what was checked, the observed value, threshold, and whether it passed"}},"type":"object","required":["criteria"],"title":"DomainCheckSummary","examples":[{"criteria":[{"description":"Embedding variance (0.031) indicates meaningful content (>0.005)","metric":"embedding_variance","passed":true,"threshold":0.005,"value":0.031},{"description":"Pairwise similarity across 3 models is consistent (0.89 > 0.60)","metric":"cross_model_consistency","passed":true,"threshold":0.6,"value":0.89}]}]},"EnsembleSummary":{"properties":{"template_vs_probe_cosine":{"type":"number","title":"Template Vs Probe Cosine","description":"Cosine similarity between reference template and probe (ensemble)"},"template_vs_probe_distance":{"type":"number","title":"Template Vs Probe Distance","description":"Euclidean distance between reference template and probe (ensemble)"},"mean_cosine":{"type":"number","title":"Mean Cosine","description":"Mean cosine similarity across 3 reference-to-probe comparisons (ensemble)"},"min_cosine":{"type":"number","title":"Min Cosine","description":"Minimum cosine similarity across 3 reference-to-probe comparisons (ensemble)"},"max_cosine":{"type":"number","title":"Max Cosine","description":"Maximum cosine similarity across 3 reference-to-probe comparisons (ensemble)"}},"type":"object","required":["template_vs_probe_cosine","template_vs_probe_distance","mean_cosine","min_cosine","max_cosine"],"title":"EnsembleSummary","examples":[{"max_cosine":0.91,"mean_cosine":0.88,"min_cosine":0.85,"template_vs_probe_cosine":0.88,"template_vs_probe_distance":0.46}]},"HealthResponse":{"properties":{"status":{"type":"string","title":"Status","description":"API health status: 'ok' or 'degraded'"},"models_loaded":{"items":{"type":"integer"},"type":"array","title":"Models Loaded","description":"List of loaded model IDs"}},"type":"object","required":["status","models_loaded"],"title":"HealthResponse","examples":[{"models_loaded":[60,62,64],"status":"ok"},{"models_loaded":[],"status":"degraded"}]},"ModelVerification":{"properties":{"model_id":{"type":"integer","title":"Model Id","description":"Model identifier"},"template_vs_probe_cosine":{"type":"number","title":"Template Vs Probe Cosine","description":"Cosine similarity between reference template and probe"},"template_vs_probe_distance":{"type":"number","title":"Template Vs Probe Distance","description":"Euclidean distance between reference template and probe"},"reference_vs_probe":{"items":{"$ref":"#/components/schemas/ReferenceVsProbe"},"type":"array","title":"Reference Vs Probe","description":"Per-reference comparison to probe"},"mean_cosine":{"type":"number","title":"Mean Cosine","description":"Mean cosine similarity across 3 reference-to-probe comparisons"},"min_cosine":{"type":"number","title":"Min Cosine","description":"Minimum cosine similarity across 3 reference-to-probe comparisons"},"max_cosine":{"type":"number","title":"Max Cosine","description":"Maximum cosine similarity across 3 reference-to-probe comparisons"}},"type":"object","required":["model_id","template_vs_probe_cosine","template_vs_probe_distance","reference_vs_probe","mean_cosine","min_cosine","max_cosine"],"title":"ModelVerification","examples":[{"max_cosine":0.9,"mean_cosine":0.87,"min_cosine":0.83,"model_id":60,"reference_vs_probe":[{"cosine_similarity":0.89,"euclidean_distance":0.42,"reference_index":0},{"cosine_similarity":0.83,"euclidean_distance":0.55,"reference_index":1},{"cosine_similarity":0.9,"euclidean_distance":0.4,"reference_index":2}],"template_vs_probe_cosine":0.87,"template_vs_probe_distance":0.49}]},"PredictDetails":{"properties":{"models":{"items":{"$ref":"#/components/schemas/ModelVerification"},"type":"array","title":"Models","description":"Per-model verification details for debugging and auditing"},"ensemble":{"$ref":"#/components/schemas/EnsembleSummary","description":"Ensemble verification summary with aggregated metrics"},"reference_mutual_similarity":{"type":"number","title":"Reference Mutual Similarity","description":"Mean cosine similarity between reference photos"}},"type":"object","required":["models","ensemble","reference_mutual_similarity"],"title":"PredictDetails","examples":[{"ensemble":{"max_cosine":0.91,"mean_cosine":0.88,"min_cosine":0.85,"template_vs_probe_cosine":0.88,"template_vs_probe_distance":0.46},"models":[{"max_cosine":0.9,"mean_cosine":0.87,"min_cosine":0.83,"model_id":60,"reference_vs_probe":[{"cosine_similarity":0.89,"euclidean_distance":0.42,"reference_index":0},{"cosine_similarity":0.83,"euclidean_distance":0.55,"reference_index":1},{"cosine_similarity":0.9,"euclidean_distance":0.4,"reference_index":2}],"template_vs_probe_cosine":0.87,"template_vs_probe_distance":0.49}],"reference_mutual_similarity":0.8}]},"PredictRequest":{"properties":{"reference_photos":{"items":{"type":"string"},"type":"array","maxItems":3,"minItems":3,"title":"Reference Photos","description":"3 base64-encoded reference muzzle images (known animal enrollment template)"},"probe_photo":{"type":"string","title":"Probe Photo","description":"Base64-encoded probe muzzle image (unknown animal to verify)"}},"type":"object","required":["reference_photos","probe_photo"],"title":"PredictRequest","examples":[{"probe_photo":"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==","reference_photos":["iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==","iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==","iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="]}]},"PredictResponse":{"properties":{"decision":{"$ref":"#/components/schemas/Decision","description":"Consolidated decision with verdict, score, confidence, and explanation"},"summary":{"$ref":"#/components/schemas/PredictSummary","description":"Criteria breakdown showing what was checked and results"},"details":{"$ref":"#/components/schemas/PredictDetails","description":"Technical details per model and ensemble for debugging and auditing"}},"type":"object","required":["decision","summary","details"],"title":"PredictResponse","examples":[{"decision":{"confidence":0.54,"confidence_label":"FAIR","explanation":"The probe photo matches the reference animal with FAIR confidence. All decision criteria passed.","score":0.88,"verdict":"MATCH"},"details":{"ensemble":{"max_cosine":0.91,"mean_cosine":0.88,"min_cosine":0.85,"template_vs_probe_cosine":0.88,"template_vs_probe_distance":0.46},"models":[{"max_cosine":0.9,"mean_cosine":0.87,"min_cosine":0.83,"model_id":60,"reference_vs_probe":[{"cosine_similarity":0.89,"euclidean_distance":0.42,"reference_index":0},{"cosine_similarity":0.83,"euclidean_distance":0.55,"reference_index":1},{"cosine_similarity":0.9,"euclidean_distance":0.4,"reference_index":2}],"template_vs_probe_cosine":0.87,"template_vs_probe_distance":0.49}],"reference_mutual_similarity":0.8},"summary":{"criteria":[{"description":"Cosine similarity between probe and reference template (0.88) meets the required threshold (0.62)","metric":"cosine_similarity","passed":true,"threshold":0.62,"value":0.88},{"description":"Euclidean distance between probe and reference template (0.46) is within the allowed limit (0.85)","metric":"euclidean_distance","passed":true,"threshold":0.85,"value":0.46},{"description":"Reference photos mutual similarity (0.80) exceeds the minimum required (0.40)","metric":"mutual_similarity","passed":true,"threshold":0.4,"value":0.8},{"description":"Photos appear to be valid muzzle images","metric":"domain_check","passed":true,"threshold":1,"value":1}]}}]},"PredictSummary":{"properties":{"criteria":{"items":{"$ref":"#/components/schemas/CriterionResult"},"type":"array","title":"Criteria","description":"List of decision criteria showing what was checked, the observed value, threshold, and whether it passed"}},"type":"object","required":["criteria"],"title":"PredictSummary","examples":[{"criteria":[{"description":"Cosine similarity between probe and reference template is 0.15, below the required 0.62","metric":"cosine_similarity","passed":false,"threshold":0.62,"value":0.15},{"description":"Euclidean distance between probe and reference template is 1.25, above the allowed 0.85","metric":"euclidean_distance","passed":false,"threshold":0.85,"value":1.25}]}]},"ReferenceVsProbe":{"properties":{"reference_index":{"type":"integer","title":"Reference Index","description":"Index of the reference photo (0, 1, or 2)"},"cosine_similarity":{"type":"number","title":"Cosine Similarity","description":"Cosine similarity between reference and probe"},"euclidean_distance":{"type":"number","title":"Euclidean Distance","description":"Euclidean distance between reference and probe"}},"type":"object","required":["reference_index","cosine_similarity","euclidean_distance"],"title":"ReferenceVsProbe","examples":[{"cosine_similarity":0.89,"euclidean_distance":0.42,"reference_index":0},{"cosine_similarity":0.91,"euclidean_distance":0.38,"reference_index":1}]}},"securitySchemes":{"ApiKey":{"type":"apiKey","in":"header","name":"X-API-Key","description":"API key for authentication. Obtain from your account settings."}}},"tags":[{"name":"Health","description":"Service health and model status checks."},{"name":"Verification","description":"Muzzle biometric verification endpoints."},{"name":"Domain Check","description":"Single-image muzzle validation endpoints. Check if a photo is a valid muzzle image."}]}