Privacy options

Because we could not confidently place this visit outside a consent-required region, LocationNotes is holding third-party calls until you confirm what this visit should allow.

Confirm the recommended defaults to move faster, or open the visit choices below if you want to limit maps or browser analytics first.

Recommended default for this visit

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?, and What is an External Link?, and What is an Image? and What is a Support Request? explain the objects these routes operate on. For step-by-step trackable sequences, use the authenticated trackable API flow, anonymous trackable API flow, and trackable error reference pages.

Base URL and conventions

Base URL

All examples on this page assume the current production host:

https://www.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. Register first when the caller does not already have a LocationNotes account. API clients that want a bearer token should then use the identity API login route with cookies turned off, which is the same pattern the Android app uses.

POST /api/auth/register Anonymous

Register request body

{
  "email": "tester@example.com",
  "password": "StrongP@ssw0rd!"
}

Register behavior

A successful register call creates the account but does not replace login. Call the bearer login route next when the client needs an access token for private, sync, team, or manage routes.

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://www.locationnotes.com/api/auth/login?useCookies=false&useSessionCookies=false" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "tester@example.com",
    "password": "StrongP@ssw0rd!"
  }'

The complete route inventory below also lists the current framework-managed identity routes under /api/auth. If you need the full interactive website flow, provider callbacks, account-security pages, or provider-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
  • Responses are capped by the site's public-data exposure limit, which is currently 500 by default, and ordered by recent activity first.

These public map reads include normal Public notes and notes using VisibleOnceAssociatedTrackableAccessed only while they still have no associated trackables. Once a trackable is attached, the note drops out of anonymous public-map discovery until the viewer has unlocked one of the associated trackables on a page-level route.

curl "https://www.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 (default 5), contentLanguage
  • Nearby results are capped by the site's public-data exposure limit, which is currently 500 by default.

The same visibility rule applies here as the bounds endpoint: Public notes are included normally, and VisibleOnceAssociatedTrackableAccessed notes are included only until they gain their first associated trackable.

curl "https://www.locationnotes.com/api/notes/public/nearby?latitude=41.8818&longitude=-87.6231&radiusKm=8&contentLanguage=en-US"

Public profile and team page note streams

These routes back the published map and note strip on public profile and public team pages. They use the same note-access rules as the page itself and accept either a nearby center point or full map bounds.

GET /api/public/profiles/{userName}/notes/nearby Anonymous or Bearer
GET /api/public/teams/{teamName}/notes/nearby Anonymous or Bearer

Required query string: latitude, longitude or full map bounds with minLatitude, minLongitude, maxLatitude, maxLongitude.

Optional query string: radiusKm (default 5), contentLanguage, includeAllLanguages

The website normally sends the current route language as contentLanguage so /en-US and /tlh public pages do not mix authored note languages in the same map or strip by default.

Set includeAllLanguages=true only when the client is intentionally building a multilingual browse surface instead of mirroring the website page.

curl "https://www.locationnotes.com/api/public/profiles/michael-kappel/notes/nearby?minLatitude=41.87&minLongitude=-87.64&maxLatitude=41.89&maxLongitude=-87.62&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",
      "externalLinkUrl": "https://example.com/offline-inspection",
      "externalLinkDescription": "",
      "applyExternalLinkChanges": true,
      "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.

Note read payloads now include lastActivityUtc so clients can show the most recent activity separately from persistence/update timestamps.

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",
  "externalLinkUrl": "https://example.com/field-guide",
  "externalLinkDescription": "",
  "latitude": null,
  "longitude": null,
  "visibility": "VisibleOnceAssociatedTrackableAccessed",
  "commentPolicy": "LoggedInUsers"
}

visibility accepts Private, Public, and VisibleOnceAssociatedTrackableAccessed. The associated-trackable mode behaves like Public until the note has one or more associated trackables. After that, public discovery and note-page access require that the viewer has already unlocked one of those associated trackables.

  • No associated trackables yet: the note behaves like a public note.
  • After the first association: the note drops out of anonymous public discovery and normal public note-page access.
  • Unlocked viewer: someone who already unlocked one associated trackable can open the note page and use the connected public note-page API routes.
  • Owner or authorized team member: keeps normal note access and management rights.

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 note access rules, including private notes and notes gated by associated-trackable 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.

  • Before the first associated trackable exists, these routes behave like normal public note-page routes.
  • After at least one association exists, callers without access to one associated trackable should expect 404 from the public note-page routes even though the note still exists.
  • The same note can still return 200 for the owner, authorized team members, or viewers who already unlocked one associated trackable.

Read categories

GET /api/categories/mine Bearer
GET /api/categories/mine/tree Bearer

Workspace tree helpers

GET /api/categories/mine/tree/sections Bearer
GET /api/categories/mine/tree/children?parentCategoryId={categoryId} Bearer

Use these lighter helper routes for lazy-loaded category pickers and sidebar trees. tree/sections groups root categories by contentLanguage. tree/children returns the direct child nodes for one parent and includes childCategoryCount so the client can decide whether to show an expand affordance before loading grandchildren.

