multipart/form-data | Bearer token | optional header X-Enforce-Paywall: truefile - palm/hand photo (JPEG/PNG/WebP, max 5 MB)200 - image accepted and analysis started. Returns a confirmation message. Then poll GET /palm/user. Also returns 200 with success: false and code: PLAN_LIMIT (not an HTTP error) when X-Enforce-Paywall: true is sent and no unconsumed palm-reading purchase exists (get one via POST /subscriptions/checkout with slug palm-read first - checkout never generates the reading itself, this endpoint is always the separate step-2 call, paid or free-via-coupon). If a purchase is still pending (paid moments ago, webhook not yet processed), this call reconciles it against Razorpay live before deciding - if confirmed paid the credit activates and generation proceeds normally; if not, the message is "We're still verifying your payment..." instead of the plain "this report needs to be purchased" text shown when no attempt exists at allfailed reading does NOT block a new upload (only pending/processing/done do), and the SAME already-paid credit from the first attempt is reused automatically - a paid credit only counts as spent once a reading actually reaches done. This applies whether the first attempt failed at the MediaPipe check (rejected before any credit was touched), or failed later during AI generation. No special client-side handling needed: it is always safe to call POST /subscriptions/checkout with slug palm-read again before retrying the upload - it will report requires_payment: false and hand back the same purchase instead of charging twice.401 - invalid/expired token409 - a palm analysis already exists for the user (pending/processing/done); re-upload is blocked only while one of those is in flight or complete422 - no hand detected by MediaPipe, unsupported format, or file > 5 MB