Utility Endpoints

A small group of utility routes used by the web app and supporting services. Most are public (no auth) and intended for narrow use cases.

Endpoint table

MethodPathAuthDescription
GET/api/tags/trendingSession or BearerMost-used tags across public messages within a trailing time window.
GET/api/tags/autocompleteSession or BearerTags on public messages matching a case-insensitive prefix.
GET/api/locationPublicResolve latitude/longitude to a US city, state, and timezone via NOAA.
GET/api/weatherPublicUS weather data from NOAA for a given lat/lon. Optional hourly and weekly series.
GET/api/images/proxyPublicServer-side image proxy for Instagram CDN images (CORS workaround).
POST/api/analytics/ingestSession optionalFirst-party page-view and action beacon used by the web app.
GET/api/oauth/client-metadataPublicBluesky AT Protocol relying-party client metadata. Not for direct calls.
GET/api/auth/linkedin/statusPublicWhether LinkedIn OAuth is configured on this deployment.
GET/api/auth/twitter/statusPublicWhether X (Twitter) OAuth is configured on this deployment.
GET/api/test-dbPublicDev diagnostic that asserts the database is reachable. Do not depend on this in production.

Tag discovery

Two authenticated read endpoints for surfacing and completing message tags. Both are scoped to public messages only (publiclyVisible = true) and require a session cookie or Authorization: Bearer <token>.

GET /api/tags/trending?window=week&limit=20
Authorization: Bearer 3f1c9e...<64 hex chars>...a8
QueryDefaultNotes
limit20Number of tags to return. Clamped to a max of 100; a missing, non-numeric, or < 1 value falls back to the default.
windowweekTrailing time window: day (1 day), week (7 days), or month (30 days). Any other value falls back to week.
{
  "tags": [
    { "tag": "release", "count": 42, "lastUsedAt": "2026-07-30T14:22:00.000Z" },
    { "tag": "dev", "count": 37, "lastUsedAt": "2026-07-31T09:05:00.000Z" }
  ]
}

Tags are ordered by count (descending), then by lastUsedAt (most recent first). count is the number of public messages using the tag within the window; lastUsedAt is an ISO-8601 timestamp of the most recent such message.

Tag autocomplete

GET /api/tags/autocomplete?q=re&limit=10
Authorization: Bearer 3f1c9e...<64 hex chars>...a8
QueryDefaultNotes
qrequiredCase-insensitive literal prefix to match. A single leading # is stripped. Returns 400 if empty after trimming.
limit10Number of suggestions to return. Clamped to a max of 50; a missing, non-numeric, or < 1 value falls back to the default.
{
  "tags": [
    { "tag": "release", "count": 42 },
    { "tag": "react", "count": 18 }
  ]
}

Suggestions are ordered by count (descending), then alphabetically. Matching is a literal prefix (% and _ are not wildcards). An empty q returns 400 { "error": "Query parameter 'q' is required" }.

Geolocation

GET /api/location?latitude=47.6&longitude=-122.3
{
  "city": "Seattle",
  "state": "WA",
  "country": "United States",
  "coordinates": { "latitude": 47.6, "longitude": -122.3 },
  "timezone": "America/Los_Angeles"
}

US coordinates only.

Weather

GET /api/weather?latitude=47.6&longitude=-122.3&extended=true
QueryDefaultNotes
latitude, longituderequired
extendedfalseInclude hourly and weekly series.
refreshfalseBypass the server-side LRU cache (30 min per gridpoint).
{
  "location": "Seattle",
  "temperature": 62,
  "condition": "Partly Sunny",
  "conditionIcon": "bx-cloud",
  "high": 68, "low": 54,
  "humidity": 71,
  "windSpeed": 5,
  "timeZone": "America/Los_Angeles",
  "hourly": [ { "startTime": "...", "temperature": 62, "probabilityOfPrecipitation": 0, "shortForecast": "Partly Sunny" } ],
  "weekly": [ { "name": "Tonight", "isDaytime": false, "temperature": 54, "shortForecast": "Mostly Cloudy", "icon": "..." } ]
}

Image proxy

GET /api/images/proxy?url=https://instagram.com/p/abc.jpg

Streams the bytes back with appropriate caching. On any failure (timeout, non-image content type, oversized response, network error) it responds with a placeholder SVG and the header X-Image-Status: placeholder; it does not return 4xx/5xx for image fetch failures.

Allowed hostnames: instagram.com, cdninstagram.com, fbcdn.net, and subdomains. Any other domain returns 403.

Analytics beacon

POST /api/analytics/ingest
Content-Type: application/json

{ "type": "page_view", "path": "/dashboard", "referrer": "https://google.com" }

For named actions:

{ "type": "action", "name": "post_created", "properties": { "channel": "web" } }

Always returns 204 No Content; invalid bodies are silently dropped. The server sets the interlinedlist_analytics_session cookie on first call (30-day lifetime by default).

Provider configuration probes

GET /api/auth/linkedin/status
GET /api/auth/twitter/status

Both respond with { "configured": boolean, "redirectUri": string | null }. Use these to decide whether to surface a LinkedIn/X sign-in button.

Bluesky AT Protocol metadata

GET /api/oauth/client-metadata returns the standard OAuth client-metadata JSON consumed by Bluesky's auth server during the OAuth handshake. The URL also serves as the BLUESKY_CLIENT_ID. Don't call this from your own code; it exists to make the OAuth flow work.

  • Authentication & OAuth: the OAuth flows these provider-status probes support.
  • Messages: tag discovery powers tag autocomplete on the composer.
  • API overview: base URL, authentication at a glance, and response conventions.
  • API explorer: try utility endpoints live with the interactive Swagger console.