HTTP API
LocationNotes API documentation
Use this page when you need the real HTTP routes, query parameters, authentication rules, and copy-paste examples behind the website and Android app. The public site, authenticated website flows, and Android client all talk to this same API surface on the same domain.
Need the product model behind the endpoints? What is a User?, What is a Team?, What is a Note?, and What is a Trackable? explain the objects these routes operate on.
Base URL and conventions
Base URL
All examples on this page assume the current production host:
https://locationnotes.com
Format
Requests and responses use JSON. GUIDs are the primary identifiers across notes, categories, teams, and devices.
Authorization
Private, sync, and team endpoints require an `Authorization: Bearer <access token>` header unless the route is marked public below.
Language
Public and management reads can pass `contentLanguage=en-US` or another supported content language to filter the returned note/category content.
Authentication and bearer tokens
Website sign-in pages and external-provider setup are documented on the Authentication page. API clients that want a bearer token should use the identity API login route with cookies turned off, which is the same pattern the Android app uses.
POST /api/auth/login?useCookies=false&useSessionCookies=false
Anonymous
Request body
{
"email": "tester@example.com",
"password": "StrongP@ssw0rd!"
}
Response excerpt
{
"tokenType": "Bearer",
"accessToken": "eyJhbGciOi..."
}
curl -X POST "https://locationnotes.com/api/auth/login?useCookies=false&useSessionCookies=false" \
-H "Content-Type: application/json" \
-d '{
"email": "tester@example.com",
"password": "StrongP@ssw0rd!"
}'
Other default identity endpoints still live under /api/auth. If you need the full interactive website flow, provider callbacks, or account-linking behavior, use the
Authentication page.
Public notes in map bounds
Use this endpoint for public map windows and browse-by-area experiences. This is the route the homepage map uses.
GET /api/notes/public/bounds
Anonymous
- Required query string:
minLatitude,minLongitude,maxLatitude,maxLongitude - Optional query string:
contentLanguage
curl "https://locationnotes.com/api/notes/public/bounds?minLatitude=41.78&minLongitude=-87.75&maxLatitude=41.96&maxLongitude=-87.54&contentLanguage=en-US"
[
{
"noteId": "4d6c5df3-3c53-4d0e-8e72-7d98a0f8a9f3",
"ownerUserId": "a7cfd28f-c17f-4cf7-8913-47fa10fd0d1f",
"categoryId": "4de6bb76-f25d-4c73-b8e3-81b9ca3bf08f",
"title": "Dock gate closed",
"body": "Security redirected vehicles to the south entrance.",
"contentLanguage": "en-US",
"latitude": 41.8818,
"longitude": -87.6231,
"visibility": "Public",
"isDeleted": false,
"updatedUtc": "2026-03-13T20:10:00Z",
"clientMutationId": "web-demo-public-1",
"teamId": null
}
]
Public notes near a point
Use this endpoint when the client already knows a center point and wants a distance-limited result set instead of a rectangular map window.
GET /api/notes/public/nearby
Anonymous
- Required query string:
latitude,longitude - Optional query string:
radiusKm(default5),contentLanguage
curl "https://locationnotes.com/api/notes/public/nearby?latitude=41.8818&longitude=-87.6231&radiusKm=8&contentLanguage=en-US"
Offline sync cycle
Sync clients should push local dirty data first, then pull server changes for the user, active teams, and public notes in the current area. Both sync routes require bearer auth.
POST /api/sync/push
Bearer
{
"deviceId": "5dd06ca7-34a5-4f2e-812d-3f1ef3e48290",
"notes": [
{
"noteId": "ef90c4ca-88a9-4a9b-b3a8-36e2649c5dcb",
"ownerUserId": "a7cfd28f-c17f-4cf7-8913-47fa10fd0d1f",
"categoryId": "4de6bb76-f25d-4c73-b8e3-81b9ca3bf08f",
"title": "Offline inspection",
"body": "Saved while disconnected.",
"contentLanguage": "en-US",
"latitude": null,
"longitude": null,
"visibility": "Private",
"isDeleted": false,
"updatedUtc": "2026-03-13T20:12:00Z",
"clientMutationId": "android-offline-42",
"teamId": null
}
],
"categories": []
}
{
"appliedNoteIds": [
"ef90c4ca-88a9-4a9b-b3a8-36e2649c5dcb"
],
"appliedCategoryIds": [],
"conflicts": []
}
POST /api/sync/pull
Bearer
{
"lastSyncUtc": "2026-03-13T19:45:00Z",
"publicArea": {
"minLatitude": 41.78,
"minLongitude": -87.75,
"maxLatitude": 41.96,
"maxLongitude": -87.54
}
}
{
"serverSyncUtc": "2026-03-13T20:13:02Z",
"userNotes": [],
"userCategories": [],
"teamCategories": [],
"publicNotes": [],
"teamNotes": []
}
Sync endpoints reject anonymous requests. If the server blocks an older Android beta build, check GET /api/system/status for the minimum compatible version and beta-page URL.
Personal notes and categories
These routes back the signed-in website workspace and personal Android sync state. All require bearer auth.
Read personal notes
GET /api/notes/mine
Bearer
Optional query string: contentLanguage and full map bounds with minLatitude, minLongitude, maxLatitude, maxLongitude.
Create a note
POST /api/notes/mine
Bearer
{
"categoryId": "4de6bb76-f25d-4c73-b8e3-81b9ca3bf08f",
"title": "Category-only feedback",
"body": "This note stays off the map on purpose.",
"contentLanguage": "en-US",
"latitude": null,
"longitude": null,
"visibility": "Public",
"commentPolicy": "LoggedInUsers"
}
Note page reads and actions
GET /api/public/notes/{noteId}
Anonymous or Bearer
GET /api/public/notes/{noteId}/comments
Anonymous or Bearer
POST /api/public/notes/{noteId}/comments
Bearer
GET /api/public/notes/{noteId}/trackables
Anonymous or Bearer
POST /api/public/notes/{noteId}/trackables
Bearer
{
"body": "I found it too."
}
{
"trackableSecretCodes": "LN4C8R2Z",
"selectedActiveTrackableIds": [
"f3a8f841-20db-4f1e-a3f8-9f14bc0b3c31"
],
"activeTrackableAttachMode": "Self"
}
These note-page routes respect private-note access. If the caller can open the note page, the API can return comments and visible trackables for that note. Attaching trackables requires the note owner or a team admin.
Read categories
GET /api/categories/mine
Bearer
GET /api/categories/mine/tree
Bearer
Create or move categories
POST /api/categories/mine
Bearer
POST /api/categories/mine/{categoryId}/move
Bearer
{
"name": "Field Reports",
"contentLanguage": "en-US",
"parentCategoryId": null
}
Team endpoints
Team routes cover membership, invites, settings, team notes, and team categories. All require bearer auth and the server enforces admin/member permissions per route.
Common reads
GET /api/teamsGET /api/teams/{teamId}/notesGET /api/teams/{teamId}/categoriesGET /api/teams/{teamId}/categories/treeGET /api/teams/{teamId}/invite-links
Create a team
{
"name": "beta-testers",
"title": "Beta Testers",
"description": "Public beta release gate for Android validation.",
"joinPolicy": "RequestsAllowed",
"pageVisibility": "Public",
"defaultNoteVisibility": "Public",
"contentLanguage": "en-US"
}
Create a team note
POST /api/teams/{teamId}/notes
Bearer
{
"categoryId": "68cb4c9b-d9bd-4bde-8c6a-a03a4c70b283",
"title": "Shared dock inspection",
"body": "Visible to the team until we publish it.",
"contentLanguage": "en-US",
"latitude": 41.8818,
"longitude": -87.6231,
"visibility": "Private",
"commentPolicy": "TeamMembers"
}
Membership and invite operations
POST /api/teams/{teamId}/memberships/requestPOST /api/teams/{teamId}/memberships/invitePOST /api/teams/{teamId}/memberships/{membershipId}/acceptPOST /api/teams/{teamId}/memberships/{membershipId}/refusePOST /api/teams/{teamId}/memberships/{membershipId}/approvePOST /api/teams/{teamId}/memberships/{membershipId}/denyPOST /api/teams/{teamId}/memberships/{membershipId}/promote-adminDELETE /api/teams/{teamId}/memberships/{membershipId}POST /api/teams/invite-links/{teamSlug}/{inviteCode}/join
Trackable endpoints
Trackables support public-code browsing, secret-code or private scan-QR activation, one-time secret reveal during creation, browser-side active sessions, comments, and grouped inventory workflows. Secret short codes and private scan URLs are only returned from the create endpoints below. They are never returned again from read, lookup, comment, or detail routes.
- Public codes are short public tokens that stay globally unique across all trackables.
- Public codes and short secret codes are collision-free against each other, so one short code cannot mean both things.
- Short secret codes and private scan URLs are possession-based access credentials.
- Website note-attachment forms accept one trackable code at a time and only attach from an existing short secret code match.
- If an external code is not registered yet, create the trackable first. Manual note-attachment entry does not auto-register new third-party codes.
- Activation is inferred from
OwnerUserId; there is no separate persisted activation flag. - Unactivated trackables cannot be attached to notes and cannot accept comments.
- A trackable can belong to only one group at a time, and changing groups requires detach first, then reattach.
Identifier formats and examples
| Format | Example | Lookup rule |
|---|---|---|
| Public code token | LN-7K4V9T |
Globally unique short public token. Safe for public lookup and public links. |
| Public entry route | GET /trackable/LN-7K4V9T |
Short public entry URL. The website can redirect this to the canonical public page. |
| Secret entry route | GET /trackable/LN4C8R2Z |
Short possession-based entry URL for someone holding the item. |
| User canonical route | GET /api/trackables/profile/alex/LN-7K4V9T |
Canonical API route for a user-owned public trackable. |
| Team canonical route | GET /api/trackables/team/beta-testers/LN-7K4V9T |
Canonical API route for a team-owned public trackable. |
| System short secret code | LN4C8R2Z |
Globally unique across all trackables and also unique against public codes. Intended for manual possession-based entry. |
| Alternate system prefix | GT8M2Q7V |
Same generated short-secret pattern on sister deployments that use the GT prefix. |
| Bring-your-own secret code | ITEM42X |
Advanced external identifier. Must stay unique across all trackables and must not start with LN or GT. |
| Private scan route | https://locationnotes.com/trackable/AB4D5QW2...<100 chars total>... |
Unique across all trackables and intended to come from QR scanning instead of manual entry. |
Generated public-code tokens and generated short-secret bodies use the same smudge-resistant character family. Typed
lookup treats O like 0, I or L like 1,
S like 5, and U like V. For system short-secret codes, that
normalization applies to the generated body after the literal prefix. Public codes use that same configured prefix
with a dash before the generated body, so they are visually distinct from short secret codes.
List and detail reads
GET /api/trackables/publicGET /api/trackables/mineGET /api/trackables/{trackableId}GET /api/trackables/profile/{userName}/{publicCode}GET /api/trackables/team/{teamSlug}/{publicCode}GET /api/trackables/{trackableId}/journey
Create one trackable
POST /api/trackables
Bearer
{
"name": "Promo coin",
"description": "Launch event inventory item",
"teamId": null,
"isPublic": true,
"activateImmediately": false,
"secretCode": ""
}
{
"trackableId": "f3a8f841-20db-4f1e-a3f8-9f14bc0b3c31",
"heading": "Unactivated Trackable",
"description": "Please activate this trackable item",
"items": [
{
"trackableId": "f3a8f841-20db-4f1e-a3f8-9f14bc0b3c31",
"name": "Unactivated Trackable",
"publicCode": "LN-7K4V9T",
"secretCode": "LN4C8R2Z",
"scanUrl": "https://locationnotes.com/trackable/ABCD...<100 characters total>...",
"qrPayload": "https://locationnotes.com/trackable/ABCD...<100 characters total>..."
}
]
}
Bring your own secret code
{
"name": "Marketing token",
"description": "Bring-your-own identifier",
"teamId": null,
"isPublic": false,
"activateImmediately": true,
"secretCode": "TAG42"
}
The server accepts caller-supplied secret codes that do not start with LN or GT and do not match any existing secret or public code. The returned public code is still system-generated.
Create a group
POST /api/trackables/groups
Bearer
{
"name": "Gnomes in this hunt",
"description": "Seasonal hunt inventory",
"defaultTrackableTitle": "",
"defaultTrackableDescription": "",
"trackableCount": 12,
"teamId": null,
"isPublic": true,
"activateImmediately": false
}
Groups are limited to 100 trackables. Unactivated items fall back to the group defaults until activation. Once activated, they must use their own title and description.
Lookup and active session
POST /api/trackables/lookupmatches a public code token or short secret code after normalizing the common smudged-character substitutions listed above.GET /trackable/{code}is the short website entry route. It accepts a public code, a short secret code, or the long QR token and then routes to the correct flow.GET /api/trackables/activelists the client's currently active secret-code sessions.GET /api/trackables/active/{trackableId}returns the active landing payload, including activation requirements and grouped items when available.POST /api/trackables/active/{trackableId}/messageupdates the remembered status text.DELETE /api/trackables/active/{trackableId}ends the remembered secret-code session on that client.
Secret-code lookups and private scan-QR visits create an active client session instead of returning the secret value. That active session is what lets later note flows attach the trackable without re-entering the code.
A bare public-code lookup is safe because short public codes are globally unique. The website also accepts the short direct route forms /trackable/{publicCode}, /trackable/{secretCode}, and /trackable/{qrToken}.
That distinction also applies on note pages: a public code is for opening the public trackable page, while note attachment expects an existing short secret code or an already-active browser session.
GET /api/trackables/lookup?code=LN4C8R2Z
{
"found": true,
"trackableId": "f3a8f841-20db-4f1e-a3f8-9f14bc0b3c31",
"isPublicCodeMatch": false,
"usesSecretAccess": true,
"redirectUrl": "/en-US/trackables/active/f3a8f841-20db-4f1e-a3f8-9f14bc0b3c31"
}
GET /trackable/LN-7K4V9T
GET /api/trackables/profile/alex/LN-7K4V9T
GET /api/trackables/team/beta-testers/LN-7K4V9T
GET /trackable/ABCD...<100 characters total>...
Activate to self or team
POST /api/trackables/{trackableId}/activate
Bearer
{
"name": "Bramble the Mossy Wanderer",
"description": "A small green gnome ready for the next stop.",
"teamId": "optional-team-guid"
}
If teamId is supplied, the trackable becomes team-owned, team admins can manage it, and the activating member keeps control while they remain on that team. Without teamId, the trackable becomes personally owned.
For group-created unactivated items, activation is also the step where the new owner must choose the final item-specific title and description.
Detach and reattach groups
DELETE /api/trackables/{trackableId}/groupremoves the current group association.POST /api/trackables/{trackableId}/groupwith{ "trackableGroupId": "..." }associates a detached trackable with a new group.
A trackable can only belong to one group at a time. The server enforces the detach-first rule. Only the original activator can reattach a detached trackable, and the destination group must also be controlled by that user or an eligible team admin.
{
"trackableGroupId": "4bdffcab-bb51-4fd8-8c15-59f7b2d72c3f"
}
Trackable comments
GET /api/trackables/{trackableId}/commentsPOST /api/trackables/{trackableId}/comments
Clients must be logged in to post comments. Unactivated trackables cannot be commented on.
{
"body": "Starting the route now."
}
Visibility and journey disclosure
Trackable journey pages may show mapped locations even when some underlying notes are private. In those cases, unauthorized viewers can receive the location point but not the protected note content.
Authorized viewers receive the note title, description, and note link directly from the preloaded journey payload and map pin popups.
Deletion and retention
Deleting one account does not automatically delete every trackable that account ever touched. Shared or team-owned trackables can remain while only the deleted user's removable personal activity is removed.
Review the Delete Data page and the account-delete API section for the current retention boundaries.
System and beta metadata
GET /api/system/status
Anonymous
Use this for health checks, Android compatibility gating, and the current beta-page URL.
GET /api/system/beta-android
Anonymous
Returns staged Android beta metadata, including display version, version code, minimum compatible version, and release notes.
curl "https://locationnotes.com/api/system/status"
{
"status": "online",
"utcNow": "2026-03-13T21:15:00Z",
"androidMinimumCompatibleDisplayVersion": "1.0.0-beta.20260313.2",
"androidMinimumCompatibleVersionCode": "2026031302",
"androidCompatibilityMessage": "The API changed after older beta builds were published. Update to the latest beta before syncing or using live team management.",
"androidBetaPageUrl": "https://locationnotes.com/es-US/account/beta"
}
Delete account from the API
Authenticated clients can permanently delete the current account and synced personal data through the API. Read the Delete Data page first if you need the full retention rules for team-owned data and exports.
Trackable cleanup is not all-or-nothing. The server removes the deleted user's personal trackable activity where it can, but it does not remove shared trackables, team-owned trackables, or other users' history just because one account is deleted. If a team-owned trackable still matters to the team, it remains with that team. If another person's activity is still attached to a trackable, that trackable stays in the system.
DELETE /api/account
Bearer
curl -X DELETE "https://locationnotes.com/api/account" \
-H "Authorization: Bearer <access token>"
{
"deletedAccount": true,
"notesDeleted": 14,
"categoriesDeleted": 6,
"linkedProvidersDeleted": 2
}
Errors and problem details
Validation, permission, and lookup failures return standard HTTP status codes. Many workspace routes return RFC 7807 style problem-details JSON with a title, detail, and status.
{
"title": "Request rejected.",
"detail": "Map bounds require minLatitude, minLongitude, maxLatitude, and maxLongitude together.",
"status": 400
}
400for validation or business-rule rejection401for missing or invalid authentication403for authenticated users who do not have access404for missing notes, categories, teams, memberships, or invite links