Selling a file#
A file is not an API response, and the difference matters more than it looks.
Put a signed URL in the payload and you have handed the buyer a link that is theirs forever: shareable, unmetered, and impossible to take back. That is not a sale, it is a distribution.
What happens instead#
c402 checkouts create --title "Q4 report (PDF)" --price 5.00 \
--fulfillment file --origin-url https://api.yourcompany.com/sign \
--download-ttl-hours 24
The buyer never receives the file's address. They receive a grant —
https://api.checkout402.com/d/dl_… — and every time they follow it we ask
your signing endpoint for a fresh short-lived URL and redirect them to it.
Your endpoint returns whichever of these is convenient:
{"url": "https://your-bucket.s3.../report.pdf?X-Amz-Expires=60&..."}
download_url, href, location and signed_url all work too, as does a
bare https://… in the body. Only https is followed — a javascript: or
http:// answer is refused rather than redirected to.
We never see your storage credentials
You sign the URL, we redirect to it. checkout402 holds no cloud keys, and the bytes go from your storage to the buyer without passing through us.
Rules for disposability#
Four independent rules. Any one of them ends the grant, and every failure returns the same message — distinguishing "expired" from "revoked" from "never existed" tells whoever is probing which one they are looking at.
| Rule | Set by | Default |
|---|---|---|
| Expiry | --download-ttl-hours |
72 hours, max 720 |
| Download count | --calls |
the package allowance |
| Revocation | you, at any time | — |
| Signed-URL lifetime | your signing endpoint | seconds, your choice |
The fourth is the one that makes the rest safe. Even a redirect captured in flight expires almost immediately, because you scoped it that way.
Revoking#
c402 receipts usage pay_… # see what has been downloaded
curl -X POST -H "Authorization: Bearer c402_…" \
https://api.checkout402.com/v1/receipts/pay_…/revoke-download
Immediate, and it cannot be undone. Re-granting means issuing a new token — "I took it back" and "I gave it again" are different events, and the log should say which happened.
A failed download is not charged#
If your signing endpoint is down, the buyer gets a 502 saying so and the download is not counted. Their grant is intact. Your outage is not their problem.
Why we redirect rather than serve the bytes#
Honestly: because carrying them would lose money.
The protocol fee is 0.5% + $0.01, priced for settlement. Bandwidth cost scales with file size while revenue scales with price, and those two are uncorrelated — a cheap large file is a guaranteed loss and no percentage fixes it:
| Price | Size | We earn | Egress | Margin |
|---|---|---|---|---|
| $1.00 | 100 MB | $0.0150 | $0.0088 | +$0.0062 |
| $1.00 | 500 MB | $0.0150 | $0.0439 | −$0.0289 |
| $25.00 | 2 GB | $0.1350 | $0.1800 | −$0.0450 |
Redirecting keeps egress on your storage bill, where it is already priced, and keeps every control that matters with us. You lose nothing: the buyer still cannot share, reuse, or outlive the grant you gave them.
If your storage genuinely cannot produce signed URLs, tell us — streaming through checkout402 is buildable, but it needs a per-GB charge to not settle at a loss, and we would rather not add a second pricing dimension for one case.