Skip to content
JeaniJeaniHow it worksFree analysisLifestyleInspectionPricingSign inAnalyse my catalogue
docs

Everything the app does, you can do with a key.

The public API mirrors the web app one-to-one: declare uploads, PUT straight to storage, analyse, estimate, dry-run, process and export. This page is the shape of it; every endpoint, with its request shape, is in the full API reference, the OpenAPI UI the API serves.

  • Concepts
    Account → Workspace → Project → Job → Image (an asset, in the API). Originals are immutable; every output is a version you can restore from. A project's stripRootFolder is off by default — in the app and over the API alike — so every path is stored exactly as you sent it. Create the project with it on to drop the first folder from each path, so that products/football/red.jpg is stored as football/red.jpg.
  • Recipes
    A recipe is a declarative description of positioning, canvas, enhancement, background, output, variants and quality policy. Jobs snapshot it.
  • Credits
    Only operations that actually run are charged. Analysis, inspection and dry runs are free. What a run will cost comes from POST /v1/recipes/{id}/estimate: the expected and worst-case credits for applying that recipe to a project, broken down per operation.
  • Dry runs
    A dry run puts up to ten images through the recipe and reports what inspection made of them: how many passed, were held for review or failed, and the score on each image. It costs nothing and quotes nothing — its creditsCharged is always 0 — so it is not where the price comes from; the estimate is.
  • Smart sampling
    Large jobs watch a rolling inspection window and pause themselves when the failure rate spikes, telling you how many completed images are safe.
  • Exports
    Replacement, variants-only or everything. The ZIP mirrors your folder tree; a manifest lists every file with its checksum and inspection score.
  • API keys
    Scoped keys (read, review, write, admin) for CI and store sync, sent as X-API-Key. All endpoints live under /v1 and use the same JSON shapes as the app.
  • Connecting a Magento store
    Jeani reads the media gallery of each product and writes the processed images back, per SKU, with an integration token.
    1. In Magento admin go to System → Extensions → Integrations → Add New Integration. Under API set Resource Access to Custom and tick Catalog and Stores.
    2. Save, Activate the integration and copy its Access Token; admin login tokens are not accepted, because they expire.
    3. On Magento 2.4.4 and later set Stores → Configuration → Services → OAuth → Consumer Settings → Allow OAuth Access Tokens to be used as standalone Bearer tokens to Yes. Stock installs ship it off and refuse every token until it is on.
    4. In Jeani, enter the storefront address as the Store URL (without /rest). Leave the store code blank unless one store view should own the image labels; a category id limits imports to that category.
    5. Test the connection. The test proves the token can read and update products; a read-only token fails with the resource to grant.
full catalogue run with an api key
API=https://jeani.ai/v1; KEY=pia_live_…
# a key goes in X-API-Key (Bearer is for app sessions); every POST body is JSON
J="-H X-API-Key:$KEY -H Content-Type:application/json"
# 1. workspace, then a project. stripRootFolder is off by default, so paths are stored exactly as
#    sent; on, it drops the first folder (products/football/red.jpg is stored as football/red.jpg)
WS=$(curl -s $J $API/workspaces | jq -r '.[0].id')
PRJ=$(curl -s $J $API/workspaces/$WS/projects \
  -d '{"name":"Autumn catalogue","stripRootFolder":false}' | jq -r .id)
# 2. declare, PUT straight to storage, complete; completion queues the free analysis
DECL=$(curl -s $J $API/projects/$PRJ/uploads \
  -d '{"files":[{"relativePath":"products/football/red.jpg","size":812345,"mimeType":"image/jpeg"}]}')
curl -s -X PUT "$(echo "$DECL" | jq -r '.files[0].upload.url')" \
  -H "Content-Type: image/jpeg" --data-binary @red.jpg
curl -s $J $API/uploads/$(echo "$DECL" | jq -r .session.id)/complete \
  -d "$(echo "$DECL" | jq -c '{files:[.files[]|{assetId}]}')"
until curl -s $J $API/projects/$PRJ | jq -e .analysedAt >/dev/null; do sleep 5; done
# 3. the estimate is what a run will cost; it is the only call that quotes credits
curl -s $J $API/recipes/rcp_sys_amazon_white/estimate \
  -d "{\"projectId\":\"$PRJ\"}" | jq '{expected,worstCase,unanalysed}'
# 4. the dry run is free: up to ten images through the recipe, inspected, 0 credits, no quote
DRY=$(curl -s $J $API/jobs -H "Idempotency-Key: dry-1" \
  -d "{\"projectId\":\"$PRJ\",\"type\":\"DRY_RUN\",\"recipeId\":\"rcp_sys_amazon_white\"}" | jq -r .id)
until curl -s $J $API/jobs/$DRY | jq -r .status | grep -Eqv 'QUEUED|RUNNING'; do sleep 5; done
curl -s $J $API/jobs/$DRY | jq '.progress | {passed,needsReview,failed}'
curl -s $J "$API/projects/$PRJ/assets?sort=qa" | jq '.data[] | {relativePath,status,qaOverall}'
# 5. the real run, then the replacement-ready ZIP
JOB=$(curl -s $J $API/jobs -H "Idempotency-Key: run-1" \
  -d "{\"projectId\":\"$PRJ\",\"type\":\"PROCESS\",\"recipeId\":\"rcp_sys_amazon_white\"}" | jq -r .id)
until curl -s $J $API/jobs/$JOB | jq -r .status | grep -Eqv 'QUEUED|RUNNING'; do sleep 5; done
EXP=$(curl -s $J $API/projects/$PRJ/exports -d '{"mode":"REPLACEMENT"}' | jq -r .id)
until curl -s $J $API/exports/$EXP | jq -r .status | grep -q READY; do sleep 5; done
curl -sL "$(curl -s $J $API/exports/$EXP/download | jq -r .url)" -o catalogue.zip
JeaniJeani© 2026 Jeani AIABN 72 238 565 706privacytermssecuritystatusdpa