[
  {
    "contentLanguage": "en-US",
    "rootCategories": [
      {
        "categoryId": "68cb4c9b-d9bd-4bde-8c6a-a03a4c70b283",
        "name": "Field Reports",
        "contentLanguage": "en-US",
        "parentCategoryId": null,
        "childCategoryCount": 2
      }
    ]
  }
]
[
  {
    "categoryId": "c4b2e65f-5cf2-4ab6-8577-a89dfb51407f",
    "name": "Dock checks",
    "contentLanguage": "en-US",
    "parentCategoryId": "68cb4c9b-d9bd-4bde-8c6a-a03a4c70b283",
    "childCategoryCount": 0
  }
]

Create or move categories

POST /api/categories/mine Bearer
POST /api/categories/mine/{categoryId}/move Bearer
{
  "name": "Field Reports",
  "contentLanguage": "en-US",
  "parentCategoryId": null
}

Personal GPX waypoint handoff

GET /api/notes/mine/gpx Bearer

GET /api/notes/mine/gpx exports only mapped personal notes with saved coordinates as GPX 1.1 waypoints.

POST /api/notes/mine/gpx Bearer

POST /api/notes/mine/gpx accepts multipart form data with file, visibility, and optional contentLanguage. The import reads waypoint entries only, creates normal mapped notes, and never overwrites an existing note.

Duplicate checks stay inside the selected personal scope. Matching title plus coordinates with the same note text are reported as duplicates. Matching title plus coordinates with different note text are skipped to avoid overwrite.

curl "https://www.locationnotes.com/api/notes/mine/gpx" \
  -H "Authorization: Bearer <access token>"
curl -X POST "https://www.locationnotes.com/api/notes/mine/gpx" \
  -H "Authorization: Bearer <access token>" \
  -F "file=@waypoints.gpx;type=application/gpx+xml" \
  -F "visibility=Private" \
  -F "contentLanguage=en-US"

What is Import and Export? covers how GPX waypoint exchange differs from the full JSON and portable ZIP account exports.

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/teams
  • GET /api/teams/{teamId}/notes
  • GET /api/teams/{teamId}/categories
  • GET /api/teams/{teamId}/categories/tree
  • GET /api/teams/{teamId}/invite-links

Team and team-note read models include lastActivityUtc. Public trimming prefers the most recently active records first.

Team category tree helpers

GET /api/teams/{teamId}/categories/tree/sections Bearer
GET /api/teams/{teamId}/categories/tree/children?parentCategoryId={categoryId} Bearer

These routes use the same section and child-node shapes as the personal workspace tree helpers, but the data stays inside the selected team scope and still requires active team membership.

Use DELETE /api/teams/{teamId}/notes/{noteId} when the note should leave the team workspace but continue existing for the owner, and use DELETE /api/teams/{teamId}/notes/{noteId}/delete when the team note itself should be permanently deleted.

Create a team

{
  "name": "Beta Testers",
  "title": "Beta Testers",
  "description": "Public beta release gate for Android validation.",
  "externalLinkUrl": "https://example.com/beta-program",
  "externalLinkDescription": "",
  "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",
  "externalLinkUrl": "https://example.com/shared-briefing",
  "externalLinkDescription": "",
  "latitude": 41.8818,
  "longitude": -87.6231,
  "visibility": "Private",
  "commentPolicy": "TeamMembers"
}

Team GPX waypoint handoff

GET /api/teams/{teamId}/notes/gpx Bearer
POST /api/teams/{teamId}/notes/gpx Bearer

GET /api/teams/{teamId}/notes/gpx and POST /api/teams/{teamId}/notes/gpx apply the same GPX rules inside the selected team scope. Duplicate and overwrite checks only compare against that team's notes.

curl "https://www.locationnotes.com/api/teams/{teamId}/notes/gpx" \
  -H "Authorization: Bearer <access token>"
curl -X POST "https://www.locationnotes.com/api/teams/{teamId}/notes/gpx" \
  -H "Authorization: Bearer <access token>" \
  -F "file=@team-waypoints.gpx;type=application/gpx+xml" \
  -F "visibility=Public" \
  -F "contentLanguage=en-US"

Membership and invite operations

  • POST /api/teams/{teamId}/memberships/request
  • POST /api/teams/{teamId}/memberships/invite
  • POST /api/teams/{teamId}/memberships/{membershipId}/accept
  • POST /api/teams/{teamId}/memberships/{membershipId}/refuse
  • POST /api/teams/{teamId}/memberships/{membershipId}/approve
  • POST /api/teams/{teamId}/memberships/{membershipId}/deny
  • POST /api/teams/{teamId}/memberships/{membershipId}/promote-admin
  • DELETE /api/teams/{teamId}/memberships/{membershipId}
  • POST /api/teams/invite-links/{teamSlug}/{inviteCode}/join
{
  "isSingleUse": false
}
{
  "inviteLinkId": "9a4726dc-4fb1-4c16-b7b3-5d48ea68fdce",
  "teamId": "0f4ddf5d-f3dc-4417-b96d-8e212d24235e",
  "teamName": "Invite Team",
  "teamSlug": "invite-team",
  "code": "8K4V9T",
  "createdByDisplayName": "invite-admin",
  "createdByCurrentUser": true,
  "inviterIsCurrentlyAdmin": true,
  "isSingleUse": false,
  "createdUtc": "2026-03-18T21:15:00Z"
}
{
  "teamSlug": "invite-team",
  "membershipStatus": "RequestingMembership"
}

Trackable endpoints

