Documentation Index
Fetch the complete documentation index at: https://docs.photon.codes/docs/llms.txt
Use this file to discover all available pages before exploring further.
im.locations sends Find My location-sharing requests and reads friend locations that are already visible to the current iMessage account.
Use request(chat, address) when you want to ask someone to share location. Use list(...), get(...), and watch(...) after location is already shared with the current account.
Location updates are not part of the durable event log. im.locations.watch(...) is a dedicated live stream. Updates missed while disconnected cannot be replayed with im.events.catchUp(...).
What You Can Do
| Need | Use this when |
|---|
| Request location sharing | You want to send a visible Find My request card to a chat participant |
| List shared locations | You want every currently visible friend location |
| Fetch one location | You want the latest snapshot for one friend |
| Watch live updates | You are updating a map or background job as locations change |
Before You Use It
| Rule | Meaning |
|---|
| Requests do not grant access | request(...) only sends a visible request card; the other person must accept or start sharing |
| Reads only show visible shares | list(...), get(...), and watch(...) only return locations already visible to the current account |
| Coordinates can be absent | Check both latitude and longitude before plotting a point |
| Missed updates are gone | watch(...) updates missed during a disconnect are not replayed by catchUp(...) |
Request Location Sharing
Send a visible Find My request card in an existing direct or group chat:
const receipt = await im.locations.request(chat.guid, "alice@example.com");
console.log(receipt.requestStatus, receipt.messageGuid);
Inputs:
| Argument | Meaning |
|---|
chat | Where to send the request card. Pass a direct or group chat.guid, not an email or phone number. |
address | Who to ask for location. Pass that person’s full email address or E.164 phone number. |
options.clientMessageId | Optional idempotency key for retrying the same logical request. |
The address must belong to someone in chat. In a direct chat, that means the other participant. In a group chat, that means an existing group member.
Returns LocationRequestReceipt. A successful call means the request card was sent or the server accepted the request operation. It does not mean the other person is now sharing location.
The returned receipt includes:
| Field | Meaning |
|---|
address | The normalized email address or E.164 phone number that was requested |
requested | Whether the server sent or accepted a request operation |
requestStatus | Server status for the request operation |
reason | Optional reason when the request was not sent or needs explanation |
messageGuid | Message GUID for the request card, when a card was created |
When messageGuid is present, use it like any other message GUID. For example, you can look it up with im.messages.get(...).
For idempotent retries from your job system, pass clientMessageId:
await im.locations.request(chat.guid, "alice@example.com", {
clientMessageId: `location-request-${job.id}`,
});
clientMessageId is only needed when your queue or worker may rerun the same logical request after a crash or timeout. Most direct calls can omit it. See error handling for details.
After the other person accepts or starts sharing, use get(...), list(...), or watch(...) to read their location.
List Shared Locations
const locations = await im.locations.list();
for (const location of locations) {
if (location.latitude === undefined || location.longitude === undefined) {
continue;
}
console.log(location.address, location.latitude, location.longitude);
}
list(...) takes no arguments.
Returns SharedFriendLocation[]. If no friends are sharing location, the array is empty. Each item is a location snapshot, and a snapshot is not guaranteed to include coordinates.
latitude and longitude are optional. They may be absent while a device is still locating, when location is unavailable, or when only address metadata is available.
Check both latitude and longitude before showing a map marker. Do not rely only on locationType, and do not assume every listed location has coordinates.
Get One Friend Location
const location = await im.locations.get("alice@example.com");
console.log(location.name ?? location.address, location.locationType);
Input:
| Argument | Meaning |
|---|
address | The friend whose location you want. Pass a full email address or E.164 phone number. Do not pass chat.guid or a display name. |
Phone numbers must include the country code, start with +, and omit spaces, parentheses, and dashes.
Returns SharedFriendLocation. If the address is not sharing location or is not visible to the current account, get(...) throws NotFoundError.
The returned object looks like this:
{
"address": "alice@example.com", // Normalized email address or E.164 phone number
"isLocatingInProgress": false, // Whether the device is still locating
"locationType": "live", // Freshness of the location source
"latitude": 37.7749, // Latitude; may be absent
"longitude": -122.4194, // Longitude; may be absent
"accuracy": 12, // Horizontal accuracy in meters; may be absent
"locationTimestamp": "2026-01-01T12:00:00Z", // Time the location was captured; may be absent
"expiresAt": "2026-01-01T13:00:00Z", // Share expiration time; may be absent
"name": "Alice", // Friend display name; may be absent
"shortAddress": "Mission, SF", // Short human-readable address; may be absent
"longAddress": "Mission District, SF" // Full human-readable address; may be absent
}
Location Types
location.locationType describes how fresh the snapshot is:
| Value | Meaning |
|---|
"live" | Actively updating live location |
"shallow" | Recent cached location |
"legacy" | Older cached location from a previous session |
"unknown" | The server could not classify the source |
locationType only describes freshness. It does not guarantee that coordinates are present.
Watch Live Updates
Scope
Watch every visible friend’s location updates:
for await (const update of im.locations.watch()) {
console.log(update.location.address, update.sourceSequence);
}
Watch one address:
for await (const update of im.locations.watch("alice@example.com")) {
const { latitude, longitude } = update.location;
if (latitude === undefined || longitude === undefined) {
continue;
}
console.log(latitude, longitude);
}
Inputs:
| Call | Meaning |
|---|
watch() | Watch every visible friend’s location updates |
watch(address) | Watch one friend by full email address or E.164 phone number |
Do not pass chat.guid or a display name to watch(...).
Update Shape
Each update is SharedFriendLocationUpdated:
{
"location": { // Latest SharedFriendLocation snapshot
"address": "alice@example.com",
"locationType": "live",
"latitude": 37.7749,
"longitude": -122.4194
},
"sourceSequence": 123 // Live-location stream sequence; useful for duplicate detection
}
Breaking out of the for await loop closes the live stream.
sourceSequence belongs only to the location live stream. It is not the same as durable event sequence. You can use it to detect duplicate live updates after reconnecting, but you cannot use im.events.catchUp(...) to recover location updates missed while disconnected.
Next Steps
- Addresses — check whether an email address or phone number is reachable over iMessage
- Events — understand durable events and catch-up
- Chats — manage chats, read state, and typing state