Claim Guard

Quality check for skin analysis claims

Skin score badges, checked before the app shows them

Claim Guard sits between YouCam skin analysis and the badge a beauty app shows. A before/after change gets a badge only when it is larger than retake noise, both photos were taken in comparable light, and the product can cause that kind of change. Otherwise the badge is blocked and the reason is logged.

Same face, same room, no skincare in between. Four ceiling-light shots and four desk-lamp shots, taken minutes apart with the phone re-positioned: the average moisture score is 70.8 under the ceiling light and 75.3 under the lamp (+6.4%). The pair in the card is one of those pairs: moisture +7%.

Same room: ceiling light to desk lamp

Demo data: developer's own photos

Moisture, change in raw score for this pair of photos

−15+15noise ±7.1+12.4 raw
−15+15noise ±7.1+12.4 raw

The shaded band is what retaking the same photo produces. This change lands outside it, so a noise check alone would show the badge.

Before: IMG_1437 · ceiling light onlyAfter: IMG_1432 · desk lamp, same room, phone re-positioned

For this pair the app would show

Moisture +7%

Claim Guard

Blocked: photos not comparable

Cases

Real YouCam SD skin analysis results from the developer's photos: one face, one phone. All four YouCam scores are higher-is-better, so a higher wrinkle score means fewer wrinkles.

Demo data: developer's own photos

Same face, same room, no skincare: in a ceiling-light shot and a desk-lamp shot taken minutes apart (the phone was re-positioned), moisture and wrinkle score higher under the lamp.

Details
  • Photos IMG_1437 (ceiling light only) and IMG_1432 (desk lamp, same room, phone re-positioned), taken on the same evening.
  • In the lamp photos the phone sits lower and tilts up, so the ceiling is in frame and the face is smaller. This is not a lamp-only change.
  • The desk-lamp photos were taken a few minutes first (IMG_1431 to 1434, then IMG_1436 to 1439). The case orders them ceiling to lamp, the direction that would show a gain.
  • Each held-out case uses the shot closest to its group's average scores, chosen on scores only.
Before photo: IMG_1437 · ceiling light only
Before IMG_1437 · ceiling light only
After photo: IMG_1432 · desk lamp, same room, phone re-positioned
After IMG_1432 · desk lamp, same room, phone re-positioned

The app would show

Moisture +7%Less visible pores (pore score) +1%Smoother skin (wrinkle score) +12%Radiance −1%

Claim Guard

Badge blockedThe capture check failed, so the two photos are not comparable.

Capture conditions

  • Face brightness0.57 → 0.44Relative to the background, the face is 23.2% darker in the after photo (limit 10.0%), so the light on the face changed.
  • Backlight0.0% / 5.3% blown out
  • Light colour1.061 → 1.139The light is warmer in the after photo: background red/blue ratio 1.061 to 1.139 (7.4%, limit 2.0%).

Per metric

Moisturehigher score = more hydratedscore 70 → 75+7%Blocked: photos not comparable
−15+15noise ±7.1+12.4 raw

The two photos were taken under different conditions, so the scores can't be compared. A noise check alone would have allowed this: raw moved +12.4, past the ±7.1 floor.

Porehigher score = less visible poresscore 71 → 72+1%Blocked: photos not comparable
−20+20noise ±8.9+3.1 raw

The two photos were taken under different conditions, so the scores can't be compared. Raw moved +3.1, inside the ±8.9 floor anyway.

Wrinklehigher score = fewer wrinklesscore 86 → 96+12%Blocked: photos not comparable
−8+8noise ±1.5+5.2 raw

The two photos were taken under different conditions, so the scores can't be compared. A noise check alone would have allowed this: raw moved +5.2, past the ±1.5 floor.

Radiancehigher score = more radiantscore 81 → 80−1%Blocked: photos not comparable
−8+8noise ±3.8−2.4 raw

The two photos were taken under different conditions, so the scores can't be compared. Raw moved −2.4, inside the ±3.8 floor anyway.

How it decides

Five checks run in order, and the first one that applies decides what happens to each badge.

  1. Same file. Two identical files mean nothing changed, so every metric is within noise.
  2. Claim type. When the brand names the product category, a metric that category can't change is blocked. Foundation can't claim moisture.
  3. Capture conditions. If the light differs between the photos, every metric is blocked.
  4. Noise floor. The change must be bigger than what retaking the same photo produces.
  5. Display change. The badge shows the display score, so that score must go up by at least 1 point.

