Analyze skin images

Upload and analyze skin images using AI to provide skin assessment.

Recent Requests
Log in to see full request history
TimeStatusUser Agent
Retrieving recent requests…
LoadingLoading…
Body Params
images
array of objects
required
length between 1 and 6

Skin images to analyze. A face scan requires exactly 3 (front, left, right). A body scan takes per-region images up to its part's own cap: hands, feet and uncategorized 6 (e.g. feet: top + sole per side), back and shoulders 3, chest 2. Each image is its own model call, which is what the caps bound. For chest_decolletage and chest_full, a bra, sports bra or top may stay on. Exposed chest skin above or around the garment is sufficient when it is detailed and well lit enough to assess; breast or nipple exposure is never required. Clothing elsewhere in the frame does not make the capture mostly_covered. Photos with too little exposed chest skin, excessive blur, darkness or distance are still rejected.

images*
file
required

Skin image file (jpeg, png, jpg, webp, max 2MB)

string

Optional unique identifier for the image

string

Optional label for the image

string
enum

Body scans only. The sub-region the user chose to capture in this image (guided capture, e.g. palm vs back of hand, sole vs top of foot, upper vs mid back). Must be a sub-region of body_part; omit for face scans and for free body capture (the analyzer then infers the region). The user's declared value is authoritative over the inferred one.

string
enum

The skin part being analyzed (defaults to 'face'). Each body part is a separate scan with its own batch, its own 0–100 score and its own routine section — back, chest and shoulders are three scans, not one trunk scan, so a finding on one never moves another's score. A photo showing another area is assessed as that area; see the 422 description. uncategorized is Other area: one body_area close-up of a patch the customer chose, assessed as the part the photo check names when it can name one.

Allowed:
uuid | null

For a body add-on (any non-face body_part): the face scan's returned batch_id. Links the add-on to its face scan so face + body read back as ONE scan — one completion email/notification and one merged result (fetching the face batch_id returns both skin_concerns and body_concerns, and a body add-on id resolves to the face root). For a mobile combined scan, release waits until every selected area succeeds and the client finalizes the family. Omit for a face/primary scan.

expected_body_parts
array of strings, unique
length ≤ 6

Mobile face scans only. Selected body areas that must complete before the combined result, completion notification, and routine are released. Billing is two skin scan credits total: one for face and one for all selected body areas together. Additional areas do not add credits or wallet charges. If credits are insufficient, the wallet covers only the uncovered services at their separately configured face/body rates. Credits cover face first; with one credit available, only body is payable. When the same person already has a face scan today, the face part reuses that analysis under a new batch_id, exactly as a plain face scan would; the response is still the pending batch_id and billing is unchanged. After every area succeeds, call POST /skin/analysis/{batch_id}/finalize.

number
≥ 0

Mobile combined scans only. Maximum wallet amount the customer accepted on the aggregate paywall returned by this endpoint. A smaller authorization is rejected before analysis. No credit or wallet debit occurs until the family is finalized.

string
enum

The channel through which the analysis was performed (defaults to 'mobile-app' if not provided)

Allowed:
uuid

Optional scan session ID from a QR code flow. When provided, the analysis results will be linked to this session and real-time status updates will be broadcast via WebSocket.

string | null

Optional email for guest/unauthenticated scans

string | null

Optional customer first name. When provided, used instead of guessing from email address.

string | null

Optional customer last name. When provided, used instead of guessing from email address.

string | null
enum

Context in which the scan is being performed. clinical_trial routes the
scan through the dedicated clinical-trial analyzer (grades extra clinical
biomarkers); sent by the clinical-trial participation link. in_store is
a staff-operated walk-in scan run from the vendor dashboard; the customer
resolves to a vendor-customer identity like widget_only.

Allowed:
string | null

Reference of a successful scan PaymentTransaction (purpose=scan), required when the
vendor's scan_payment_bearer is customer AND the request originates from the
embedded scan widget without a batch_id (mobile-capture continuation) and without
scan_context=appointment_booking. See POST /scan-payments/initialize.