Trackables support public-code browsing, secret-code or scan-only QR access, one-time secret reveal during creation, browser-side active sessions, comments, and grouped inventory workflows. Secret short codes and scan-only QR URLs are only returned from the create endpoints below. They are never returned again from read, lookup, comment, or detail routes.

Important trackable rules:
  • 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 scan-only QR URLs are possession-based access credentials.
  • Think of trackables and trackable groups as Visible Once Accessed or Always Visible To Everyone. Signed-in secret access can be saved to the account for later devices.
  • 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 share URL. The same base route is used for public codes, short secret codes, and long scan-only QR tokens.
Localized public trackable page GET /en-US/trackables/LN-7K4V9T Language-specific rendered public page reached after the share URL resolves the code.
Secret entry route GET /trackable/LN4C8R2Z Short possession-based entry URL for someone holding the item. It uses the same <code>/trackable/{code}</code> base route as the public and long scan forms.
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 <code>GT</code> prefix.
Bring-your-own secret code ITEM42X Advanced external identifier. Must stay unique across all trackables and must not start with <code>LN</code> or <code>GT</code>.
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

Public list reads are capped by the site's public-data exposure limit, which is currently 500 by default.

Trackable and trackable-group read models include lastActivityUtc so dashboards and API clients can show recent activity explicitly.

  • GET /api/trackables/public
  • GET /api/trackables/mine
  • GET /api/trackables/{trackableId}
  • POST /api/trackables/{trackableId}/watch
  • DELETE /api/trackables/{trackableId}/watch
  • GET /api/trackables/{trackableId}/journey
  • POST /api/trackables/{trackableId}/journey-stops
  • DELETE /api/trackables/{trackableId}/journey-stops/{journeyStopId}

GET /api/trackables/public and the website's public trackable browse pages stay multilingual so public journey and logistics data are not hidden by route language.

Switch the website language when you want localized chrome around the same public trackable data.

Create one trackable

POST /api/trackables Bearer
{
  "name": "Promo coin",
  "description": "Launch event inventory item",
  "externalLinkUrl": "https://example.com/promo-coin",
  "externalLinkDescription": "",
  "teamId": null,
  "visibility": "AlwaysVisibleToEveryone",
  "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": "ABCD...<100 characters total>..."
    }
  ]
}

Bring your own secret code

{
  "name": "Marketing token",
  "description": "Bring-your-own identifier",
  "teamId": null,
  "visibility": "VisibleOnceAccessed",
  "activateImmediately": true,
  "secretCode": "TAG42"
}

The server accepts caller-supplied secret codes that do not start with LN, GT, or GC 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": "",
  "defaultExternalLinkUrl": "https://example.com/hunt-rules",
  "defaultExternalLinkDescription": "",
  "trackableCount": 12,
  "teamId": null,
  "visibility": "AlwaysVisibleToEveryone",
  "activateImmediately": false,
  "watchGroupActivityWhenCreated": true
}

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.

Trackable-group creation can also accept watchGroupActivityWhenCreated when an unactivated batch should start on the creator's watch list.

Lookup and active session

  • POST /api/trackables/lookup matches 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 shown to users and API clients. It accepts a public code, a short secret code, or the long QR token and then routes to the correct flow.
  • GET /api/trackables/active lists the client's current active secret-code session when one exists.
  • GET /api/trackables/active/{trackableId} returns the active landing payload, including activation requirements and grouped items when available.
  • POST /api/trackables/active/{trackableId}/message updates the remembered status text.
  • DELETE /api/trackables/active/{trackableId} ends the remembered secret-code session on that client.

Secret-code lookups and scan-only 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.

When the caller is authenticated, the same Visible Once Accessed lookup also links the trackable to that account so later signed-in devices can reopen the trackable, its group page, and normal follow-up routes without re-entering the secret.

The website intentionally uses the same direct entry base route for all three forms: /trackable/{publicCode}, /trackable/{secretCode}, and /trackable/{qrToken}.

The localized /{lang}/trackable/{code} route is the rendered single-item landing after automatic language selection, but it still should not be shown as the shareable or printed URL.

After a public-code lookup resolves, the rendered public page lives at /{lang}/trackable/{publicCode}.

That shared entry route stays valid even if the configured short-code lengths change later, because the server resolves the code value instead of depending on hard-coded website path variations.

Owner-scoped website trackable routes are intentionally not part of the public URL system.

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 /en-US/trackables/LN-7K4V9T
GET /trackable/ABCD...<100 characters total>...

Activate to self or team

POST /api/trackables/{trackableId}/activate Bearer
{
  "name": "",
  "description": "",
  "useGroupDefaultTitle": true,
  "useGroupDefaultDescription": true,
  "externalLinkUrl": "",
  "externalLinkDescription": "",
  "useGroupDefaultExternalLink": true,
  "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.

When a grouped item is activated, the owner can either save an explicit item title and description or intentionally copy the group's current default title and description into the item.

Detach and reattach groups

  • DELETE /api/trackables/{trackableId}/group removes the current group association.
  • POST /api/trackables/{trackableId}/group with { "trackableGroupId": "..." } associates a detached trackable with a new group.
  • POST /api/trackables/groups/{trackableGroupId}/watch starts monitoring the group's visible members. Existing member-item watches from that same group can still collapse into the group watch, and owner-or-watch overlap is deduplicated per user.
  • DELETE /api/trackables/groups/{trackableGroupId}/watch stops monitoring the 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}/comments
  • POST /api/trackables/{trackableId}/comments
  • PUT /api/trackables/{trackableId}/comments/{commentId}
  • DELETE /api/trackables/{trackableId}/comments/{commentId}