On the developer's first 15 photos, all 168 pairs with a changed setup were blocked and all 42 same-light retake pairs passed; on 12 photos from a later evening, 64 of 64 changed-light pairs were blocked and 26 of 32 same-lamp pairs before and after a moisturizer passed (re-run on photos that informed the change, not held out).

Method and limitsNoise floor, capture limits, claim policy, the later-evening check and what these numbers don't show

Noise floor

Uses raw_score, because ui_score is a compressed display scale. SD within 5 sets of retakes, each at one spot under one light with nothing changed, pooled across the sets (22 photos). Each set keeps its own average, so a light change between sets never counts as noise. MDC95 = 1.96 × √2 × SD: a change beyond it happens by chance under 5% of the time.

MetricPooled SDMDC95 (used)Session 1 only
Moisture2.56±7.10±5.14
Pore3.22±8.93±2.87
Wrinkle0.55±1.51±0.17
Radiance1.38±3.83±3.38

Sets: Session 1, desk spot (6), Session 3, ceiling light (other room) (4), Session 4A, ceiling light (4), Session 4B, desk lamp (4), Session 4C, desk lamp, after moisturizer (4). The window session is left out because every photo in it is backlit. Sending the same file twice returned identical scores, so the API adds no jitter of its own.

Capture conditions

Measured on a fixed central face box and the background strips beside the head. Face brightness is divided by background brightness, so a uniform exposure change cancels out, and light colour is read from the background, which skincare does not change. Limits sit between what same-spot retakes and a different room and light produced on the first 15 photos.

CheckLimitSame spot and light, largestDifferent room and light, range
Face brightnessFace brightness divided by the background beside the head, so exposure changes cancel out±10.0%5.7%8.4% to 98.1%
BacklightShare of blown-out pixels in the background beside the head15.0% max0.2%0.0% to 64.0%56.1% to 64.0% when one photo has the window behind the head
Light colourRed-to-blue ratio of the background beside the head (skincare does not change it)±2.0%1.0%0.5% to 5.7%

No single check catches every change. Of the 210 ordered photo pairs, the 42 taken at the same spot under the same light (30 desk spot, 12 ceiling light) all pass, and the 168 that cross setups or include a backlit window photo are all blocked. These counts are in-sample. Run npm run calibrate for the full printout.

Claim type policy

Which metrics each product category may make claims about. A configurable policy a brand's compliance team would edit, not a regulation. “Appearance” means the badge carries the label “appearance with product on”.

CategoryMoisturePoreWrinkleRadiance
moisturizerSkinNo claimNo claimSkin
serumSkinSkinSkinSkin
foundationmakeupNo claimAppearanceNo claimAppearance
cleanserNo claimSkinNo claimNo claim
sunscreenNo claimNo claimNo claimNo claim

Later-evening check, before and after a design change

Twelve more photos of the same face from a different evening, four shots each under three setups:

  • A. Ceiling light only. Phone held closer: the face is larger in frame and no ceiling is visible.
  • B. Same room, desk lamp instead of the ceiling light. Phone re-positioned lower and tilted up: the ceiling is in frame and the face is smaller.
  • C. Same desk lamp as B, similar framing (a little less ceiling in frame), about 20 minutes after washing the face and applying a moisturizer.
Pairs (ordered)ExpectedVersion 1, limits frozenVersion 2, current
Same setup (A-A, B-B, C-C)Pass32/36 pass (88.9%)fails: face brightness 432/36 pass (88.9%)fails: face brightness 4
Different light (A-B, A-C)Blocked64/64 blocked (100.0%)64/64 blocked (100.0%)
Same lamp, before and after moisturizer (B-C)Pass10/32 pass (31.3%)fails: light colour 15, face brightness 726/32 pass (81.3%)fails: face brightness 6

Version 1 was frozen on the first 15 photos and never saw these twelve, so its column is a true held-out result. It read light colour from the face and face brightness on its own. After washing and moisturizer the face measured redder, which it read as warmer light, and it blocked most same-lamp pairs.

