Ring virtual try-on has a reputation among developers for being the hard one. Makeup sits on a face that a detector can find reliably. Clothes drape over a body with a well understood silhouette. A ring has to land on one specific finger, at one specific point along that finger, oriented correctly, lit to match the hand it is sitting on, and partially hidden where the finger curls in front of it. Get any of those wrong and the result does not look slightly off, it looks fake.
The useful news is that none of that has to be your problem. The YouCam AR Ring Virtual Try-On API takes a flat product photograph of a ring and a photograph of a hand and returns a composited image, with finger selection, placement, shadow and ambient light exposed as parameters you control. No 3D model, no asset pipeline, no rigging. This walkthrough builds a working try-on app against it, driven from Claude Code through the YouCam MCP server, and spends most of its time on the parameters that decide whether the output is convincing.
What You Are Building
A web app where a user uploads a photograph of their hand, picks a ring from a catalogue, and sees it rendered on the finger of their choice with adjustable placement. The pieces:
| Layer | What it does | What provides it |
|---|---|---|
| Catalogue | Ring product images, three-quarter front view | Your own data. Flat 2D photographs, no 3D assets |
| Upload | Gets the hand image and ring image into the service | Two step file API, or a public image URL |
| Composite | Places, orients, lights and occludes the ring | The ring try-on task endpoint |
| Controls | Finger, position along the finger, shadow, ambient light | Task parameters, exposed in your UI |
| Delivery | Returns a rendered image to the browser | Task polling, then a signed result URL |
Claude Code writes almost all of the glue. What it cannot do is composite the ring, which is the point worth internalising before starting: a language model can build the entire application around an image operation it has no ability to perform.
What You Need Before You Start
| Requirement | Detail |
|---|---|
| API key | Free from the YouCam API Console, with free units for testing |
| Ring images | Three-quarter front view, roughly 45 degrees. JPG, JPEG or PNG |
| Hand images | Back of the hand fully visible, all five fingers clear, nothing occluding them |
| Image limits | Long side 4096px or less, under 10MB per file |
| Runtime | Anything that can make HTTPS requests. Examples exist for Node, Python, PHP, Java, Flutter and cURL |
| Claude Code | Optional but assumed here, with the YouCam MCP server connected |
The two image requirements are not suggestions. A ring photographed straight on has no visible band curvature for the engine to align, and a hand with fingers overlapping gives it nothing to place against. More integration time is lost to source imagery than to code.
Connecting Claude Code to the YouCam MCP Server
The MCP server lets Claude call YouCam features directly while it is writing your app, which means you can test a composite before any of your own code exists. Fashion and accessory features live on the fashion server.
claude mcp add youcam-fashion \
-- npx -y mcp-remote https://mcp-api-01.makeupar.com/mcp/fashion \
--header "Authorization: Bearer YOUR_API_KEY"With that connected, you can hand Claude a ring image and a hand image and ask for a try-on directly in the conversation. The value is not the shortcut, it is that you see a real output before committing to an interface, and you find out immediately whether your product photography is usable. If you prefer working outside an editor entirely, the YouCam CLI reaches the same endpoints from a terminal, and YouCam API Agent Skills cover the agent side.
The Integration Flow
Three calls, against base URL https://yce-api-01.makeupar.com, with a bearer token on every request.
1. Register and upload the files
File upload is two steps rather than a multipart post. You declare the files first and receive a signed destination for each.
POST /s2s/v2.0/file/2d-vto/ring
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
{
"files": [
{ "content_type": "image/jpeg", "file_name": "hand.jpg", "file_size": 842134 },
{ "content_type": "image/png", "file_name": "ring.png", "file_size": 211904 }
]
}The response returns a file_id per file plus the upload request details, including the URL, method and headers to use. Upload each file to its own destination, then carry the file_id values into the task. If your catalogue images are already on a public CDN you can skip this entirely and pass URLs instead, which is usually the right call for the ring side and the wrong call for the hand side, since user uploads are rarely public.
2. Create the try-on task
POST /s2s/v2.0/task/2d-vto/ring
Authorization: Bearer YOUR_API_KEY
{
"source_info": {
"src_file_id": "HAND_FILE_ID"
},
"object_infos": [
{
"name": "ring_1",
"ref_file_id": "RING_FILE_ID",
"parameter": {
"ring_wearing_finger": 3,
"ring_wearing_location": 0.45,
"ring_shadow_intensity": 0.15,
"ring_ambient_light_intensity": 1.0,
"ring_need_remove_background": true
}
}
]
}Source and reference inputs accept file ids or URLs and the two can be mixed, so a CDN hosted ring and an uploaded hand in the same request is a normal shape.
3. Poll for the result
GET /s2s/v2.0/task/2d-vto/ring/{task_id}
Authorization: Bearer YOUR_API_KEYPoll roughly every ten seconds until task_status is success or error. On success the response carries a result URL. Webhooks are the better pattern where your stack supports them, because polling a composite that a user is waiting on is exactly the case where webhooks earn their complexity.
| Lifetime | Value | What it means for your app |
|---|---|---|
| task_id | 24 hours | You can re-poll a task long after the request, but do not treat it as storage |
| Result URL | 2 hours | Download and persist it yourself if the user needs it again. Do not store the signed URL |
That second row catches people. A signed URL saved into a database works perfectly in testing and starts returning errors in production two hours later. Fetch the bytes, store them somewhere you own, and serve your own URL.
The Parameters That Decide Whether It Looks Real
This is the part worth slowing down for. The endpoint is simple. The difference between a convincing try-on and an obvious paste job lives entirely in these five values.
| Parameter | Range | Default | What it controls |
|---|---|---|---|
| ring_wearing_finger | 0 to 4 | Engine default | 0 thumb, 1 index, 2 middle, 3 ring, 4 little finger |
| ring_wearing_location | 0.0 to 1.0 | Engine default | Position along the finger, from the base knuckle toward the middle joint |
| ring_shadow_intensity | 0.0 to 1.0 | 0.15 | How strongly the ring casts a shadow onto the finger |
| ring_ambient_light_intensity | 0.0 to 1.0 | 1.0 | 0 ignores the hand's lighting, 1 fully matches it |
| ring_need_remove_background | Boolean | true | Automatic background removal on the ring image |
Expose the finger, not just the ring
Most ring try-on interfaces let the user pick a ring and nothing else. That is a mistake, and an easy one to avoid since the parameter already exists. People wear rings on different fingers for different reasons, and an engagement ring rendered onto an index finger is not the experience anyone came for. Make finger selection a visible control with a sensible default rather than a hard coded 3.
Position matters more than it sounds
ring_wearing_location runs from the base of the finger toward the first joint. A wedding band sits low. A statement cocktail ring usually sits higher. A stacking set needs each ring at a different value or they render on top of each other. If you are selling more than one category, store a default location per product rather than using one value for the catalogue.
Ambient light is the realism dial
At 1.0 the ring picks up the lighting and shadows of the hand it is sitting on, which is almost always what you want. Lower values make the ring look lit independently of the scene, which reads as a sticker. The reason to ever reduce it is a hand photograph with extreme or coloured lighting, where full matching tints the metal in a way that misrepresents the product. That is a merchandising decision and worth surfacing to whoever owns the catalogue, not burying in a config file.
Shadow is the one to leave alone
The 0.15 default is conservative and generally correct. Raising it looks dramatic in a single hero image and wrong across a varied catalogue of hand photographs, because the shadow no longer agrees with the light in the scene. Change it only with a specific image in front of you.
Two Advanced Controls Worth Knowing About
| Control | When you need it |
|---|---|
| Occlusion masks | Optional masks for the hand or the ring, supplied alongside the images, to fine tune segmentation. Reach for these when a specific product repeatedly renders with the wrong edge in front |
| ring_anchor_point | Two pixel coordinates marking the inner edge contact points of the ring. Useful for wide or unusually thick bands where automatic detection is slightly off. Omit it and the engine detects them |
Both are escape hatches rather than defaults. Ship without them, identify the handful of catalogue items that look wrong, and apply them per product. Setting anchor points for an entire catalogue up front is a large amount of work to solve a problem you may not have.
Stacking Multiple Rings
Stacking is one of the stronger commercial cases for this API, and it is handled by the shape of the request rather than by a separate endpoint. object_infos is an array, so you add an entry per ring, each with its own name, reference image and parameters. Add the hand once.
"object_infos": [
{ "name": "band", "ref_file_id": "A", "parameter": { "ring_wearing_finger": 3, "ring_wearing_location": 0.30 } },
{ "name": "solitaire", "ref_file_id": "B", "parameter": { "ring_wearing_finger": 3, "ring_wearing_location": 0.55 } },
{ "name": "midi", "ref_file_id": "C", "parameter": { "ring_wearing_finger": 1, "ring_wearing_location": 0.80 } }
]Give each one a distinct ring_wearing_location. Two rings at the same value on the same finger will fight for the same pixels, and the result looks exactly as broken as it sounds. A stacking builder that enforces minimum spacing between entries is a small amount of front end work that prevents most bad output.
What Claude Code Can and Cannot Do Here
Worth being precise about, because the split is instructive.
| Task | Claude Code |
|---|---|
| Upload flow, polling, retries, error handling | Writes it well. This is ordinary API plumbing |
| Catalogue UI, finger picker, stacking builder | Writes it well, and iterates on it quickly |
| Storing results, signed URL expiry handling | Writes it well once you tell it the two hour limit |
| Choosing sensible default locations per product type | Reasonable suggestions, but verify against real output |
| Compositing the ring onto the hand | Cannot do this at all. This is what the API is for |
| Judging whether the output looks convincing | Cannot do this reliably. A human has to look |
The last two rows are the ones that matter. An agent can build the entire product around an operation it cannot perform, which is a reasonable description of what most AI application development now looks like.
Testing Before You Ship
Four things, none of which take long and all of which are routinely skipped.
Test every finger. Hand geometry varies and the little finger is the one most likely to expose placement problems.
Test across skin tones. Segmentation and ambient light matching both behave differently across a range, and a catalogue that only ever rendered onto the development team's hands is not tested.
Test your worst product photograph. Not your best one. The straight on shot, the one on a reflective surface, the one with a shadow across the band. Those are the items that will generate support tickets.
Test expiry. Deliberately hold a result URL for over two hours and confirm your app handles it. This is the single most common production bug in polling based image APIs.
The ring try-on playground is the fastest way to run these checks before writing any code, and the API documentation carries the full field definitions.
Why Rings Are a Good First Try-On Build
They are commercially well suited to virtual try-on in a way that is not true of every category. Jewellery has a high average order value, a high return rate driven by uncertainty, and almost no ability to try before buying online. A ring is also a small, well defined object, which keeps the composite tractable.
If you are weighing which category to build first, the AI jewelry API overview covers the commercial case, and virtual ring try-on use cases covers where it tends to be deployed.
FAQ
Do I need 3D models of my rings?
No. Flat 2D product photographs are sufficient, which is the main practical advantage over traditional AR jewellery pipelines. A three-quarter front view at roughly 45 degrees is what the engine expects.
Which fingers are supported?
All five. ring_wearing_finger takes 0 through 4, mapping to thumb, index, middle, ring and little finger.
Can I place more than one ring?
Yes. object_infos is an array and each entry carries its own ring image and parameters. Give each one a different ring_wearing_location so they do not overlap.
What image sizes does it accept?
JPG, JPEG or PNG, long side 4096px or less, under 10MB per file. The hand needs all five fingers visible and unobscured.
How long are results available?
The task id stays valid for 24 hours and the result URL for 2 hours. Download and store the image yourself if the user needs it later.
Should I poll or use webhooks?
Webhooks where your stack supports them. Polling roughly every ten seconds is the documented fallback and is fine for a prototype, but a user staring at a spinner is the case webhooks exist for.
Do I have to use Claude Code or MCP?
No. It is a standard RESTful API with bearer authentication and examples in JavaScript, Java, Flutter, Python, PHP, Node and cURL. MCP is a convenience for building, not a dependency at runtime.
What usually goes wrong first?
Source imagery, by a wide margin. Rings photographed straight on, hands with overlapping fingers, and product shots with a background the removal step struggles with. Fix the images before debugging the integration.
Conclusion
The hard parts of ring try-on, placing a small object on a specific finger with correct orientation, lighting and occlusion, are the parts you do not write. What is left is an upload flow, a polling loop and a handful of parameters, which an agent can assemble in an afternoon.
Spend the time you save on the two things that actually determine quality: product photography that meets the input requirements, and sensible default placements per product category. Those decisions are worth more to the finished experience than any amount of integration code.
Get a free API key and run your worst ring photograph through the playground first, or talk to our team about catalogue scale deployment.
Author: 



