> ## Documentation Index
> Fetch the complete documentation index at: https://docs.loci.ng/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> An API key acts for one organisation, reads and nothing else, and expires. How it is minted, how it is sent, and what happens when it goes stale.

Every call to the API carries an API key. There is no anonymous access, and a
person's app session is not accepted in its place.

```
Authorization: Bearer loci_sk_...
```

A key is `loci_sk_` followed by 43 URL-safe characters. The fixed prefix is
there so secret scanners can find a leaked key, and so the server can tell
what it has been handed without a lookup.

## Minting a key

A key is minted by a person in the app, under **Developers → New key**. It
needs the **Admin** or **Owner** role, and the ability to mint is marked
human-only, so no key can mint another whatever its scope.

<Steps>
  <Step title="Name it after what will hold it">
    `field-app-prod`, not `my key`. The name is what you read in the key list
    and in the audit trail when something needs revoking.
  </Step>

  <Step title="Pick a scope">
    **Locality**, **Analyst** or **Full read**. The preset expands into a list
    of capabilities at mint time, and that list is what the key stores, so a
    preset changing later cannot silently widen an existing key. Which calls
    each one reaches is in [Scopes](/scopes).
  </Step>

  <Step title="Optionally narrow it to named datasets">
    By default a key sees every dataset its organisation owns or licenses. It
    can be narrowed to a chosen set, in which case the other datasets are
    simply absent from its answers.
  </Step>

  <Step title="Choose an expiry">
    From seven days to a year. There is no never-expires option. A credential
    that outlives the project it was made for is the usual way one leaks
    without anyone noticing it was still live.
  </Step>

  <Step title="Copy the secret">
    It is shown once. The server stores a hash and has no way to show it
    again.
  </Step>
</Steps>

## Sending it

The key goes in the `Authorization` header. It is read from nowhere else: a
key in a cookie is ignored by design, because a credential a browser can be
made to present cross-site is exactly what a machine credential should never
be.

A key the server does not recognise, or one that has expired or been revoked,
is `401 invalid_key`. It is not treated as anonymous: the prefix proves a key
was meant, so answering as though none was sent would hide an expired key
behind a confusing empty result. Presenting the same bad key repeatedly is
metered on its own, five attempts a minute, so fix the credential rather than
retrying it.

## What a key may do

A key reads. It reaches the four calls in this reference, subject to its
scope, and nothing else. It cannot create, change or delete data, cannot buy
or sell on the marketplace, cannot manage members, and cannot mint or revoke
keys. There is no write capability a key can hold at any rank.

It acts for exactly one organisation, the one it was minted in, and its
answers cover that organisation's own datasets and the ones it has licensed.
A key does not reach another organisation's data by knowing a frame id; what
is recorded on a frame is answered from the datasets the key can read, and
those alone.

## Authority is re-derived on every request

A key stores the capabilities it was granted and the role its creator held
when it was minted. On every request the server takes the lower of that
stored role and the creator's role **right now**, and both halves must pass:
the capability must be in the grant, and the effective role must be enough
for it.

The consequences are worth planning for:

* A creator who is demoted takes their keys down with them.
* A creator who leaves the organisation kills their keys on the next request,
  with no revocation step and no grace period.
* Removing a member also revokes their keys explicitly, so the key list says
  what is true.

<Warning>
  If a production integration depends on a key, mint it under an account that
  will outlive the integration, or expect to re-mint it when that person leaves.
</Warning>

## Expiry

Expiry is a required property of every key: the default is 90 days and the
maximum a year. The server refuses an expired key from the expiry moment
onward, but the key's stored status is not rewritten, so a tool that reads the
status to judge a key's health will be misled. Compare the expiry date against
the clock instead.

## When a key leaks

Revoke it in the Developers tab. The next request carrying that secret is
`401 invalid_key`; there is no propagation delay. Revocation is permanent.
Then mint a replacement, and ask whether it needed to be as wide as the one
that leaked: a Locality key narrowed to the datasets an integration actually
uses is a much smaller incident next time.

Because keys are read-only, a leaked key cannot delete, publish, spend or
invite. It can read what its scope permitted until it is revoked, which is
the reason the read-only rule exists.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.