Quickstart
This guide runs one listing photo through the pipeline — perspective correction, signature tone, a high-cloud 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) examples target SDK 1.1.0. For Python and Kotlin installation and repository access, contact us; these packages are not currently available on public PyPI or Maven Central. Kotlin examples run on the JVM.
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: 'high_cloud',
})
const result = await order.wait({
onProgress: (p) => console.log(p.stage, p.percent),
})
await result.save('enhanced.jpg')The same, on the wire
Run the following steps in the same Bash session with curl and jq installed. Each function stops on failure without changing your interactive shell options. Continue to the next step only after the previous one succeeds. Every successful 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 short-lived uploadUrl, PUT the bytes there (they never transit the API), then confirm.
Ingest states are pending, validating, processing, ready and failed. Define this helper to wait for readiness with a client deadline:
# Bash: a five-minute client deadline, not a processing-time guarantee
wait_for_asset () (
set -euo pipefail
DEADLINE=$((SECONDS + 300))
while [ "$SECONDS" -lt "$DEADLINE" ]; do
STATUS=$(curl -fsS --max-time 30 "https://api.bkbn.ai/v1/assets/$1/status" \
-H "X-API-Key: $BKBNLAB_API_KEY")
case "$(printf '%s' "$STATUS" | jq -er .data.status)" in
ready) exit 0 ;;
failed) printf '%s\n' "$STATUS" >&2; exit 1 ;;
pending|validating|processing) sleep 1 ;;
*) printf 'Unexpected asset status: %s\n' "$STATUS" >&2; exit 1 ;;
esac
done
echo "Timed out waiting for asset $1; inspect its status before retrying." >&2
exit 1
)upload_photo () (
set -euo pipefail
CREATE=$(curl -fsS -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"}')
ID=$(printf '%s' "$CREATE" | jq -er .data.assetId)
UPLOAD_URL=$(printf '%s' "$CREATE" | jq -er .data.uploadUrl)
curl -fsS -X PUT --upload-file living-room.jpg -H "Content-Type: image/jpeg" \
"$UPLOAD_URL" > /dev/null
curl -fsS -X POST "https://api.bkbn.ai/v1/assets/$ID/uploaded" \
-H "X-API-Key: $BKBNLAB_API_KEY" > /dev/null
wait_for_asset "$ID"
printf '%s\n' "$ID"
)
ASSET_ID=$(upload_photo)If ingest fails or times out, the function prints the error and returns a nonzero status. Inspect the error before uploading again.
Step 2: Submit the order
One endpoint for every feature: POST /v1/orders. inputs takes 1-5 ready, uploaded asset ids owned by your key — the exposure brackets of one scene; HDR merge is automatic when there is more than one. The other fields are the feature options.
submit_order () (
set -euo pipefail
: "${ASSET_ID:?Upload must succeed before submitting an order}"
curl -fsS -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": "high_cloud"
}' | jq -er .data.orderId
)
ORDER_ID=$(submit_order)Step 3: Follow it live
The order streams its lifecycle over SSE: a snapshot on connect, then progress events, then a terminal state: completed, failed, cancelled or expired — at which point the stream closes on its own.
curl -fsSN 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
Continue only when the order is completed. If the stream drops, reconnect or read the order summary before continuing. Failed, cancelled and expired orders have no result to download.
Authenticated GET /v1/assets/:id returns JSON with data.url and data.expiresIn (seconds). Fetch that signed URL to get the image bytes. If it expires, request a new URL.
download_result () (
set -euo pipefail
: "${ORDER_ID:?Submit an order before downloading}"
# Download only a completed order
OUTPUT_ID=$(curl -fsS "https://api.bkbn.ai/v1/orders/$ORDER_ID" \
-H "X-API-Key: $BKBNLAB_API_KEY" \
| jq -er '.data | select(.status == "completed") | .outputAssetId')
# This authenticated API call returns JSON containing a short-lived URL
URL=$(curl -fsS "https://api.bkbn.ai/v1/assets/$OUTPUT_ID" \
-H "X-API-Key: $BKBNLAB_API_KEY" | jq -er .data.url)
# Fetch the image from storage without sending your API key
curl -fsS --output enhanced.jpg "$URL"
)
download_resultNext steps
- Authentication: API keys, rotation, scopes.
- Features reference: every feature and its options.
- Streaming: WebSocket and SSE in detail.