Jewelry AI · the model step
Your model photo is uploaded at the pick, not at Next
Choosing your own photograph starts the transfer immediately: no button after the picker is what sends it. Switching back to the catalogue clears the screen, not the server — and what the server does with an upload nothing ever used is code you do not drive.
The send happens at the pick, not at Next
In the model step of the create flow you either take a model from the catalogue or use a photograph of your own. On the own-photo side, the file is sent to the server the moment you pick it: before you press Next, and before any generation has been started.
This is why backing out feels misleading. The screen still looks like a step you have not committed to, because nothing you pressed has cost you a generation. The transfer, however, was requested and begun at the pick.
What is established here belongs to that one step. It is not a statement about every picker in the app, and it is not a claim about how any other screen handles a file you choose.
The behaviour is read from apps/jewelry-mobile/src/features/generation/create-flow/steps/model-source-step.tsx, where the upload call sits directly in the handler for the chosen asset and the line carries an upload-on-pick comment; verified in the repository on 18 August 2026.
What each part of the step leaves behind
Four separate things happen around the pick, and they leave different traces. Keeping them apart is most of the answer.
- The pick starts the upload.
- The own-photo branch calls the upload as soon as a file is chosen. Nothing later in the step is the send (apps/jewelry-mobile/src/features/generation/create-flow/steps/model-source-step.tsx).
- Switching back to the catalogue resets the screen only.
- The client drops the upload state it was holding. No delete call is made for the file that already went to the server, so the reset is about the interface, not about storage.
- The permission request is what writes a record.
- The app asks the server for authorisation to receive a file, and that request leaves an asset record even if the client never uploads anything. It is bookkeeping created by the server-side request — not by the photo-access dialog your phone shows, and not a copy of your photograph.
- The gate that authorises the transfer never sees the file.
- At that point the server has no bytes, and no size ceiling is attached to the signed link — the code states this in its own comment. A limit does exist; it is applied at the generation request instead (apps/api/src/uploads/uploads.controller.ts).
Where the file is at each point of the model step
Read the middle column as the state on the server, and the right column as what changing your mind actually affects.
| Point in the step | What the server has afterwards | What backing out changes |
|---|---|---|
| You move to the own-photo side | Nothing from your library | Nothing yet to change |
| You pick a file | Permission has been requested, a record exists, and the transfer has begun | The upload state on the screen clears; the object that reached the server is not deleted by the step |
| You do not press Next | A stored object that is linked to no generation | Clearing it is scheduled server code, not an action available in the step |
| You do submit a generation | The check that can refuse the file runs at this request, not at the pick | Not covered on this page |
What comes back
No image comes back, because nothing was generated. The file moved; the flow did not. That is the whole shape of this path, and it is why the page describes no output format, no transparency behaviour and no resolution — this route produces none of them.
What comes back on screen is an upload state on the own-photo side of the step, and that state is exactly what clears when you switch back to the catalogue.
What the server has afterwards is the stored object and its record, unlinked to any generation because none was submitted.
The rest of what choosing your own photograph changes — what a run made from it keeps and what it drops — is set out where that path is described, and is not repeated here.
Clearing an upload nothing ever used is server-side code
An upload that is never linked to a generation is handled by clean-up code on the server. It is not an action offered to you in the flow, and the moment you change your mind is not the moment anything is removed.
The window the server waits before treating an unused upload as a candidate for clearing is many times longer than the fifteen minutes a signed upload link stays valid, and that gap is deliberate: a slow network or an app pushed into the background should count as a legitimate transfer, not as abandonment.
User model assets are covered by a separate ageing and clean-up watcher in the database layer (packages/db/src/asset-purge.ts:265-286). What the code shows is that such assets are watched. It does not show when a particular file is removed, so no interval is given here for a model photograph.
Two further limits belong to the same mechanism. The sweep for unlinked uploads runs on a schedule rather than continuously, and an image that was actually used in a generation is outside its reach — it is not the sweep that touches those.
The code also handles the race between a removal and a transfer that arrives late: an asset is not treated as permanently gone while its upload window is still open, and a second removal follows once that window has closed, so a file already removed cannot quietly reappear through a link that is still valid (apps/api/src/storage/asset-purge-sweeper.ts).
Whether these watchers are running in the production environment cannot be read from the code. What the code guarantees is narrower: the timer is not installed in the test environment. None of this is a retention period, a deletion guarantee or a privacy commitment — it is a description of a mechanism, and it is scheduled rather than promised.
Where expectations break
Each of these is a reasonable assumption that the step quietly does not meet.
Treating Next as the moment of sending, so backing out feels like nothing happened.
Treat the pick as the send. The only decision point that exists on this path is which photograph you open the picker for.
Expecting the switch back to the catalogue to undo the upload.
Expect it to clear the screen. The object that already reached the server is not deleted by that switch, and clearing it is server code on its own schedule.
Assuming a photograph you never sent left no trace of any kind.
The request that asks the server to accept a file writes a record even when no bytes follow it. Read that as bookkeeping on the server, not as a copy of the photograph.
Expecting the file to be vetted at the moment you pick it.
The permission step sees no bytes. The check that can refuse a file runs at the generation request, so picking is not a verdict on the file.
Before you pick your own model photo
Four habits that match how the step actually behaves.
- Decide which photograph you are willing to send before you open the picker, because the pick is the send.
- If you are only comparing options, stay on the catalogue side — taking a model from the catalogue does not send a file from your library.
- Expect the switch back to the catalogue to clear the screen state, and nothing beyond it.
- Do not plan on removing an abandoned upload yourself; what the code describes is a server mechanism, not a control in the flow.
What this page does not settle
The boundaries below are honest gaps, not omissions to be filled in later by inference.
- Where the upload starts is established for the model step of the create flow. It is not a statement about every picker in the app.
- How long a model photograph you never used stays on the server is not readable from this surface, and no interval is claimed for it.
- Whether the clean-up watchers run in the production environment is not readable from the code; the code only guarantees the timer is not installed under test.
- No retention period, deletion guarantee, privacy statement or compliance claim is made here, and none can be built out of a clean-up interval.
- No user-facing way to remove an abandoned upload is described or claimed on this page.
- Whether the storage bucket or the infrastructure below it imposes its own size ceiling cannot be read from the code.
- What is actually sent when you do submit a generation is a separate question with its own page.
- How long signed links stay valid is used above only as a comparison of scale; the lifetime of those links is not explained on this page.
Questions
I picked a photo and went back before pressing Next. Was it uploaded?
Yes. On the own-photo side of the model step the file is sent as soon as you pick it, before Next and before any generation. Going back to the catalogue clears the upload state on the screen; it does not send a delete for what already reached the server.
Does the app delete it when I change my mind?
Not at that moment, and not as an action of the step. An upload that is never linked to a generation is handled by clean-up code on the server, on its own schedule, and user model assets are covered by a separate watcher whose exact interval is not readable from this surface. Nothing here is a deletion guarantee.
I gave the app access to my photos but picked nothing. Did that leave a record?
The record described here is not created by your phone's photo-access dialog. It is written when the app asks the server for permission to receive a file, and it can exist even if no bytes are ever uploaded. It is bookkeeping on the server, not a copy of a photograph.
Is the file checked before it goes up?
No. The permission step sees no bytes and puts no size ceiling on the signed link — the code says as much in its own comment. A limit does exist, but it is applied at the generation request rather than at the pick.