# LunaCore

> LunaCore (https://lunacore.dev) publishes free products, such as the LunaNPC Minecraft mod. This API
> exposes its products, releases, changelog, the LunaNPC mod API reference and the public
> issue tracker as JSON, for AI agents and scripts.

All responses are JSON. Timestamps are ISO 8601 (UTC). Errors are `{"error": "<message>"}`
with a 4xx status. Machine-readable endpoint list: https://lunacore.dev/api/v1
Human docs: https://api.lunacore.dev/LunaCore

## Where to start

- "What is LunaNPC and what's the latest version?" `GET /api/v1/products/LunaNPC`
- "What changed in a release?" `GET /api/v1/products/LunaNPC/versions/latest`
- "Full release history with changes" `GET /api/v1/products/LunaNPC/versions?changelog=true`
- "How do I call X in the mod API?" `GET /api/v1/search?q=X&in=api`, then
  `GET /api/v1/products/LunaNPC/reference?type=<Type>` for that type's full reference
- "Is this bug known?" `GET /api/v1/search?q=<words>&in=reports`

Prefer search and the `type`/`compact` filters over pulling the whole reference.

## Authentication

Reads need nothing. For a higher tier, and for writes, send `Authorization: Bearer <key>`.
If the environment has `LUNACORE_API_KEY`, use it. Keys start with `luna_`.

- Personal key: made by a user in their profile's Settings tab. Acts as that user, at their tier.
- Staff key: acts as the `system` staff bot account, tier 3.

Check a key with `GET /api/v1/me`. Never print, log or paste a key into a reply.

## Rate limits

Every request counts once against the caller's tier, whatever it returns. Windows follow
the clock (minute, hour, Berlin day) and reset at their boundary; `limit`
parameters only cap rows per request. Guests are counted per IP.

- Tier 1, Guest: 10 per minute, 100 per hour, 500 per day. No key and not signed in. Counted per IP address.
- Tier 2, Registered: 50 per minute, 500 per hour, 2500 per day. Signed in, or sending your personal API key.
- Tier 3, Staff: 100 per minute, 1000 per hour, 5000 per day. A staff account, or a staff key.

Responses carry `x-ratelimit-tier`, `x-ratelimit-window`, `x-ratelimit-limit` and
`x-ratelimit-remaining`. On 429, wait `retry-after` seconds. `GET /api/v1/usage` is free
and shows what is left.

## Triage loop for an agent acting as staff

1. `GET /api/v1/reports?status=open` and pick reports with `"awaiting_staff": true`
   (open, and no staff reply has come after the user's last word).
2. `GET /api/v1/reports/{id}` for the description, images and thread.
3. Reply with `POST /api/v1/reports/{id}/comments`; fix the title or priority, or close it,
   with `PATCH /api/v1/reports/{id}`.
4. Poll again with `since=<generated_at from the previous list>` to see only new activity.
   Title and priority edits do not count as activity.

Replies notify the report's author. Write as a member of the LunaCore team; never paste
secrets or internal details into a reply. Every report and comment is public.

# Endpoints

## Products

### GET /api/v1/products

Every product with its latest version, download totals, report counts and links.

Tier: 1 (Guest)

### GET /api/v1/products/{slug}

One product. The slug is case-insensitive, e.g. LunaNPC.

Tier: 1 (Guest)

### GET /api/v1/products/{slug}/versions

Release history, planned versions first, then releases newest first, with download links, per-version download counts and what each version requires ("requires": Minecraft, loader, libraries, with "or_newer"). "released" is false for a planned version.

Tier: 1 (Guest)

Query:
- `changelog`: true to include each release's changes, default false
- `limit`: 1-200, default 50

### POST /api/v1/products/{slug}/versions

Add a planned version that is not released yet, so reports can already be resolved in it. Publishing that version later releases it.

Tier: 3 (Staff)

JSON body:
- `version`: string, e.g. 2.0.0

### GET /api/v1/products/{slug}/versions/{version}

One release with its changes. Use "latest" for the current release.

Tier: 1 (Guest)

### GET /api/v1/products/{slug}/reference

The product's mod API reference: types, functions, parameters and enums. Every type, function and field says the version it arrived in ("since"); functions carry their "history" of signature changes. An archived version lists what changed after it in "newer_changes".

Tier: 1 (Guest)

Query:
- `version`: API version or "latest" (the default); archived versions are listed in "versions"
- `type`: only this type, e.g. NpcTrade; adds the type's full change history
- `compact`: true for names, signatures and "since" only

### GET /api/v1/products/{slug}/reference/changes

What changed in the mod API between versions: every added, changed or removed type, function, field and enum constant, with breaking flags and before/after signatures. No range = the whole history, oldest first.

Tier: 1 (Guest)

Query:
- `version`: only what this version changed, e.g. 0.2.0 or "latest"
- `from`: only changes after this version (use with to for an upgrade path)
- `to`: only changes up to and including this version
- `type`: only this type, e.g. NpcRegistry
- `member`: only this function, field or constant, e.g. getOrCreate
- `kind`: type | function | field | enum-constant
- `change`: added | changed | removed
- `breaking`: true for breaking changes only

## Changelog

### GET /api/v1/changelog

Changelog entries across every product, newest first. Each change has a tag: Added, Changed, Fixed or Removed.

Tier: 1 (Guest)

Query:
- `product`: product slug, e.g. LunaNPC or LunaCore (the website)
- `kind`: release (entries with a version) | note (everything else)
- `since`: ISO 8601 timestamp; only entries published after it
- `before`: entry id; only older entries, for paging
- `limit`: 1-100, default 20

### GET /api/v1/changelog/{id}

One changelog entry.

Tier: 1 (Guest)

## Search

### GET /api/v1/search

Search products, reports, changelog entries and the API reference in one call. Results are grouped by scope.

Tier: 1 (Guest)

Query:
- `q`: 2-100 characters, case-insensitive substring match
- `in`: comma list of products, reports, changelog, api; default all
- `limit`: 1-50 results per scope, default 10

## Reports

### GET /api/v1/reports

List reports, most recent activity first.

Tier: 1 (Guest)

Query:
- `status`: open (default) | resolved | all
- `type`: issue | suggestion
- `product`: product slug, e.g. LunaNPC
- `since`: ISO 8601 timestamp; only reports created, resolved or commented on after it
- `limit`: 1-200, default 50

### GET /api/v1/reports/{id}

One report with its full description, images, comment thread and events (resolved, reopened, type, priority and title changes), each in time order.

Tier: 1 (Guest)

### POST /api/v1/reports/{id}/comments

Reply on a report. Staff, or the report's author. The author is notified.

Tier: 2 (Registered)

JSON body:
- `body`: string, the reply text

### PATCH /api/v1/reports/{id}

Rename, re-prioritise, retype, close or reopen a report. Send only the fields to change. Closing notifies the author; with resolved_in it says which version fixes the bug or ships the suggestion.

Tier: 3 (Staff)

JSON body:
- `title`: string, 3-200 chars
- `priority`: low | medium | high | emergency
- `type`: issue | suggestion
- `status`: open | resolved
- `resolved_in`: { product, version } of an existing version, released or planned; null clears it. Needs status resolved

## Account

### GET /api/v1/me

Which account the request is acting as, and its tier.

Tier: 2 (Registered)

### GET /api/v1/usage

Your tier, request limits and what is left, plus your AI budget when signed in. Does not count against the limits.

Tier: 1 (Guest), not counted