Version 2 reads light colour from the background and divides face brightness by background brightness. These twelve photos showed us why version 1 failed, so they informed the change and its column is not a strict held-out result. The limit values (10%, 15%, 2%) were checked on the first 15 photos only and not tuned to make any case pass. The remaining failures are real: in set B the face got brighter from shot to shot relative to the background under the same lamp.

The same photos made the noise floor wider. Under the session 1 floor, 6 of 18 same-setup pairs moved moisture past it and 10 of 18 moved wrinkle past it, where about 1 in 20 is expected. With the pooled floor, 3, 3, 3 and 0 of 18 still do, for moisture, pore, wrinkle and radiance. That count is in-sample, and the floor may still be too tight.

What these numbers don't show

Everything here comes from one face and one phone. The face box is fixed: an offline face detector found one face in each of the 17 demo images and the box sat inside it left to right, but its top edge reaches the forehead and, in the window photos, the hairline. We kept the fixed box because the detector would add about 300 MB of dependencies and change every stored measurement. A brand would recalibrate with retakes from its own users and devices before relying on these limits.

Try your own photos

The capture check runs first and costs nothing. Skin analysis runs only after you agree, and it uses real YouCam credits, so this demo has a daily cap.

JPG or PNG, up to 4 MB per photo. Larger photos are downscaled in your browser before upload.

Free. Runs on this server; nothing goes to YouCam.

Audit log

Every claim check with its time, product, per-metric verdict, reason and the limits used, for a compliance team to review or export. The demo cases are listed; sample and live checks you run here are added on top. Rows stay in this browser tab.

7 checks in this session. Thresholds: capture-v2: face brightness ±10%, backlight 15% max, light colour ±2%; raw noise floor moisture ±7.10, pore ±8.93, wrinkle ±1.51, radiance ±3.83; display change 1 point

Time (UTC)Case or requestProductConditionsPer metric verdict and reason
2026-10-13same-room-lampdemo caseNot givenDifferRelative to the background, the face is 23.2% darker in the after photo (limit 10.0%), so the light on the face changed. The light is warmer in the after photo: background red/blue ratio 1.061 to 1.139 (7.4%, limit 2.0%).
  • Moisture: Blocked: conditions The two photos were taken under different conditions, so the scores can't be compared.
  • Pore: Blocked: conditions The two photos were taken under different conditions, so the scores can't be compared.
  • Wrinkle: Blocked: conditions The two photos were taken under different conditions, so the scores can't be compared.
  • Radiance: Blocked: conditions The two photos were taken under different conditions, so the scores can't be compared.
2026-10-13same-lightdemo caseNot givenMatch
  • Moisture: Within noise Raw score moved +4.8. Same-light retakes stay within about ±7.1 (95% band), so this can be noise.
  • Pore: Within noise Raw score moved +0.5. Same-light retakes stay within about ±8.9 (95% band), so this can be noise.
  • Wrinkle: Within noise Raw score moved +0.1. Same-light retakes stay within about ±1.5 (95% band), so this can be noise.
  • Radiance: Within noise Raw score moved +2.0. Same-light retakes stay within about ±3.8 (95% band), so this can be noise.
2026-10-13room-windowdemo caseNot givenDifferRelative to the background, the face is 48.4% darker in the after photo (limit 10.0%), so the light on the face changed. The after photo is backlit: 56.1% of the background beside the head is blown out (limit 15.0%).
  • Moisture: Blocked: conditions The two photos were taken under different conditions, so the scores can't be compared.
  • Pore: Blocked: conditions The two photos were taken under different conditions, so the scores can't be compared.
  • Wrinkle: Blocked: conditions The two photos were taken under different conditions, so the scores can't be compared.
  • Radiance: Blocked: conditions The two photos were taken under different conditions, so the scores can't be compared.
2026-10-13room-deskdemo caseNot givenDifferRelative to the background, the face is 38.8% darker in the after photo (limit 10.0%), so the light on the face changed. The light is warmer in the after photo: background red/blue ratio 1.099 to 1.160 (5.6%, limit 2.0%).
  • Moisture: Blocked: conditions The two photos were taken under different conditions, so the scores can't be compared.
  • Pore: Blocked: conditions The two photos were taken under different conditions, so the scores can't be compared.
  • Wrinkle: Blocked: conditions The two photos were taken under different conditions, so the scores can't be compared.
  • Radiance: Blocked: conditions The two photos were taken under different conditions, so the scores can't be compared.
