API
Run any of your account's 9 generation modes remotely: send a photo, get back the transformed result. One endpoint, one request, JSON response.
Overview
Every API project (created from the API projects page) is configured to run exactly one of the 9 generation modes below - the same modes an event on this platform can run. A single request runs that project's configured mode and returns the resulting image inline as base64 - there is no separate polling step and no file storage involved on either side.
Authentication
Every request is authenticated with an API key tied to one API project. Create a project and get its key from the API projects page in your dashboard (sign-in required). The key is shown in full exactly once, at creation or refresh - store it somewhere safe. It has no expiry; refresh it any time to immediately invalidate the previous one.
Send it as a bearer token:
Authorization: Bearer pb_live_...Endpoint
POST /api/v1/generate
Request
multipart/form-data:
| Field | Type | Description |
|---|---|---|
image | file, required | The subject/guest photo. JPG, PNG, or WebP. Max 15MB (5MB for a project configured to "headshot" mode - that pipeline's background-removal step has its own hard limit). |
reference | file, conditional | A background or garment photo. Required only when your project's configured mode is one of faceswap/identity/headless/apparel/combination/teleport (all of which need a scene or garment to compose onto; for combination and teleport this is the scene) - ignored otherwise. There is no saved asset library on this API - you always supply the reference image inline, every call. |
garment | file, conditional | combination mode only, required there: the uniform/garment laid out with no person wearing it. Ignored for every other mode. |
pose | file, conditional | combination mode only, required there: a full-body photo of someone (or a mannequin) in the pose to copy - only the body pose is taken from it, never the subject, clothing, or background. Ignored for every other mode. |
prompt | text, optional | Only used when your project's mode is selfprompt - your own description of the scene you want, up to 500 characters. |
Mode is not a request field - it's whatever your project is configured to run, set once on the API projects page.
Response (200)
JSON, with the resulting image returned inline as base64 (not a URL - nothing is stored server-side):
{
"image": "<base64-encoded image bytes>",
"contentType": "image/png",
"width": 1200,
"height": 1800,
"mode": "faceswap",
"creditsUsed": 1,
"creditBalance": 149
}Errors
| Status | Meaning |
|---|---|
| 400 | Missing image (or reference/garment/pose, when your mode needs them), or not multipart/form-data |
| 401 | Missing or invalid Authorization header / API key |
| 402 | Insufficient credits on the project's account |
| 413 | An uploaded file exceeds the size limit |
| 422 | No face was detected in the photo ("headshot" mode only) |
| 429 | Rate limit exceeded for this API key or IP |
| 502 | The pipeline failed |
| 504 | The pipeline didn't complete in time - safe to retry |
Every non-200 response body is { "error": "<message>" }.
A note on response time
This endpoint blocks until the result is ready and returns it in one response - there is no separate poll step to implement. For headshot mode this is typically a few seconds. For the other, image-editing modes it can occasionally take noticeably longer (up to several minutes in the worst case) since those run a genuine AI image edit rather than a fast, narrow crop - set a generous client-side timeout if you're calling one of those modes.
Billing
Each successful call debits credits from the same account credit balance your kiosk events use - one shared balance, topped up from your dashboard's billing page. The per-call cost is set per mode by the account's admin and returned as creditsUsed in every successful response, with your remaining balance as creditBalance. A failed call is never billed.
Rate limits
Up to 30 requests per minute per API key. A 429 response means you've exceeded that - wait and retry.
Example
curl
curl -X POST https://oobler.oangle.com/api/v1/generate \
-H "Authorization: Bearer pb_live_..." \
-F "[email protected]" \
-F "[email protected]"Node.js
const form = new FormData();
form.append("image", fs.readFileSync("photo.jpg"), "photo.jpg");
form.append("reference", fs.readFileSync("scene.jpg"), "scene.jpg");
const res = await fetch("https://oobler.oangle.com/api/v1/generate", {
method: "POST",
headers: { Authorization: "Bearer pb_live_..." },
body: form,
});
const { image, contentType } = await res.json();
fs.writeFileSync("result.png", Buffer.from(image, "base64"));Python
import base64, requests
with open("photo.jpg", "rb") as image, open("scene.jpg", "rb") as reference:
res = requests.post(
"https://oobler.oangle.com/api/v1/generate",
headers={"Authorization": "Bearer pb_live_..."},
files={"image": image, "reference": reference},
)
data = res.json()
with open("result.png", "wb") as out:
out.write(base64.b64decode(data["image"]))Machine-readable spec
An OpenAPI 3.0 description of this endpoint is available at /openapi.json.