Verifying a purge
A purge returns as soon as the edge has accepted it, and its result counts keys, not cached
responses. To know what a purge actually covered, look it up in the service’s purge history. To
know that a particular response is gone, request it again and read the gp-cache header.
The purge history
Section titled “The purge history”Every purge of a service’s cache is recorded, as long as request telemetry is stored: the
automatic ones a write’s response triggers, and the manual ones sent through the GraphPilot
API, gpilot purge or the portal. Each record holds the cleartext keys the
purge addressed.
The history is request telemetry, so it is stored and kept under the same rules; see what Insights needs.
In the portal
Section titled “In the portal”Open the service and choose Purges. The list, its filters and the detail panel of one purge are described on Purges. A purge takes effect immediately, but it can take up to a minute to appear there.
Through the GraphPilot API
Section titled “Through the GraphPilot API”The same history is the purges field on a Service, looked up here by its
subdomain. It takes a required time window, an
optional filter, and cursor pagination (first, at most 1000 per page, and after). Entries come
newest first.
query { services(condition: { subdomain: "my-shop" }) { purges( first: 50 timeRange: { from: "2026-09-22T00:00:00Z", to: "2026-09-23T00:00:00Z" } filter: { trigger: [AUTO], key: "User:id:1" } ) { nodes { timestamp trigger soft keys keyCount error actor { name email } triggeredByRequest { id path method } } pageInfo { hasNextPage endCursor } } }}| Filter field | Type | Keeps |
|---|---|---|
trigger | [PurgeTrigger!] | AUTO, MANUAL, or both. |
scope | PurgeScope | EVERYTHING (purge-all) or KEYS. |
soft | Boolean | true for soft purges, false for hard ones. |
outcome | PurgeOutcome | SUCCEEDED or FAILED (an error was recorded). |
key | String | Purges with at least one key containing this text. % and _ are not wildcards. |
actorUserIds | [String!] | Manual purges sent by these users. Automatic purges have no actor, so this filter excludes them. A purge sent with an API key is not recorded under a member’s user id. |
All filter fields combine with AND; list fields match any of their values. keys is empty and
keyCount is 0 for a purge-all. error is null unless the purge recorded one.
What an automatic purge records
Section titled “What an automatic purge records”When a GraphQL mutation response carries keys that automatic invalidation follows, GraphPilot purges them and records one entry:
- Trigger is automatic, and there is no actor.
- Keys are the ones it purged: the entity keys and origin keys on the response, plus
![Type]for each type it touched. Keys that ride along without being purged (such asm:updateUseror a type key) are not listed. - Triggered by points to the mutation request.
- It is always a hard purge.
A plain HTTP write whose response carries a Surrogate-Key header
is recorded the same way, with the header’s keys and the write request as Triggered by.
A mutation whose response carried nothing to purge records nothing. If you expected a purge and the history shows none for that mutation, the response did not name the object; see what is not invalidated automatically.
The mutation request’s Overview tab in Insights also lists CACHE_PURGED under Edge codes
for each automatic purge, with the number of keys (key_count) rather than the keys themselves.
Edge codes are stored with the request, so they follow the same rules as the history.
Confirming at the edge
Section titled “Confirming at the edge”The history tells you what was purged. To see that a particular response is really gone, request
it before and after, and compare the gp-cache header, which is on every response by default.
-
Send the query and check that it is cached. Run it twice if needed:
Terminal window curl -sS -D- -o/dev/null "https://$GRAPHPILOT_HOST/graphql" \-H 'content-type: application/json' \-d '{"query":"{ user(id: 1) { id name } }"}' | grep -i '^gp-cache'gp-cache: HITgp-cache-age: 412 -
Purge it, for example with
gpilot purge --yes User:id:1, or run the mutation that should invalidate it. -
Send the same query again. After a hard purge the entry is gone and the request goes to your origin:
gp-cache: MISSAfter a soft purge of an entry stored with a stale window you see
STALEinstead: the old response is served once more while GraphPilot fetches a fresh one. -
Send it once more. It is a
HITagain, with a smallgp-cache-age, because the response from step 3 was stored as a new entry.
Purges reach the edge locations asynchronously. If the request in step 3 still reports a HIT
with a large gp-cache-age, retry after a moment before looking further.
If it keeps hitting, the purge did not name a key that entry carries. Check, in this order:
- The entry’s keys. Switch on the
gp-surrogate-keyheader or open the request in the portal, and compare its keys with the ones in the purge record. See debugging. - A plain HTTP cache key without its prefix. A cache key taken from telemetry through the
API needs
ck:in front of it. A GraphQL cache key without the prefix is recognized and purged in both forms, so the prefix is not the cause there; a plain HTTP one sent without it matches nothing. See purging one cached response by its cache key. - A hostname in a plain HTTP cache key. By default the key has no scheme or hostname, so
ck:GET:https://catalog-api.k7m2qp.graphpilot.cloud/products/42matches nothing: purgeck:GET:/products/42. If the service setscache.http_key.host = "include", it is the other way round: the key is the full URL, and each hostname the path was requested through has an entry of its own to purge. - A named operation purged by hash.
purgeByQueryHashwith avariablesHashandoperationName: nullpurges only the entry of requests that sent no operation name. Pass the name from the request’s cache key, after the query hash and a#(graphql.operationNamecan differ for a request that sent none), or omit it to have the names looked up; the lookup refuses the purge when telemetry saw no request with that query and variable set. - A different service. A purge applies to one service; a request to another service’s hostname reads another cache.
Related
Section titled “Related”- Purging through the API and CLI:
gpilot purgeand the four purge mutations - Automatic invalidation: what a mutation purges
- Cache statuses: what
HIT,MISSandSTALEmean - Debugging surrogate keys: reading the keys a response carries