Build your first integration in 15 minutes
One working script, start to finish: get a sandbox key, scan a product photo, and handle both a catalog match and a miss — with complete code, not fragments.
This lesson builds one real thing: a script that takes a product photo and returns a name, price, and stock level. It uses POST /api/v1/scan — the same endpoint covered in the reference guide, but framed here as a single project you build in order, from an empty folder to a working result.
- 1
Create a sandbox key
Sign in to developer.askbiz.co, open Settings → API Keys, and create a new key in sandbox (test) mode. A sandbox key returns realistic-shaped responses without debiting your wallet or touching a real inventory — see Build safely with a sandbox key for exactly what it simulates.
# No install needed — this project just uses fetch and a JPEG file. # Create a free account at developer.askbiz.co, then create a sandbox key # from the dashboard: Settings → API Keys → New key → Sandbox (test mode) - 2
Store the key as an environment variable
Never hardcode a key in source you might commit. Put it in a .env file (or your platform’s secret manager) and load it at runtime.
// .env ASKBIZ_API_KEY=abz_test_xxxxxxxxxxxxxxxx // sandbox key — no real debit - 3
Write the full scan function
This one function does the whole job: read a JPEG, base64-encode it, send it to POST /api/v1/scan with an Idempotency-Key so an accidental retry can’t double-run the vision call, and surface a clear error on a non-2xx response.
import { readFile } from 'node:fs/promises' import { randomUUID } from 'node:crypto' async function scanProduct(photoPath) { const bytes = await readFile(photoPath) const base64Jpeg = bytes.toString('base64') // no data URI prefix const res = await fetch('https://askbiz.co/api/v1/scan', { method: 'POST', headers: { 'x-api-key': process.env.ASKBIZ_API_KEY, 'Content-Type': 'application/json', 'Idempotency-Key': randomUUID(), }, body: JSON.stringify({ image: base64Jpeg }), }) if (!res.ok) { const err = await res.json() throw new Error(`Scan failed (${res.status}): ${err.error}`) } return res.json() } const result = await scanProduct('./product-photo.jpg') if (result.found) { console.log(`Matched: ${result.name} — ${result.price} (${result.stock_qty} in stock)`) } else { console.log(`No catalog match. Vision model saw: "${result.name}" — prompting manual entry.`) } - 4
Handle both outcomes, not just the happy path
A sandbox (and a real) scan can come back found: true with a real price and stock level, or found: false with just an identified name and no catalog entry. Both are billed, successful responses — found: false is not an error. Your integration needs a real fallback path (a manual price-entry screen pre-filled with the identified name), not just a console.log.
- 5
Switch to a live key when you’re ready
Create a second key in live mode from the same Settings screen, swap the environment variable, and you’re calling the real vision pipeline against real inventory. Nothing else in your code changes — that’s the entire point of the sandbox/live split.
What’s next
Once this works against a sandbox key, the natural next step is scoping it to a real merchant’s inventory instead of your own — covered in Connect to a merchant. Before you point real traffic at it, read the production readiness checklist.
Questions about this lesson
Do I need a real product photo to follow along?+
Any JPEG of a packaged product works for testing the shape of the response. In sandbox mode the result is simulated, so the specific photo content matters less than getting the base64 encoding and request format right.
Why does the example strip the data URI prefix?+
The image field expects raw base64 only. If you capture the photo from a browser file input or canvas, the resulting data URL starts with "data:image/jpeg;base64," — that prefix must be removed before sending, or the request will fail to decode server-side.
What happens if I forget the Idempotency-Key header?+
The call still succeeds — the header is optional but strongly recommended. Without it, a network-level retry on your end becomes a brand-new request, which on a live key means a second real charge and a second vision call.