The lct.me API

Share a location as a link that expires.

Create a link to a position — a fixed one, or a live session a device keeps updating — and share the URL. The link stops working when it expires or when it is revoked.

Everything under /v1 is a promise and will not break under you. Everything under /api is the browser app's own surface: undocumented on purpose, and free to move.

Getting a key

Keys are issued by hand while the API is in its first release, and there is no way to request one yet — not a signup form and not an address. Read what Professional is for where that stands.

Authentication

Your key, as x-lctme-api-key: lctme_sk_….

Not Authorization: Bearer, and that is a limitation rather than a preference. The API is served through CloudFront with origin access control, which signs each request to the origin and OVERWRITES the Authorization header with its own signature. A key sent conventionally would be discarded in transit and every call would read as unauthenticated. The conventional form needs a different front door, which is planned; when it arrives both headers will be accepted for long enough that nothing has to change on a schedule we set.

curl https://lct.me/v1/me \
  -H 'x-lctme-api-key: lctme_sk_…'

Endpoints

Everything below is generated from the same definition the service answers from, so this page cannot describe an endpoint that does not exist. The machine-readable form is at /v1/openapi.json.

GET /v1/me

What this key is.

The smallest call that proves a key works. Worth making first from a new integration: the alternative is finding out from whichever endpoint you actually wanted, and guessing at the failure.

Responses

200 The key is live.
FieldType
name required string
createdAt required string (date-time)
401 No key, or a key that is not valid. The two are distinguished (no_credentials and invalid_key) because the difference is about YOUR request rather than about our data — neither answer tells an attacker whether a guessed key exists. Error
403 The key was revoked. Codes: key_revoked. Error
404 This deployment does not serve the business API. Codes: not_found. A 404 rather than a 500, because it is true and reveals nothing about why. Error
405 Codes: method_not_allowed. Error

GET /v1/usage

What you have used.

Defaults to the current UTC calendar month, which is the period an invoice covers.

Every boundary is UTC. A day that starts when a server happens to think it does is a total you cannot reproduce.

There is no parameter naming a customer, and that is structural rather than a check we remembered to write: the key you authenticate with is the only one you can read.

Query parameters

NameType
from string (date) YYYY-MM-DD, inclusive. A date that does not exist is refused.
to string (date) YYYY-MM-DD, inclusive.

Responses

200 Totals for the period, and a line per day — reconciling an invoice means finding the day that surprised you, and one number gives you nowhere to look.
FieldType
from required string (date)
to required string (date)
snapshots required integer
sessions required integer
days required object[] Only days with usage appear.
400 Codes: invalid_period. Error
401 No key, or a key that is not valid. The two are distinguished (no_credentials and invalid_key) because the difference is about YOUR request rather than about our data — neither answer tells an attacker whether a guessed key exists. Error
403 The key was revoked. Codes: key_revoked. Error
404 This deployment does not serve the business API. Codes: not_found. A 404 rather than a 500, because it is true and reveals nothing about why. Error
405 Codes: method_not_allowed. Error

GET /v1/openapi.json

This document.

No key needed. This is the one endpoint that answers without one.

Responses

200 The OpenAPI document.
405 Codes: method_not_allowed. Error

Objects

Position

Optional fields are omitted rather than sent as null. A device that cannot measure altitude should leave the key out; null is refused so that 'unknown' has one representation instead of two.

FieldType
latitude required number (-90 to 90)
longitude required number (-180 to 180)
accuracy required number (0 or more) Radius in metres, as the browser's Geolocation API reports it.
altitude number Metres above the WGS 84 ellipsoid.
altitudeAccuracy number
heading number Degrees clockwise from true north.
speed number Metres per second.

Error

FieldType
error required object

Errors

Every failure is the same shape. Branch on error.code, which is stable; error.message is written for a person and may be reworded.

{ "error": { "code": "invalid_position", "message": "latitude must be between -90 and 90." } }

What is not here yet

Named honestly rather than left to be discovered: webhooks and custom domains are not built. Both are deferred until a customer defines what they need, because publishing an event taxonomy invites integrations against it — and then it is not a guess any more, it is a commitment.

Two things that are built and worth knowing about: usage is counted per key per UTC day and readable at /v1/usage before any invoice quotes it, and the key travels in a custom header rather than Authorization for the reason given above.