Most hosts give you two states: a file exists, or it is gone. DurableFile adds a third that automation actually needs: a link that stops serving but can come back — at the identical URL.

The default: no automatic expiry

An upload with no expiry fields has no automatic expiry. It serves while the claim stays published and the account keeps its quota. Nothing sweeps it away in the background.

Scheduling an expiry

Send one of two fields, never both:

# Relative: stop serving in 72 hours.
curl -X POST "https://durablefile.com/v1/upload?unpublish_after_hours=72" \
  -H "Authorization: Bearer $DURABLEFILE_KEY" \
  -F "file=@handoff.csv"

# Absolute: stop serving at an exact instant.
curl -X POST "https://durablefile.com/v1/upload?unpublish_at=2027-01-01T00:00:00Z" \
  -H "Authorization: Bearer $DURABLEFILE_KEY" \
  -F "file=@handoff.csv"

The rules are strict and return exact errors:

  • unpublish_after_hours must be greater than zero. A computed zero is rejected instead of silently meaning "no expiry", so a bug in your duration math surfaces as an error rather than a link that never dies.
  • The maximum window is 8,760 hours — one year.
  • unpublish_at accepts epoch seconds or an ISO 8601 time, must be in the future, and must be within one year.
  • Sending both fields is a conflicting_expiry error.

Expiry is evaluated when the public URL is requested, not by a cron job. The link stops serving at its deadline even if no background task ever runs.

Hiding a link now

curl -X POST https://durablefile.com/v1/files/$HASH/unpublish \
  -H "Authorization: Bearer $DURABLEFILE_KEY"

The public URL returns 404 from that moment. Two things do not happen: the bytes are not deleted, and your quota is not freed. You are still storing the file — that is precisely what makes the next step possible.

Bringing the identical URL back

# Republish permanently.
curl -X POST https://durablefile.com/v1/files/$HASH/publish \
  -H "Authorization: Bearer $DURABLEFILE_KEY" \
  -H "Content-Type: application/json" -d '{}'

# Or republish with a fresh 24-hour window.
curl -X POST https://durablefile.com/v1/files/$HASH/publish \
  -H "Authorization: Bearer $DURABLEFILE_KEY" \
  -H "Content-Type: application/json" -d '{"unpublish_after_hours":24}'

Because URLs are content-addressed, the republished link is byte-for-byte the URL you shared before. Documents that embedded it, tickets that quoted it, and systems that stored it all work again. The mechanics of that stability are covered in the content-addressing article.

A repeat upload of the same bytes also acts as a republish — and replaces the expiry with whatever that upload specifies, including none.

Deleting for real

curl -X DELETE https://durablefile.com/v1/files/$HASH \
  -H "Authorization: Bearer $DURABLEFILE_KEY"

Delete is the only operation that frees quota. It removes your claim immediately; the freed bytes show up in GET /v1/me right away.

One nuance in a content-addressed world: if another account independently claims the same bytes, the shared object and its URL survive your delete. The stored object disappears only when the last claim is gone. Your part of the contract — your claim, your quota — ends either way.

The contract in one table

Action Public URL Bytes Your quota
Expiry passes 404 (while no live claim exists) kept still used
Manual unpublish 404 (while no live claim exists) kept still used
Republish serves again, identical URL kept still used
Delete 404 unless another account claims it kept only if claimed elsewhere freed

Files are public while live, so never rely on an expiry to protect a secret that was uploaded by mistake — delete it. And remember that anyone who downloaded a file while it was live keeps their copy; unpublishing controls future downloads only.