Unactivated trackables cannot receive comments or journey stops.

Authenticated users can post comments directly. Anonymous callers can also post, but every anonymous write must come from that browser's active trackable session or resend the exact short secret code or private QR token for that specific trackable.

Only the signed-in comment author can edit their own comment. Trackable owners and current team admins can delete comments or journey stops, but they still cannot rewrite somebody else&apos;s words.

{
  "body": "Starting the route now.",
  "accessCode": "LN4C8R2Z"
}

Direct journey stops

The journey feed is now an immutable stop history. Note-backed stops snapshot the location when the note is attached, and direct map reports can be saved without creating a note first.

If a linked note later moves to a different coordinate, the trackable journey still keeps the original snapped stop so logistics history does not silently rewrite itself.

Anonymous direct reports follow the same rule as anonymous comments: the caller must use that browser's active trackable session or resend the short secret code or private QR token on the write request.

{
  "latitude": 41.8819,
  "longitude": -87.6278,
  "accessCode": "LN4C8R2Z"
}

GET /api/trackables/{trackableId}/journey returns label-first place facts. Each point carries coordinateId, locationLabel, and currentNotesAtCoordinate so clients can show the current visible notes at that coordinate without treating the stop as if it owns one saved note.

Journey responses do not split the outward place contract into city, stateOrProvince, and country fields anymore. Read locationLabel plus the coordinates instead.

[
  {
    "journeyStopId": "8a274ad6-5dd4-45c4-969a-c13cc1b8d92c",
    "coordinateId": "565c42dd-2e12-49b4-a16b-c89ff4502b8e",
    "latitude": 41.8819,
    "longitude": -87.6278,
    "associatedUtc": "2026-04-05T18:30:00Z",
    "isLocationOnly": false,
    "isAnonymous": false,
    "canDelete": false,
    "canConvertToNote": false,
    "locationLabel": "Chicago, Illinois, United States",
    "creatorDisplayText": "Jordan",
    "creatorProfileUserName": "jordan",
    "currentNotesAtCoordinate": [
      {
        "noteId": "4d6c5df3-3c53-4d0e-8e72-7d98a0f8a9f3",
        "title": "Lobby drop",
        "contentLanguage": "en-US",
        "thumbnailUrl": "/api/images/notes/4d6c5df3-3c53-4d0e-8e72-7d98a0f8a9f3/thumbnail"
      }
    ]
  }
]

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&apos;s removable personal activity is removed.

Review the Delete Data page and the account-delete API section for the current retention boundaries.

Trackable flow guides

The trackable API has both a full signed-in ownership workflow and a lighter anonymous secret-backed reporting workflow. Use the dedicated pages below when you need ordered call sequences, decision points, and copy-paste examples instead of a route catalog.

Authenticated flow

From found-item lookup through browser secret access, sign-in, activation, note linking, direct journey logging, moderation, and export/deletion considerations.

Open the authenticated trackable API flow guide

Anonymous flow

For callers who will not sign in but do have the exact secret code or private QR token and need to post a direct location report or comment safely.

Open the anonymous trackable API flow guide

Error lookup

Trackable problem-details responses now include a stable machine-readable code field. Use the error reference page to map failures to likely causes and fixes.

Open the trackable API error reference

Image endpoints

Image visibility always follows the parent item. If the caller can open the connected profile, note, team, trackable, or trackable-group page, the caller can open its images too. If the parent item is not accessible, image list reads return no items and direct image downloads return 404.

Clients should also remember that uploads are screened before save, originals are not retained, and the server stores only resized JPEG variants. The website and Android app both use these same API contracts and file routes.

List images by parent

GET /api/images/profiles/{userId} Anonymous or Bearer
GET /api/images/notes/{noteId} Anonymous or Bearer
GET /api/images/teams/{teamId} Anonymous or Bearer
GET /api/images/trackables/{trackableId} Anonymous or Bearer
GET /api/images/trackable-groups/{trackableGroupId} Anonymous or Bearer

Use these reads to populate galleries. Each item includes the image GUID, original dimensions, and the relative URLs for the thumbnail, small, medium, and large stored copies.

originalWidth and originalHeight describe the uploaded source image for reference. The actual downloadable files are still the resized stored variants linked by thumbnailUrl, smallUrl, mediumUrl, and largeUrl.

curl "https://www.locationnotes.com/api/images/notes/4d6c5df3-3c53-4d0e-8e72-7d98a0f8a9f3"
[
  {
    "contentImageId": "f1d52aa2-4d59-49bf-8d21-7d0b4e9e57f1",
    "targetType": 1,
    "noteId": "4d6c5df3-3c53-4d0e-8e72-7d98a0f8a9f3",
    "originalWidth": 1600,
    "originalHeight": 1200,
    "createdUtc": "2026-03-18T21:15:00Z",
    "thumbnailUrl": "/Images/Thumbnail/f1d52aa2-4d59-49bf-8d21-7d0b4e9e57f1-64.jpg",
    "smallUrl": "/Images/Small/f1d52aa2-4d59-49bf-8d21-7d0b4e9e57f1-306.jpg",
    "mediumUrl": "/Images/Medium/f1d52aa2-4d59-49bf-8d21-7d0b4e9e57f1-612.jpg",
    "largeUrl": "/Images/Large/f1d52aa2-4d59-49bf-8d21-7d0b4e9e57f1-1024.jpg"
  }
]

