API
Conditional requests and caching
ETag and If-None-Match make repeat reads cheap, If-Match makes concurrent item edits safe, and the Cache-Control headers say what a cache may keep.
Cheap re-reads: ETag and If-None-Match
get_item and get_openapi_document return a strong ETag. Send it back as If-None-Match and an unchanged resource answers 304 Not Modified with no body, which is quick and does not cost more than a normal read in your rate limit. Archive downloads (get_version_archive) also answer 304 to If-None-Match.
curl
curl -i https://reuseforge.com/api/v1/items/acme/invoice-extractor # note the ETag header
curl -i https://reuseforge.com/api/v1/items/acme/invoice-extractor -H 'If-None-Match: "3f9a0c1d7b2e4a65"' # 304 if unchangedPython
import reuseforge
client = reuseforge.Client()
item = client.get_item("acme/invoice-extractor")
etag = client.last_response.etag
again = client.get_item("acme/invoice-extractor", etag=etag) # None when the server answered 304JavaScript
import { ReuseForge } from '@reuseforge/sdk';
const client = new ReuseForge();
const item = await client.getItem('acme/invoice-extractor');
const etag = client.lastResponse.etag;
const again = await client.getItem('acme/invoice-extractor', { etag }); // null when the server answered 304An ETag is a validator, not a version number: treat it as an opaque quoted string.
Safe edits: If-Match
update_item is a partial update that requires If-Match with the ETag you last read. That makes concurrent edits safe without version fields:
| Situation | Answer |
|---|---|
If-Match matches the current ETag | The update is applied. The new item and a new ETag come back. |
If-Match is stale | 412 precondition_failed with current_etag. Re-read, merge, send again. |
If-Match is missing | 428 precondition_required. |
curl
ETAG=$(curl -sI https://reuseforge.com/api/v1/items/acme/invoice-extractor | awk 'tolower($1)=="etag:" {print $2}' | tr -d '\r')
curl -X PATCH https://reuseforge.com/api/v1/items/acme/invoice-extractor \
-H "Authorization: Bearer $REUSEFORGE_TOKEN" -H "If-Match: $ETAG" -H "Content-Type: application/json" \
-d '{"summary": "Extracts totals and line items from PDF invoices."}'Python
item = client.update_item("acme/invoice-extractor", {"summary": "Extracts totals and line items."}, if_match="auto") # reads the ETag firstJavaScript
await client.updateItem('acme/invoice-extractor', { summary: 'Extracts totals and line items.', ifMatch: 'auto' }); // reads the ETag firstCLI
rf items update acme/invoice-extractor --summary 'Extracts totals and line items.' # reads the ETag itself and sends If-MatchIn the SDKs a 412 is a ConflictError whose current_etag (currentEtag in JavaScript) is the value to retry with. if_match="auto" is a convenience for scripts: it reads, then writes, so use an explicit ETag when you want to detect edits made between your own read and your write.
What caches may keep
| Response | Cache-Control |
|---|---|
Anonymous GET of a public resource | public, max-age=15, s-maxage=30, stale-while-revalidate=60 |
| Anything authenticated | private, no-cache |
| Money and order endpoints, token secrets | no-store |
| Free public version archives | public, max-age=300; the bytes never change, but access can (a retraction must reach caches soon) |
Every JSON response also sends X-Content-Type-Options: nosniff and Vary: Authorization, Accept-Encoding, so a shared cache never serves a signed-in response to an anonymous caller. Cross-origin browsers can read ETag, Link, RateLimit-*, Retry-After, X-Request-Id, ReuseForge-Version, Idempotent-Replayed and Location; preflights allow Authorization, Content-Type, Idempotency-Key, If-Match and If-None-Match.
Last updated: