Asking questions
Places
A place is a spot the user named — home, work, the gym. They set its centre and radius. You never see either.
You get identity only:
{ "id": "9f2c…", "label": "Home", "kind": "home" }
No lat, no lon, no radiusM, anywhere in the API. That is how your app can ask "are they home?" without learning where home is.
kind is one of home, work, school, gym, other, or null. Use it for icons. Most users leave it unset.
You only see what was shared
Place visibility is per connection. When a user connects your app, they tick which places you may ask about, and can change that later without re-minting your token.
You can list the ids you're allowed to ask about:
GET /v1/answers/places
Authorization: Bearer cka_…
{
"version": 7,
"places": [
{ "id": "9f2c…", "kind": "home" },
{ "id": "41ab…", "kind": null }
]
}
No labels. This is the one deliberate gap, and it's the point: a label is the most revealing thing a place carries — people name places Dr Patel or AA Thursday — so a single call at connect time would hand you the user's whole private vocabulary. Labels reach you as the user actually moves, through current-place, presence and visits, one at a time.
Use this to keep your stored ids honest: an id missing from the list is gone, whether the user unshared it or archived it, and you can drop it without waiting for a 404. An empty list is normal.
It needs location.place.presence — the scope this route exists to make usable — and doesn't count against your question budget.
A place that wasn't shared with you is invisible everywhere: presence returns 404, current-place never names it, visits never label with it, and a rule pointed at it fails with 404. That is the same response as an id that doesn't exist, so you cannot discover ids by guessing.
Noticing when the set changes
A place created after you connected is shared with nobody until the user shares it, and nothing about your existing token changes when they do. So you need a way to find out — without polling a list that is almost always identical.
version is that signal. It moves whenever the set you can see moves, in either direction, and never when it doesn't — a user opening their share dialog and pressing save unchanged does not move it. Every answer you receive carries the current value:
ContextKit-Places-Version: 7
Store the version that came with the list you fetched, then compare it against the header on subsequent answers. When it differs, re-read GET /v1/answers/places; when it matches, you already hold the current set and can skip the call entirely.
Store the list's own version rather than a header value read at some other moment — the two describe different instants, and the list's is the one that describes the list you have.
If your app is usually asleep and needs to know without waiting to be asked, subscribe to places.changed instead. It carries the same version and no place identity, so you still re-read the list to learn what changed. It needs location.places.watch, which is a separate scope: an existing connection has to be re-authorized before it can subscribe.
Archiving
Users archive places instead of deleting them. An archived place stops matching new fixes immediately, but keeps its label on past visits so their history stays readable.
From your side it looks exactly like an unshared place. If a cached place_id starts returning 404, drop it.
Building on this
Cache the id, not the label. Users rename places. The id is stable.
Never require a specific place. A user may share none, one, or ten. An onboarding step that says "share your Home place to continue" strands anyone who named theirs something else. Call current-place, show what comes back, and treat no_place as normal.
Don't try to work out where a place is. Correlating visit times against other signals to infer coordinates defeats the arrangement, and the per-connection budgets exist partly to make it impractical. If you need coordinates, ask for the sensitive tier and go through its review.