GET /v1/health
Is the API up
Responses: 200 OK
Base URL https://api.agentblue.app · OpenAPI https://api.agentblue.app/v1/openapi.json · every service lives under /v1/{service}
Every call to a service needs an API key, sent as Authorization: Bearer blue_… (or X-Api-Key: blue_…). A key carries scopes — the services it may call — and its own calls-per-minute. Keys are issued by the agentblue team; the key is shown once and never stored in clear.
curl https://api.agentblue.app/v1/me -H "Authorization: Bearer blue_XXXX"Success answers are JSON objects with status 200 (or 201). Every error is an RFC 9457 problem document, application/problem+json, with a stable type (https://api.agentblue.app/problems/{slug}), a title, the status, a human detail and the request id as instance (also in the X-Request-Id header on every response — quote it when you write to us).
{"type":"https://api.agentblue.app/problems/no-face","title":"No face found","status":422,"detail":"no face scored above 0.7","instance":"req_…"}Rate limits are per key per minute; every response carries X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (seconds). Over the limit you get 429 with Retry-After. Outputs the API stores for you (crops, renders) are served from /files/{id}.{ext}: unguessable, immutable, cached a year, kept as the response's expires_at says.
| Problem type | Meaning |
|---|---|
bad-request | Bad request |
invalid-json | The body is not valid JSON |
validation | A parameter is invalid |
invalid-url | The URL is invalid |
blocked-url | The URL is not allowed |
fetch-failed | The URL could not be fetched |
unsupported-media-type | Unsupported media type |
payload-too-large | The payload is too large |
no-face | No face found |
unauthorized | A valid API key is required |
forbidden | The key may not call this service |
not-found | Not found |
method-not-allowed | Method not allowed |
rate-limited | Too many requests |
engine-failed | The engine failed |
internal | Internal error |
Health, your key, this document, stored files
/v1/healthIs the API up
Responses: 200 OK
/v1/meThe key you are calling with
Responses: 200 The key · 401 Unauthorized
/v1/openapi.jsonThis document
Responses: 200 OpenAPI 3.1
/files/{id}A stored output (a crop, a render…), immutable, cached a year
| Parameter | Type | Where | Description |
|---|---|---|---|
id * | string | path | the id with its extension, as a service returned it (e.g. 3fK…9q.webp) |
Responses: 200 The file · 404 NotFound
Face detection and face-centred crops for profile pictures: send an image (by URL, upload or base64), get the faces with boxes, confidence and landmarks, or a square (or any aspect) crop centred on the face as a stored file. Engine: OpenCV YuNet (2023), on our own servers — nothing leaves the platform.
/v1/faces/detectDetect faces
Returns every face found (largest first) with its box, confidence and five landmarks. GET with the same parameters in the query string works too.
| Parameter | Type | Where | Description |
|---|---|---|---|
url | string (uri) | body | A public http(s) URL of a JPEG, PNG, GIF or WebP (up to 25 MB). Give one of url, file, image_base64. |
image_base64 | string | body | The image as base64 (a data: URL prefix is fine). |
curl -X POST https://api.agentblue.app/v1/faces/detect \
-H "Authorization: Bearer blue_XXXX" -H "Content-Type: application/json" \
-d '{"url": "https://example.com/photo.jpg"}'Responses: 200 The faces · 401 Unauthorized · 403 Forbidden · 400 Problem · 413 Problem · 415 Problem · 429 RateLimited · 502 Problem
{"ok":true,"image":{"width":1200,"height":800,"mime":"image/jpeg","bytes":183452,"source":"https://example.com/photo.jpg"},"count":1,
"faces":[{"box":{"x":512,"y":210,"width":190,"height":240},"confidence":0.98,"landmarks":{"right_eye":[560,290],"left_eye":[650,292],"nose":[605,345],"mouth_right":[570,395],"mouth_left":[640,397]}}],
"model":"yunet-2023mar","ms":84}/v1/faces/cropFace-centred crop (a profile picture)
Finds the face (the largest by default), cuts a crop centred on it with `padding` around (0.45 = the face box's larger side plus 45 % on every side), in the `aspect` you ask, resized to `size`, encoded as `format`, and stores it: the answer carries the file's URL. When no face is found the answer is a 422 `no-face` problem — or pass `no_face=center` to crop the middle of the picture instead.
| Parameter | Type | Where | Description |
|---|---|---|---|
url | string (uri) | body | A public http(s) URL of a JPEG, PNG, GIF or WebP (up to 25 MB). Give one of url, file, image_base64. |
image_base64 | string | body | The image as base64 (a data: URL prefix is fine). |
size | integer | body | the output's longer side in pixels, 16–2048 · default 400 |
padding | number | body | room around the face, as a fraction of the face size, 0–3 · default 0.45 |
aspect | string | body | the crop's aspect, like 1:1, 4:3, 3:4, 16:9 · default 1:1 |
format | string | body | · default webp · one of webp, jpg, png |
quality | integer | body | 1–100 (webp, jpg) · default 85 |
which | string | body | largest, first (leftmost), or a face index from /detect · default largest |
no_face | string | body | · default error · one of error, center |
curl -X POST https://api.agentblue.app/v1/faces/crop \
-H "Authorization: Bearer blue_XXXX" -H "Content-Type: application/json" \
-d '{"url": "https://example.com/photo.jpg", "size": 400, "format": "webp"}'Responses: 201 The crop, stored · 422 No face (type no-face) · 401 Unauthorized · 403 Forbidden · 400 Problem · 413 Problem · 415 Problem · 429 RateLimited · 502 Problem
{"ok":true,"count":1,"face":{"box":{"x":512,"y":210,"width":190,"height":240},"confidence":0.98,…},
"crop":{"x":385,"y":97,"width":456,"height":456},
"file":{"id":"3fKq…","url":"https://api.agentblue.app/files/3fKq….webp","mime":"image/webp","bytes":21804,"width":400,"height":400,"expires_at":"2027-10-17 10:00:00"},
"options":{"size":400,"padding":0.45,"aspect":"1:1","format":"webp","quality":85,"which":"largest","no_face":"error"},"model":"yunet-2023mar","ms":131}Problem — RFC 9457 problem details — every error of the API{
"type": "object",
"description": "RFC 9457 problem details \u2014 every error of the API",
"properties": {
"type": {
"type": "string",
"format": "uri",
"example": "https://api.agentblue.app/problems/no-face"
},
"title": {
"type": "string"
},
"status": {
"type": "integer"
},
"detail": {
"type": "string"
},
"instance": {
"type": "string",
"description": "the request id (also in X-Request-Id) \u2014 quote it when you write to us"
},
"errors": {
"type": "object",
"additionalProperties": {
"type": "string"
},
"description": "field \u2192 what is wrong (validation problems)"
}
},
"required": [
"type",
"title",
"status"
]
}KeyInfo{
"type": "object",
"properties": {
"id": {
"type": "integer"
},
"name": {
"type": "string"
},
"prefix": {
"type": "string"
},
"scopes": {
"type": "array",
"items": {
"type": "string"
}
},
"rate_per_minute": {
"type": "integer"
}
}
}StoredFile — An output the API stored for you{
"type": "object",
"description": "An output the API stored for you",
"properties": {
"id": {
"type": "string"
},
"url": {
"type": "string",
"format": "uri"
},
"mime": {
"type": "string"
},
"bytes": {
"type": "integer"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
},
"expires_at": {
"type": "string",
"nullable": true,
"description": "when the file is removed (null = kept)"
}
}
}Point — [x, y] in the original image{
"type": "array",
"items": {
"type": "integer"
},
"minItems": 2,
"maxItems": 2,
"description": "[x, y] in the original image"
}Face{
"type": "object",
"properties": {
"box": {
"type": "object",
"properties": {
"x": {
"type": "integer"
},
"y": {
"type": "integer"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
}
},
"description": "pixels in the original image"
},
"confidence": {
"type": "number",
"description": "0\u20131"
},
"landmarks": {
"type": "object",
"properties": {
"right_eye": {
"$ref": "#/components/schemas/Point"
},
"left_eye": {
"$ref": "#/components/schemas/Point"
},
"nose": {
"$ref": "#/components/schemas/Point"
},
"mouth_right": {
"$ref": "#/components/schemas/Point"
},
"mouth_left": {
"$ref": "#/components/schemas/Point"
}
}
}
}
}DetectResult{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"image": {
"type": "object",
"properties": {
"width": {
"type": "integer"
},
"height": {
"type": "integer"
},
"mime": {
"type": "string"
},
"bytes": {
"type": "integer"
},
"source": {
"type": "string",
"enum": [
"url",
"upload",
"base64"
]
}
}
},
"count": {
"type": "integer"
},
"faces": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Face"
}
},
"model": {
"type": "string"
},
"ms": {
"type": "integer"
}
}
}CropResult{
"type": "object",
"properties": {
"ok": {
"type": "boolean"
},
"image": {
"type": "object",
"properties": {
"width": {
"type": "integer"
},
"height": {
"type": "integer"
},
"mime": {
"type": "string"
},
"bytes": {
"type": "integer"
},
"source": {
"type": "string",
"enum": [
"url",
"upload",
"base64"
]
}
}
},
"count": {
"type": "integer"
},
"face": {
"$ref": "#/components/schemas/Face"
},
"crop": {
"type": "object",
"properties": {
"x": {
"type": "integer"
},
"y": {
"type": "integer"
},
"width": {
"type": "integer"
},
"height": {
"type": "integer"
}
}
},
"file": {
"$ref": "#/components/schemas/StoredFile"
},
"options": {
"type": "object"
},
"model": {
"type": "string"
},
"ms": {
"type": "integer"
}
}
}