Quickstart
This guide runs one listing photo through the pipeline — perspective correction, signature tone, a dramatic sky — first with an SDK, then with raw curl so you can see every request on the wire.
Get an API key
Request a key for your workspace, then export it. Keys start with sk- and ride the X-API-Key header (the SDKs read BKBNLAB_API_KEY from the environment).
export BKBNLAB_API_KEY=sk-...With an SDK
The SDKs hide the upload dance and the realtime plumbing: upload, order, wait, save. wait() follows the order live over a single multiplexed WebSocket and falls back to polling on restrictive networks.
npm install @bkbnlab/api The Python (bkbnlab) and Kotlin (ai.bkbnlab:api-kotlin) clients share the same surface; their packages are being published.
import { BkbnClient } from '@bkbnlab/api'
const bkbn = new BkbnClient() // reads BKBNLAB_API_KEY
const photo = await bkbn.upload('living-room.jpg')
const order = await bkbn.order({
inputs: [photo],
perspective: true,
tonecraft: true,
sky: 'dramatic',
})
const result = await order.wait({
onProgress: (p) => console.log(p.stage, p.percent),
})
await result.save('enhanced.jpg')The same, on the wire
Four steps with plain curl. Every JSON response is wrapped in a data envelope; errors come back as { "error": { "status", "reason" } }.
Step 1: Upload your image
Uploads are presigned: declare the file to get an assetId and a one-time uploadUrl, PUT the bytes there (they never transit the API), then confirm.
CREATE=$(curl -s -X POST https://api.bkbn.ai/v1/assets \
-H "X-API-Key: $BKBNLAB_API_KEY" -H "Content-Type: application/json" \
-d '{"filename": "living-room.jpg", "contentType": "image/jpeg"}')
ASSET_ID=$(echo "$CREATE" | jq -r .data.assetId)
curl -s -X PUT --upload-file living-room.jpg -H "Content-Type: image/jpeg" \
"$(echo "$CREATE" | jq -r .data.uploadUrl)"
curl -s -X POST https://api.bkbn.ai/v1/assets/$ASSET_ID/uploaded \
-H "X-API-Key: $BKBNLAB_API_KEY" Ingest then normalizes the file. Poll until the asset is ready:
until [ "$(curl -s https://api.bkbn.ai/v1/assets/$ASSET_ID/status \
-H "X-API-Key: $BKBNLAB_API_KEY" | jq -r .data.status)" = "ready" ]; do sleep 1; doneStep 2: Submit the order
One endpoint for every feature: POST /v1/orders. inputs takes 1-5 asset ids — the exposure brackets of one scene; HDR merge is automatic when there is more than one. The other fields are the feature options.
ORDER_ID=$(curl -s -X POST https://api.bkbn.ai/v1/orders \
-H "X-API-Key: $BKBNLAB_API_KEY" -H "Content-Type: application/json" \
-d '{
"inputs": ["'$ASSET_ID'"],
"perspective": true,
"tonecraft": true,
"sky": true, "sky_style": "dramatic"
}' | jq -r .data.orderId)Step 3: Follow it live
The order streams its lifecycle over SSE: a snapshot on connect, then progress events, then completed or failed — at which point the stream closes on its own.
curl -sN https://api.bkbn.ai/v1/orders/$ORDER_ID/events \
-H "X-API-Key: $BKBNLAB_API_KEY" Polling works too: GET /v1/orders/$ORDER_ID returns the same summary (status, progress, an ETA estimate while running).
Step 4: Download the result
OUTPUT_ID=$(curl -s https://api.bkbn.ai/v1/orders/$ORDER_ID \
-H "X-API-Key: $BKBNLAB_API_KEY" | jq -r .data.outputAssetId)
curl -o enhanced.jpg https://api.bkbn.ai/v1/assets/$OUTPUT_IDNext steps
- Authentication: API keys, rotation, scopes.
- Features reference: every feature and its options.
- Streaming: WebSocket and SSE in detail.