2026-10-13same-filedemo caseNot givenMatch
  • Moisture: Within noise Both photos are the same file, so nothing changed.
  • Pore: Within noise Both photos are the same file, so nothing changed.
  • Wrinkle: Within noise Both photos are the same file, so nothing changed.
  • Radiance: Within noise Both photos are the same file, so nothing changed.
2026-10-13moisturizerdemo caseMoisturizerMatch
  • Moisture: Within noise Raw score moved +1.0. Same-light retakes stay within about ±7.1 (95% band), so this can be noise.
  • Pore: Blocked: claim type Moisturizer is not a product that changes pore. A pore claim needs a product such as a serum or cleanser.
  • Wrinkle: Blocked: claim type Moisturizer is not a product that changes wrinkle. A wrinkle claim needs a product such as a serum.
  • Radiance: Claimable Raw score moved −4.9, more than the ±3.8 that same-light retakes produce. This is a real drop, not a gain, so there is no improvement badge.
2026-10-13real-changedemo caseFoundationMatch
  • Moisture: Blocked: claim type Foundation is makeup. A moisture claim needs a product such as a moisturizer or serum.
  • Pore: Within noise Raw score moved −2.8. Same-light retakes stay within about ±8.9 (95% band), so this can be noise.
  • Wrinkle: Blocked: claim type Foundation is makeup. A wrinkle claim needs a product such as a serum.
  • Radiance: ClaimableRadiance +11% Raw score moved +11.5, more than the ±3.8 that same-light retakes produce. This is an appearance claim: it describes the skin with the product on.

Why brands need this

A before/after skin score in a brand's app is a claim about its product, and regulators ask for evidence behind it. Claim Guard logs a reason for every badge it shows or blocks.

What a check costs against what a claim can cost

  • Assumed price: NT$1 per check. This is our assumption for illustration, not a quote and not a Perfect Corp price.
  • Taiwan: a false or exaggerated cosmetic claim costs NT$40,000 to 200,000 (Cosmetic Hygiene and Safety Act, Article 20), and the fine can repeat for each further run of the ad. The minimum fine pays for 40,000 checks at the assumed price.
  • UK: since April 2025 the Competition and Markets Authority can fine a business up to 10% of global turnover for breaking consumer law (gov.uk, 7 April 2025).
  • Scale: if each of Perfect Corp's 859 brand clients ran 1,000 checks a month at the assumed price, that is NT$859,000 a month of add-on revenue on top of skin analysis. The volume and the price are our assumptions; only the client count is reported.

EU

Commission Regulation (EU) No 655/2013

Common criteria for cosmetic claims, adopted under Article 20 of Regulation (EC) No 1223/2009. Annex, points 3 and 4.

Claims must be supported by adequate and verifiable evidence, from methods that are valid, reliable and reproducible. Performance must not be presented beyond the available evidence.

Taiwan

Cosmetic Hygiene and Safety Act

Article 10, with penalties in Article 20.

Labels, promotion and advertising must not be false or exaggerated. Breaking Article 10(1) carries a fine of NT$40,000 to 200,000, and the fine can be imposed again for each further run of the ad.

Claim Guard does not make a claim compliant. It catches common ways a before/after number misleads: photos taken under different light, retake noise read as a result, and a product claiming a change it can't cause. We found no public enforcement case about a skin-analysis app itself; the cases above are about skincare ads.

For brands

Your app already gets scores from YouCam. Before showing a badge, send those scores, the two photos and the product category to Claim Guard. It makes no YouCam call of its own, so the check costs no credits.

Made-up brand app. Photos and scores are from the demo cases above.

  1. Run YouCam skin analysis on the before and after photos, as you do now.
  2. POST /api/v1/claim-check with both sets of scores, both photos and product_category.
  3. Show a badge only where badge is not null, with badge_label next to it when present. Otherwise follow the verdict below, and log reason for your review team.
VerdictMeaningSuggested app copy
claimableRaw change past the noise floor, display score moved, same light, product may claim it. A drop is real but gets no badge.Show the badge, e.g. “Radiance +11%”, plus the appearance label for makeup.
blocked-claim-typeThe product category can't make this claim.No badge for this metric.
blocked-conditionsThe two photos were taken under different light.“Retake in the same light to see your progress.”
within-noiseThe change is no bigger than retaking the photo.“No clear change yet. Check again next week.”
no-visible-changeRaw moved, but the display score did not.No badge for this metric.