Download stored bytes

GET /api/images/{contentImageId}/{variant} Anonymous or Bearer

Valid variant values are thumbnail, small, medium, and large. Responses return image/jpeg.

The website file URLs under /Images/{Variant}/{guid}-{size}.jpg enforce the same parent-visibility rule before image bytes are served, so direct image links are still protected by the connected profile, note, team, trackable, or trackable-group permissions.

curl -L "https://www.locationnotes.com/api/images/f1d52aa2-4d59-49bf-8d21-7d0b4e9e57f1/large" --output note-large.jpg

Upload images

POST /api/images/profiles Bearer
POST /api/images/notes/{noteId} Bearer
POST /api/images/teams/{teamId} Bearer
POST /api/images/trackables/{trackableId} Bearer
POST /api/images/trackable-groups/{trackableGroupId} Bearer

Send one multipart file field named file. The caller must already have parent-management permission for the target, which means team-admin access for team pages and the normal edit/management permission for the other parent types. Uploads are screened before save, then stored as resized JPEG copies only.

curl -X POST "https://www.locationnotes.com/api/images/trackables/721f5205-ed2c-43e8-8ecd-1502d5bb7b56" \
  -H "Authorization: Bearer <access token>" \
  -F "file=@tracker.jpg"
{
  "image": {
    "contentImageId": "f1d52aa2-4d59-49bf-8d21-7d0b4e9e57f1",
    "targetType": 3,
    "trackableId": "721f5205-ed2c-43e8-8ecd-1502d5bb7b56",
    "originalWidth": 1600,
    "originalHeight": 1200,
    "createdUtc": "2026-03-18T21:15:00Z",
    "thumbnailUrl": "/Images/Thumbnail/f1d52aa2-4d59-49bf-8d21-7d0b4e9e57f1-64.jpg",
    "smallUrl": "/Images/Small/f1d52aa2-4d59-49bf-8d21-7d0b4e9e57f1-306.jpg",
    "mediumUrl": "/Images/Medium/f1d52aa2-4d59-49bf-8d21-7d0b4e9e57f1-612.jpg",
    "largeUrl": "/Images/Large/f1d52aa2-4d59-49bf-8d21-7d0b4e9e57f1-1024.jpg"
  }
}

Delete images

DELETE /api/images/{contentImageId} Bearer

Deletion requires the same parent-management permission that upload requires. When a delete succeeds, the content-image row and all stored resized variants are removed together.

API clients must send the HTTP DELETE verb here. Opening this URL directly in a browser address bar sends GET instead, which returns 405 Method Not Allowed because this route is not a download page.

Website delete buttons use the localized website form route at /{culture}/images/{contentImageId}/delete so browser gallery deletes keep anti-forgery protection.

curl -X DELETE "https://www.locationnotes.com/api/images/f1d52aa2-4d59-49bf-8d21-7d0b4e9e57f1" \
  -H "Authorization: Bearer <access token>"

What is an Image? explains the visibility, moderation, reporting, resize, export, and deletion rules behind these routes.

Compliance reporting

Use these routes for inappropriate-content reports, tracked system errors, and the same support-ticket records that back the website support-request flow. Anonymous callers may submit reports, but only signed-in reporters can later track status updates and super-admin resolutions through the API.

Submit a report

POST /api/compliance/reports Anonymous or Bearer

If the caller is authenticated by website cookie or bearer token, the created report is attached to that account so it appears under GET /api/compliance/reports/mine.

{
  "pageType": 2,
  "contentType": 5,
  "pageTitle": "Reported note title",
  "pageUrl": "/en-US/Note/2f4a9f80-b7db-4f4b-9d34-0c2cb8777d9a#note-page-title",
  "pageReference": "2f4a9f80-b7db-4f4b-9d34-0c2cb8777d9a",
  "contentLabel": "Note Page title and content",
  "contentReference": "2f4a9f80-b7db-4f4b-9d34-0c2cb8777d9a:page-content",
  "contentPreview": "Title: Reported note title\n\nContent: Reported note body",
  "reportTitle": "Needs review",
  "reportExplanation": "This title is not appropriate for a public note."
}

Track an API ticket or report an app error

POST /api/compliance/errors Anonymous or Bearer

If an API route already failed with an unexpected 500 and returned a ticketNumber in problem-details JSON, post that ticket here instead of creating a second error log row. Include userExplanation and set trackInSupportTickets when the caller wants super-admin follow-up.

{
  "ticketNumber": "ERR-1M7Q4D9K2X5R8V6N",
  "userExplanation": "Android app hit this right after I tapped refresh twice.",
  "trackInSupportTickets": true,
  "clientContext": {
    "platform": "Android",
    "screen": "My Journeys",
    "appVersion": "1.0.0-beta.20260318.1"
  }
}

When the failure happened only inside the Android app and there is no server ticket yet, create a new error ticket with a marker that describes where the app was.