Single-use per bundle: the payment covers one analysis AND the product
recommendations generated from it. It is spent once those recommendations
are delivered (POST /skin/care-routine), after which requests carrying the
reference return 402. Until then it stays valid, so a customer whose
recommendations failed can resume without paying twice.

Resuming returns the analysis already paid for. If this reference has
already produced an analysis, that analysis is returned as-is and no new one
is run — submitted images are ignored, and the response is the same shape as
GET /skin/analysis/{batch_id}. Proceed straight to POST /skin/care-routine
with the returned batch_id. Resuming requires email to match the email on
the original payment; a mismatch is treated as a new scan rather than
disclosing the payer's results.

uuid | null

UUID the widget generated at scan start and sent to
POST /scan-payments/initialize. Used solely to prove this request belongs to
the referenced payment — it is matched against the transaction's
skin_image_analysis_batch_id and a mismatch is a 422.

It is NOT the id of the resulting analysis: a new scan is created under its
own server-generated batch_id, returned in the response. Always read the
returned batch_id and use that for POST /skin/care-routine.

string | null

A vendor scan coupon, in place of payment_reference. Requires email
and client_batch_id. Only consulted when the customer would otherwise
pay, so a scan the vendor already covers never spends a use. The scan is
billed to the vendor. A refused coupon is a 402 whose code is one of
scan_coupon_invalid, scan_coupon_exhausted, scan_coupon_not_eligible
or scan_coupon_unavailable; the customer can still pay instead. A body
add-on whose covered_by_batch_id names a coupon-covered face scan is
covered by the same coupon.

uuid | null

Body add-ons only (any body_part other than face). The client_batch_id
of the paid face scan this add-on belongs to. For a customer-bears-cost vendor
the add-on then rides on that payment instead of needing its own, so one face
payment covers every body zone captured in the same sitting.

Coverage expires 24h after the face payment (configurable via
FEATURE_SKIN_ANALYSIS_BODY_ADDON_COVERAGE_HOURS). Body captures follow the
face scan within the same sitting, so this only bounds replay; an expired
add-on isn't rejected outright, it just has to pay its own way (402).

string | null

The customer's phone number, collected by the widget's pre-scan form when the
vendor enabled phone collection (collectPhoneNumber on GET /widget/config,
set under categories.collection.collect_phone_number in scan settings). The
widget makes the field required when that toggle is on; this endpoint simply
records what it was given.

Stored per-vendor, against the vendor-customer, for the same reason consent is:
a number given to one brand's widget must not surface in another brand's
customer list. The shared users.phone identity is filled in as well, but only
when it is empty — the newest scan never overwrites a number the customer set
themselves elsewhere.

Formatting is stripped on the way in (+20 10 1234 5678 stores as
+201012345678); a leading + is kept. Deliberately not format-validated —
numbering plans vary by country and the widget is embedded worldwide. Optional
everywhere: omitting it (or sending blank) means "not collected on this
request" and leaves any existing number untouched rather than clearing it.

string | null

The pre-scan quiz as a JSON string (this request is multipart): the same object
sent as quiz_answers on POST /skin/care-routine. Vendor scans start building
their routine as soon as the scan is saved, usually before that POST arrives, so
send the quiz here too or the routine is built without the customer's allergies
and budget. Optional; an unparseable value is ignored and never fails the scan.

Headers
string
^[a-zA-Z0-9._-]{8,128}$

Optional AppsFlyer install UID from the mobile SDK. Used only with X-CHANNEL mobile-app and an allowed X-AppsFlyer-App-ID. Invalid or absent attribution is ignored and never changes the business response. For payments this identity is frozen at creation for later confirmed-payment events.

string

AppsFlyer application identifier configured for this API environment. Numeric iOS App Store IDs require the id prefix; Android uses its package name. This header grants no authentication or payment authority.

string
^[0-9.]{1,30}$

OS release version from expo-device, not the Android API level.

Responses

Language
Credentials
Bearer
JWT
LoadingLoading…
Response
Click Try It! to start a request and see the response here! Or choose an example:
application/json