Request

POST /api/v1/claim-check
Content-Type: application/json

{
  "product_category": "foundation",
  "before": {
    "scores": [
      { "type": "moisture", "ui_score": 76, "raw_score": 66.841 },
      { "type": "pore", "ui_score": 73, "raw_score": 61.626 },
      { "type": "wrinkle", "ui_score": 97, "raw_score": 98.73 },
      { "type": "radiance", "ui_score": 82, "raw_score": 83.086 }
    ],
    "image_base64": "<JPEG as base64>"
  },
  "after": {
    "scores": [
      { "type": "moisture", "ui_score": 81, "raw_score": 80.561 },
      { "type": "pore", "ui_score": 71, "raw_score": 58.801 },
      { "type": "wrinkle", "ui_score": 97, "raw_score": 99.092 },
      { "type": "radiance", "ui_score": 91, "raw_score": 94.54 }
    ],
    "image_base64": "<JPEG as base64>"
  }
}

Response (the virtual foundation case)

{
  "badge_allowed": true,
  "product_category": "foundation",
  "conditions": {
    "match": true,
    "reasons": []
  },
  "metrics": [
    {
      "metric": "moisture",
      "verdict": "blocked-claim-type",
      "display_change_pct": 6.58,
      "raw_delta": 13.72,
      "mdc95": 7.104,
      "badge": null,
      "claim_kind": null,
      "reason": "Foundation is makeup. A moisture claim needs a product such as a moisturizer or serum."
    },
    {
      "metric": "pore",
      "verdict": "within-noise",
      "display_change_pct": -2.74,
      "raw_delta": -2.825,
      "mdc95": 8.925,
      "badge": null,
      "claim_kind": "appearance",
      "reason": "Raw score moved −2.8. Same-light retakes stay within about ±8.9 (95% band), so this can be noise."
    },
    {
      "metric": "wrinkle",
      "verdict": "blocked-claim-type",
      "display_change_pct": 0,
      "raw_delta": 0.362,
      "mdc95": 1.511,
      "badge": null,
      "claim_kind": null,
      "reason": "Foundation is makeup. A wrinkle claim needs a product such as a serum."
    },
    {
      "metric": "radiance",
      "verdict": "claimable",
      "display_change_pct": 10.98,
      "raw_delta": 11.454,
      "mdc95": 3.829,
      "badge": "Radiance +11%",
      "claim_kind": "appearance",
      "badge_label": "Appearance with product on",
      "reason": "Raw score moved +11.5, more than the ±3.8 that same-light retakes produce. This is an appearance claim: it describes the skin with the product on."
    }
  ]
}

Accepts YouCam's output entries as returned, or a { "moisture": { "ui": 74, "raw": 59.9 } } map. product_category is optional; without it the claim-type check is skipped. Photos up to 4 MB each, JPEG or PNG; 30 requests per minute per address.

In your backend: examples/claim-check.ts

import { claimCheck } from "./claim-check"; // examples/claim-check.ts

const r = await claimCheck({
  baseUrl: "https://your-claim-guard-host",
  before: { scores: youcamBefore.output, photo: beforeJpeg },
  after: { scores: youcamAfter.output, photo: afterJpeg },
  productCategory: "moisturizer",
});
for (const m of r.metrics) {
  if (m.badge) showBadge(m.badge, m.badge_label);
  else logForReview(m.metric, m.verdict, m.reason);
}

In Claude Desktop, next to YouCam's MCP server

{
  "mcpServers": {
    "youcam-beauty": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "https://mcp-api-01.makeupar.com/mcp/beauty",
               "--header", "Authorization:${AUTH_HEADER}"],
      "env": { "AUTH_HEADER": "Bearer YOUR_API_KEY" }
    },
    "claim-guard": {
      "command": "node",
      "args": ["C:/path/to/youcam-claim-guard/node_modules/tsx/dist/cli.mjs",
               "C:/path/to/youcam-claim-guard/mcp/server.ts"]
    }
  }
}

npm run mcp starts a local MCP server with two tools: claim_check (scores plus two photo paths or base64, and an optional product category) and explain_thresholds. An assistant can run YouCam's skin analysis through YouCam's own MCP server, then check the claim with Claim Guard before writing a badge.