{
  "pageMarkerType": "Android screen",
  "pageMarker": "My Journeys",
  "requestCulture": "en-US",
  "requestUrl": "https://locationnotes.com/api/trackables/active/721f5205-ed2c-43e8-8ecd-1502d5bb7b56?includeHistory=true",
  "httpMethod": "GET",
  "responseStatusCode": 503,
  "exceptionType": "Java.Lang.IllegalStateException",
  "exceptionMessage": "Journey list render failed.",
  "stackTrace": "at com.locationnotes.trackables.MyJourneysFragment.render(MyJourneysFragment.kt:42)",
  "userExplanation": "This happened right after scanning a QR code.",
  "trackInSupportTickets": true,
  "clientContext": {
    "platform": "Android",
    "deviceModel": "Pixel 9",
    "osVersion": "Android 17",
    "appVersion": "1.0.0-beta.20260318.1"
  }
}
{
  "ticketNumber": "ERR-1M7Q4D9K2X5R8V6N",
  "usedExistingTicket": false,
  "explanationSaved": true,
  "trackedInSupportTickets": true,
  "trackedContentReportId": "d91f6e1c-b8e2-4e38-9e96-f2de0cc4f0e2"
}

Use pageMarkerType values such as URL, Android screen, or Android background task. For server-generated tickets, keep the original URL marker and send the returned ticketNumber back here so the logged API URL stays attached to the same ticket.

Reporter status reads

GET /api/compliance/reports/mine Bearer
GET /api/compliance/reports/{contentReportId} Bearer

These routes return only the caller's own reports unless the caller is a super-admin. Use them to show Reported, Reviewing, or Resolved plus any final resolution note for both content reports and tracked system errors.

{
  "contentReportId": "3ab419ab-4b71-4d43-b52c-303d6039f01f",
  "reporterUserId": "6d650c55-b235-4370-8572-e4b772cd1aea",
  "pageType": 2,
  "pageTypeLabel": "Note Page",
  "contentType": 5,
  "contentTypeLabel": "Title and content",
  "status": 2,
  "statusLabel": "Resolved",
  "pageTitle": "Reported note title",
  "pageUrl": "/en-US/Note/2f4a9f80-b7db-4f4b-9d34-0c2cb8777d9a#note-page-title",
  "pageReference": "2f4a9f80-b7db-4f4b-9d34-0c2cb8777d9a",
  "contentLabel": "Note Page title and content",
  "contentReference": "2f4a9f80-b7db-4f4b-9d34-0c2cb8777d9a:page-content",
  "contentPreview": "Title: Reported note title\n\nContent: Reported note body",
  "reportTitle": "Needs review",
  "reportExplanation": "This title is not appropriate for a public note.",
  "resolutionText": "Reviewed and recorded for moderation follow-up.",
  "reporterDisplayName": "site-compliance-reporter",
  "reviewerDisplayName": "site-compliance-admin",
  "createdUtc": "2026-03-18T18:00:00Z",
  "reviewedUtc": "2026-03-18T18:10:00Z"
}

Super-admin review

GET /api/compliance/reports Bearer + SuperAdmin
PUT /api/compliance/reports/{contentReportId} Bearer + SuperAdmin

The admin list includes the report link, page metadata, offending-content metadata, reporter identity when available, and any previous review note. Updates write the current review state and visible resolution text.

{
  "status": 1,
  "resolutionText": "Review opened and queued for follow-up."
}

Set status to 2 and include a non-empty resolutionText when closing a report as resolved.

Enum values

  • pageType: 0 profile page, 1 team page, 2 note page, 3 trackable page, 4 trackable group page, 5 system error, 6 support page
  • contentType: 0 title, 1 description, 2 comment, 3 body, 4 bio, 5 title and content, 6 error details, 7 image, 8 support request
  • status: 0 reported, 1 reviewing, 2 resolved

Website page-level reports now use 5 so one report button can cover both the visible title and the visible description/body together. Comment-level reports still use the comment-specific content types.

Website general support requests create Support Page plus Support request tickets through the same COMPLIANCE.ContentReports store, even though the browser form uses the Support page instead of this raw API route.

Tracked website, API, and Android app errors are stored in LOG.Errors. When trackInSupportTickets is enabled, the linked support ticket also appears in COMPLIANCE.ContentReports so super-admin review and reporter status follow the same workflow.

System and beta metadata

GET /api/system/status Anonymous

Use this for health checks, Android compatibility gating, the current beta-page URL, and the resolved map/privacy state for the current request.

GET /api/system/beta-android Anonymous

Returns staged Android beta metadata, including display version, version code, minimum compatible version, and release notes.

GET /api/system/ip-location Anonymous

Returns the current request's best-effort IP-based location for map centering and troubleshooting. When the client IP is private, local, or otherwise untrusted, the response stays HTTP 200 and explains the failure in failureReason instead of throwing a transport error.

GET /api/system/coordinate-locality Anonymous

Reverse-geocodes a latitude/longitude pair into city, state/province, and country labels for stop-planner and map-selection flows. Invalid coordinates return a non-throwing payload with resolved=false and failureReason=invalid-coordinates.

  • requestedExperienceMode versus effectiveExperienceMode tells you whether the signed-in preference survived request-time privacy rules or was forced down to no_3rd_parties.
  • requestedMapSource, preferredMapSource, and fallbackMapSource tell you which provider the user asked for, which provider the server wants to try first, and which provider should load next if the preferred source fails.
  • thirdPartyBrowserCallsAllowed, googleMapsAllowed, and openStreetMapAllowed tell you whether this request may call browser-side providers at all.
  • hostedMapsForcedByPrivacy plus hostedMapTileUrlTemplate tell you when the request was forced onto same-origin hosted tiles, which currently render through /maps/tiles/{z}/{x}/{y}.png.

