API · Conventions
Caching
ETags, Cache-Control, and 304 Not Modified on expensive GET endpoints.
Last updated
TL;DR. GETs return ETag. Send If-None-Match: "<etag>" to get 304 Not Modified
when nothing changed. Expensive views (cap table, compliance rollup) ship strong
validators; individual resources ship weak validators.
Matter returns ETags on resource GET endpoints where caching is cheap and safe. Use
conditional requests to avoid re-fetching and re-serializing unchanged resources.
ETag on retrieve
GET /v1/entities/ent_Nq3KcAbc HTTP/1.1
HTTP/1.1 200 OK
ETag: W/"sha256:9f86d08..."
Cache-Control: private, max-age=10
Content-Type: application/jsonOn a subsequent request, send If-None-Match with the ETag:
GET /v1/entities/ent_Nq3KcAbc HTTP/1.1
If-None-Match: W/"sha256:9f86d08..."
HTTP/1.1 304 Not ModifiedYou still spend a round trip, but the body is empty — useful for polling loops where the
resource is usually unchanged (e.g., watching an entity in registered state until it hits
active).
Cache-Control values
| Endpoint family | Cache-Control |
|---|---|
| Retrievals of immutable resources (audit entries, events, incorporator receipts, signed documents) | public, max-age=86400, immutable |
| Retrievals of mutable resources (entities, tax profile, registered agent) | private, max-age=10 |
| Cap-table snapshots | private, max-age=30 |
| Compliance dashboards | private, max-age=60 |
| Token metadata | no-store |
| List endpoints | no-cache |
Don't cache errors
RFC 7807 problem responses carry Cache-Control: no-store. Errors are never cached because
retrying may produce a different result.
Best practices
- Use conditional requests for polling loops — cheaper than re-fetching bodies.
- Never cache across keys or tokens. Each consumer has its own view.
- Never cache
POSTresponses. - Don't cache
listresponses. They're paginated and their membership changes.
Implementation example
async function pollUntilActive(entityId) {
let etag = null;
while (true) {
const headers = { Authorization: `Bearer ${KEY}`, "Matter-Version": "2026-05-01" };
if (etag) headers["If-None-Match"] = etag;
const res = await fetch(`https://api.mattermode.com/v1/entities/${entityId}`, { headers });
if (res.status === 304) {
await sleep(2000);
continue;
}
etag = res.headers.get("ETag");
const entity = await res.json();
if (entity.status === "active") return entity;
await sleep(2000);
}
}For long polling, prefer webhooks or the SSE stream — conditional-GET polling works but is not the fastest path to "operational" state.