# Authentication

URL: https://docs.lastscanned.com/authentication

Send your key in either header:

```http
Authorization: Bearer lsk_live_…
x-api-key: lsk_live_…
```

Every `/v1/*` route needs a key except `GET /v1/carriers`.

## Without a key

`GET /v1/track/{trackingNumber}` works once a day without a key, per IP address, to try the API. A malformed number, a failure on our side, or a `not_found` (up to 5 a day) doesn't use it up. After that:

```json
{ "error": { "code": "payment_required", "message": "You've used today's free lookup. Pay with MPP (see the WWW-Authenticate header): $0.50 by card for 25 tracking numbers. Or sign up at https://app.lastscanned.com for 5 free lookups a day." } }
```

## Pay per request

Agents can pay instead of signing up, with [MPP](https://mpp.dev). A tracking request without a key (once the free lookup is used, and always for `GET /v1/track?numbers=`) answers `402 Payment Required` with a `WWW-Authenticate: Payment` challenge. Pay and retry with `Authorization: Payment …`; any MPP client does this for you.

| Pay with | Price |
|---|---|
| A card, as a shared payment token (e.g. from [Link's agent wallet](https://link.com/agents)) | $0.50 for 25 tracking numbers |
| Stablecoins (USDC on Tempo) | 2 cents per tracking number |

The paid request spends what it looks up, and only if it gets an answer (a result, or the carrier saying it has no such number). What's left comes back as a credit key, in the `x-lastscanned-key` header and a `credits` field:

```json
{ "carrier": "ups", "status": "in_transit", "…": "…", "credits": { "key": "lsk_pack_…", "remaining": 24 } }
```

Send it like an API key on later tracking requests. Each response has `x-lastscanned-credits` with what's left. Credit keys work only for `/v1/track`; when one runs out you get `402` `credits_used`, and a request without the key pays again.

Paid routes and prices are also listed at `https://api.lastscanned.com/openapi.json`.

## Keys

- A key is `lsk_live_` followed by 32 hex characters.
- It is shown once, when it is made. Only a hash is stored, so a lost key can't be recovered; make a new one.
- Create and revoke keys in the [dashboard](https://app.lastscanned.com/keys), or with [`POST /v1/keys`](/reference/account/createKey) and [`DELETE /v1/keys/{prefix}`](/reference/account/revokeKey). The prefix is `lsk_live_` plus the key's first 8 hex characters, e.g. `lsk_live_1a2b3c4d`.
- All keys on an account share its usage and limits.

Call the API from your server. It sends no CORS headers, so browsers can't call it directly, and a key in front-end code is a leaked key.

## 401 Unauthorized

```json
{ "error": { "code": "unauthorized", "message": "Invalid API key" } }
```

Returned for a missing, malformed or revoked key, or a disabled account.