Private-network, local-network, and otherwise unresolved IP requests intentionally take the stricter path, so a localhost or office-network test can legitimately show no_3rd_parties and hosted_maps even when the signed-in preference was different.

curl "https://www.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://www.locationnotes.com/en-US/account/beta",
  "requestedExperienceMode": "latest_and_greatest",
  "effectiveExperienceMode": "no_3rd_parties",
  "usesSavedExperienceModePreference": false,
  "usesVisitorExperienceModePreferenceCookie": false,
  "thirdPartyBrowserCallsAllowed": false,
  "requestedMapSource": "google_maps",
  "preferredMapSource": "hosted_maps",
  "fallbackMapSource": "hosted_maps",
  "usesSavedMapPreference": false,
  "usesVisitorMapPreferenceCookie": false,
  "googleMapsAllowed": false,
  "googleMapsConfigured": false,
  "openStreetMapAllowed": false,
  "openStreetMapConfigured": true,
  "hostedMapsConfigured": true,
  "hostedMapsForcedByPrivacy": true,
  "hostedMapTileUrlTemplate": "/maps/tiles/{z}/{x}/{y}.png"
}
curl "https://www.locationnotes.com/api/system/ip-location"
{
  "resolved": true,
  "clientIpAddress": "50.77.187.28",
  "lookupIpAddress": "50.77.187.28",
  "usedDevelopmentFallbackIp": false,
  "sourceKind": "IpAddress",
  "coordinateSourceProvider": "IpInfoDb",
  "localitySourceProvider": "IpInfoDb",
  "latitude": 41.8758,
  "longitude": -87.6206,
  "city": "Chicago",
  "stateOrProvince": "Illinois",
  "country": "United States of America",
  "isApproximate": true,
  "accuracyRadiusKm": null,
  "failureReason": ""
}
curl "https://www.locationnotes.com/api/system/coordinate-locality?latitude=41.8818&longitude=-87.6231"
{
  "resolved": true,
  "sourceKind": "Coordinates",
  "coordinateSourceProvider": "StopPlanner",
  "localitySourceProvider": "HostedMapPostGIS",
  "latitude": 41.8818,
  "longitude": -87.6231,
  "city": "Chicago",
  "stateOrProvince": "Illinois",
  "country": "United States",
  "isApproximate": false,
  "accuracyRadiusKm": null,
  "failureReason": ""
}

Complete route inventory

This section is the exhaustive verb-and-path checklist for the current LocationNotes API surface, including the framework-managed identity routes under /api/auth. The detailed sections above explain the main workflows; this inventory is the route-level source of truth that should stay aligned with the live endpoint table.

Identity auth and account security

  • POST /api/auth/register Anonymous
  • POST /api/auth/login Anonymous
  • POST /api/auth/refresh Anonymous
  • GET /api/auth/confirmEmail Anonymous
  • POST /api/auth/resendConfirmationEmail Anonymous
  • POST /api/auth/forgotPassword Anonymous
  • POST /api/auth/resetPassword Anonymous
  • GET /api/auth/manage/info Bearer
  • POST /api/auth/manage/info Bearer
  • POST /api/auth/manage/2fa Bearer

Account, system, sync, and external links

  • DELETE /api/account Bearer
  • GET /api/system/status Anonymous
  • GET /api/system/beta-android Anonymous
  • GET /api/system/ip-location Anonymous
  • GET /api/system/coordinate-locality Anonymous
  • POST /api/sync/push Bearer
  • POST /api/sync/pull Bearer
  • POST /api/external-links/verify Bearer

Notes and public note pages

  • GET /api/notes/public/bounds Anonymous
  • GET /api/notes/public/nearby Anonymous
  • GET /api/notes/mine Bearer
  • POST /api/notes/mine Bearer
  • GET /api/notes/mine/gpx Bearer
  • POST /api/notes/mine/gpx Bearer
  • POST /api/notes/mine/{noteId}/move Bearer
  • DELETE /api/notes/mine/{noteId} Bearer
  • GET /api/public/profiles/{userName}/notes/nearby Anonymous or Bearer
  • GET /api/public/teams/{teamName}/notes/nearby Anonymous or Bearer
  • 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

Categories and workspace trees

  • GET /api/categories/mine Bearer
  • GET /api/categories/mine/tree Bearer
  • GET /api/categories/mine/tree/sections Bearer
  • GET /api/categories/mine/tree/children Bearer
  • POST /api/categories/mine Bearer
  • POST /api/categories/mine/{categoryId}/move Bearer
  • DELETE /api/categories/mine/{categoryId} Bearer

