Retention and storage
How long Windpaint keeps your assets, how to change it, what storage past the free 7 days costs, and how to delete assets.
Every asset, uploaded or generated, is kept for your organization’s retention period and then its bytes are deleted. The default is 7 days, and storage within the first 7 days of an asset’s life is free.
Retention period
An organization keeps assets for 1 to 365 days, or forever. The default is 7 days for every organization. The setting applies to assets created after it changes; existing assets keep the expires_at they were created with. Assets created before retention was introduced have expires_at: null and are kept until you delete them.
Change it in the dashboard under Settings → Organization → Asset retention, which also shows how much you store and how much of it is billed. Or use the API:
curl -X PATCH https://api.windpaint.ai/v1/account/storage \
-H "Authorization: Bearer $WINDPAINT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"asset_retention_days": 30}'
Send null to keep assets forever. Reading the setting needs orgs:read and changing it needs orgs:update. See Storage settings.
Per-request retention
You can keep a single job’s outputs, or a single upload, for less time than the organization setting:
- On
POST /v1/generation/capabilities/{capability},POST /v1/generation/models/{model}andPOST /v1/workflows/{slug}/runs, send"retention_days": Nin the JSON body. It applies to the outputs. - On
POST /v1/generation/uploads, add?retention_days=Nto the URL. It must be a query parameter; a multipart field namedretention_daysis ignored.
N is 1 to 365. It can only shorten retention: a value above the organization setting is clamped to it. Out-of-range values return 422 request.validation_failed.
What happens at expiry
Every asset has an expires_at (or null). Once that time passes:
GET /v1/generation/assets/{id}/contentand/urlreturn410with codegeneration.asset_goneanddetails{asset_id, reason, deleted_at, expires_at}, wherereasonisexpiredordeleted.GET /v1/generation/assetsleaves the asset out.- Using it as a job or workflow-run input returns
422with codegeneration.asset_gone. GET /v1/generation/assets/{id}still returns the asset, withdeleted_atanddeleted_reasonset once the bytes are gone. The job that made it still lists it inoutputs.
The bytes are deleted within a few minutes of expires_at. If a queued or running job or workflow run is using the asset as an input, deletion waits until that job ends. Download anything you want to keep before it expires.
Storage billing
Storage within the first 7 days after an asset is created is free, so a retention of 7 days or less never costs anything. Storage past day 7 costs 2 credits per GiB per 30 days (about 0.05 per credit).
- It’s computed per finished UTC day from the bytes you store past their free 7 days, and taken from your credit balance as one ledger entry per day, such as
Asset storage beyond the 7-day free retention: 12.345 GB-days, 2026-10-04 (2 credits per GB-month). - Billing for an asset stops when it expires or you delete it.
- If your balance is short, what’s available is charged and the rest is collected later. Assets are not deleted for non-payment.
For example, keeping 10 GiB for 30 days on a 30-day retention costs about 10 × 2 × 23/30 ≈ 15.3 credits, since the first 7 days are free. GET /v1/account/storage returns stored_bytes and billable_bytes so you can see where you stand.
Deleting assets
Delete one asset:
curl -X DELETE https://api.windpaint.ai/v1/generation/assets/$ASSET_ID \
-H "Authorization: Bearer $WINDPAINT_API_KEY"
It returns 204 and deletes the bytes immediately. Deleting an already-deleted asset returns 204 again. It returns 404 if the asset isn’t in your organization, and 409 generation.asset_in_use if a queued or running job or workflow run uses it as an input.
Delete everything a job produced, and its inputs:
curl -X DELETE "https://api.windpaint.ai/v1/generation/requests/$REQUEST_ID/assets?inputs=true" \
-H "Authorization: Bearer $WINDPAINT_API_KEY"
{ "data": { "deleted": ["d77a4c1e-..."], "skipped": [] } }
inputs defaults to true; pass false to delete only the outputs. skipped lists assets that are still in use as inputs by an unfinished job, or that a running workflow run needs in a later step. The job must have finished, otherwise the call returns 409 request.conflict.
Both endpoints need the generation:write scope. A deleted asset stays listed on its job as a record, but its bytes are gone and can’t be recovered.