Teams, memberships, and invite links

  • GET /api/teams Bearer
  • POST /api/teams Bearer
  • PUT /api/teams/{teamId}/settings Bearer
  • GET /api/teams/{teamId}/notes Bearer
  • GET /api/teams/{teamId}/notes/gpx Bearer
  • DELETE /api/teams/{teamId}/notes/{noteId} Bearer
  • POST /api/teams/{teamId}/notes Bearer
  • POST /api/teams/{teamId}/notes/gpx Bearer
  • POST /api/teams/{teamId}/notes/{noteId}/move Bearer
  • DELETE /api/teams/{teamId}/notes/{noteId}/delete Bearer
  • GET /api/teams/{teamId}/categories Bearer
  • GET /api/teams/{teamId}/categories/tree Bearer
  • GET /api/teams/{teamId}/categories/tree/sections Bearer
  • GET /api/teams/{teamId}/categories/tree/children Bearer
  • POST /api/teams/{teamId}/categories Bearer
  • POST /api/teams/{teamId}/categories/{categoryId}/move Bearer
  • DELETE /api/teams/{teamId}/categories/{categoryId} Bearer
  • POST /api/teams/{teamId}/memberships/request Bearer
  • POST /api/teams/{teamId}/memberships/invite Bearer
  • POST /api/teams/{teamId}/memberships/{membershipId}/accept Bearer
  • POST /api/teams/{teamId}/memberships/{membershipId}/refuse Bearer
  • POST /api/teams/{teamId}/memberships/{membershipId}/approve Bearer
  • POST /api/teams/{teamId}/memberships/{membershipId}/deny Bearer
  • POST /api/teams/{teamId}/memberships/{membershipId}/promote-admin Bearer
  • DELETE /api/teams/{teamId}/memberships/{membershipId} Bearer
  • GET /api/teams/{teamId}/invite-links Bearer
  • POST /api/teams/{teamId}/invite-links Bearer
  • DELETE /api/teams/{teamId}/invite-links/{inviteLinkId} Bearer
  • POST /api/teams/invite-links/{teamSlug}/{inviteCode}/join Bearer
  • DELETE /api/teams/{teamId} Bearer

Trackables and active secret-backed sessions

  • GET /api/trackables/public Anonymous or Bearer
  • GET /api/trackables/mine Bearer
  • GET /api/trackables/{trackableId} Anonymous or Bearer
  • GET /api/trackables/{trackableId}/journey Anonymous or Bearer
  • POST /api/trackables Bearer
  • POST /api/trackables/groups Bearer
  • POST /api/trackables/{trackableId}/activate Bearer
  • POST /api/trackables/{trackableId}/watch Bearer
  • DELETE /api/trackables/{trackableId}/watch Bearer
  • DELETE /api/trackables/{trackableId}/group Bearer
  • POST /api/trackables/{trackableId}/group Bearer
  • POST /api/trackables/groups/{trackableGroupId}/watch Bearer
  • DELETE /api/trackables/groups/{trackableGroupId}/watch Bearer
  • GET /api/trackables/lookup Anonymous
  • POST /api/trackables/lookup Anonymous
  • GET /api/trackables/active Anonymous
  • GET /api/trackables/active/{trackableId} Anonymous
  • POST /api/trackables/active/{trackableId}/message Anonymous
  • DELETE /api/trackables/active/{trackableId} Anonymous
  • GET /api/trackables/{trackableId}/comments Anonymous or Bearer
  • POST /api/trackables/{trackableId}/comments Anonymous or Bearer
  • PUT /api/trackables/{trackableId}/comments/{commentId} Bearer
  • DELETE /api/trackables/{trackableId}/comments/{commentId} Bearer
  • POST /api/trackables/{trackableId}/journey-stops Anonymous or Bearer
  • DELETE /api/trackables/{trackableId}/journey-stops/{journeyStopId} Bearer

Images and compliance

  • GET /api/images/profiles/{userId} Anonymous or Bearer
  • GET /api/images/notes/{noteId} Anonymous or Bearer
  • GET /api/images/teams/{teamId} Anonymous or Bearer
  • GET /api/images/trackables/{trackableId} Anonymous or Bearer
  • GET /api/images/trackable-groups/{trackableGroupId} Anonymous or Bearer
  • GET /api/images/{contentImageId}/{variant} Anonymous or Bearer
  • POST /api/images/profiles Bearer
  • POST /api/images/notes/{noteId} Bearer
  • POST /api/images/teams/{teamId} Bearer
  • POST /api/images/trackables/{trackableId} Bearer
  • POST /api/images/trackable-groups/{trackableGroupId} Bearer
  • DELETE /api/images/{contentImageId} Bearer
  • POST /api/compliance/reports Anonymous or Bearer
  • POST /api/compliance/errors Anonymous or Bearer
  • GET /api/compliance/reports/mine Bearer
  • GET /api/compliance/reports/{contentReportId} Bearer
  • GET /api/compliance/reports Bearer + SuperAdmin
  • PUT /api/compliance/reports/{contentReportId} Bearer + SuperAdmin

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://www.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": "Forbidden",
  "code": "trackable_access_code_required",
  "detail": "Sign in, keep this trackable active on this browser, or provide this trackable's secret code or QR access code before posting comments or location reports.",
  "status": 403
}
  • 400 for validation or business-rule rejection
  • 401 for missing or invalid authentication
  • 403 for authenticated users who do not have access
  • 404 for missing notes, categories, teams, memberships, or invite links

Trackable write routes also add a stable code property so API clients can distinguish cases like trackable_access_code_required, trackable_access_code_invalid, trackable_activation_required, and trackable_already_activated without parsing English text. The full mapping is documented on the trackable error reference page.