Lore Scanner
Base URL: https://loremind.peekgames.dev/api/loremind/v1
Last verified against LoreMind v1.0.67 (August 2026).
Scan game files for lore extraction. The flow is archive-based: describe your files in a manifest, upload a single ZIP archive to a signed URL, then confirm to start processing. See Lore Scanner for a feature overview and step-by-step walkthrough.
Authentication: Editor Key (sk_editor_*) for all scanner endpoints.
Tooling API - subject to change. The scanner endpoints power the SDK’s scanner window and are handy for CI pipelines, but they are tooling APIs: the flow may change between SDK releases without notice (it has before). Pin your versions if you script against them directly.
Every scanner endpoint returns 401 Invalid authentication when the Editor Key is missing or invalid; the per-endpoint tables below list only endpoint-specific errors. Common failures are explained in Errors & Rate Limits.
POST /scanner/estimate
Get a cost estimate from file metadata without uploading content.
Request Body
{
files: Array<{
filename: string; // File name (e.g., "story.ink")
sizeBytes: number; // File size in bytes
}>;
}Response
{
estimatedMicrocredits: number;
estimatedCredits: number;
reservedMicrocredits: number; // Amount that will be reserved at confirm (refunded down to actual cost)
reservedCredits: number;
availableCredits: number; // Team's current credit balance
perFile: Array<{
filename: string;
estimatedMicrocredits: number;
}>;
estimateType: "rolling_average" | "minimum_balance";
message: string;
}Estimates are based on a rolling average of previous scan jobs. When no history exists yet, a minimum balance is required instead (estimateType: "minimum_balance"). The scanner reserves more than the estimate up front and refunds the difference once actual cost is known.
Example
curl -X POST https://loremind.peekgames.dev/api/loremind/v1/scanner/estimate \
-H "Authorization: Bearer sk_editor_your_key" \
-H "Content-Type: application/json" \
-d '{
"files": [
{ "filename": "story.ink", "sizeBytes": 45000 },
{ "filename": "npcs.json", "sizeBytes": 12000 }
]
}'POST /scanner/submit-archive
Create a scan job from a file manifest. Returns 202 Accepted with a signed upload URL for the ZIP archive.
Request Body
{
files: Array<{
filename: string; // File name (e.g., "story.ink")
relativePath: string; // Path inside the archive
sizeBytes: number;
contentHash: string; // SHA-256 hash of file content
}>;
totalSizeBytes: number; // Total archive size (max 200 MB)
userContext?: string; // Optional context about your game (max 5,000 chars)
idempotencyKey?: string; // Optional deduplication key
}Response (202)
{
jobId: string;
status: "PENDING_UPLOAD";
uploadUrl: string; // Signed URL — PUT your ZIP archive here (Content-Type: application/zip)
expiresIn: number; // Upload URL lifetime in seconds (1800 = 30 minutes)
totalFiles: number;
estimatedCredits: number;
}If you resend the same idempotencyKey, the existing job is returned with deduplicated: true instead of creating a new one.
No credits are charged at this step. Upload the ZIP to uploadUrl with an HTTP PUT (Content-Type: application/zip), then call the confirm endpoint. The upload window is 45 minutes; unconfirmed jobs expire.
Error Responses
| Status | Error | Description |
|---|---|---|
| 400 | Invalid manifest | Missing/invalid manifest fields |
| 400 | Archive too large | totalSizeBytes exceeds 200 MB |
| 400 | Invalid file types in manifest | Response lists rejected files and allowed extensions |
| 402 | Insufficient credits | Balance below the required reservation |
POST /scanner/submit-archive/confirm
Confirm that the ZIP archive has been uploaded. This reserves credits and queues the job for processing.
Request Body
{
jobId: string;
}Response
{
jobId: string;
status: "QUEUED";
totalFiles: number;
estimatedCredits: number; // Reserved amount; the difference is refunded after processing
}Once confirmed, the job expires 24 hours later; if processing never completes, reserved credits are refunded.
Error Responses
| Status | Error | Description |
|---|---|---|
| 400 | Archive not found in storage | Upload the ZIP to the signed URL first |
| 402 | Insufficient credits | Not enough credits for the reservation |
| 404 | Job not found | Invalid jobId |
| 409 | Job is not awaiting upload | Job already confirmed, expired, or cancelled (response includes currentStatus) |
GET /scanner/jobs
List scan jobs for the key’s project (most recent 50).
Response
{
jobs: Array<{
jobId: string;
status: "PENDING_UPLOAD" | "QUEUED" | "PROCESSING" | "COMPLETED" | "FAILED";
totalFiles: number;
completedFiles: number;
failedFiles: number;
createdAt: string;
startedAt: string | null;
completedAt: string | null;
committedAt: string | null;
}>;
}Example
curl https://loremind.peekgames.dev/api/loremind/v1/scanner/jobs \
-H "Authorization: Bearer sk_editor_your_key"GET /scanner/jobs/{jobId}
Get the status and results of a scan job. Poll this endpoint to track progress.
Response
{
jobId: string;
status: "PENDING_UPLOAD" | "QUEUED" | "PROCESSING" | "COMPLETED" | "FAILED";
totalFiles: number;
completedFiles: number;
failedFiles: number;
estimatedCredits: number;
actualCredits: number;
creditAdjustment: number; // Positive = refunded after reconciliation
adjustmentMessage: string;
errorMessage: string | null;
createdAt: string;
startedAt: string | null;
completedAt: string | null;
committedAt: string | null;
extractions: Array<{
id: string;
title: string;
tags: string[];
location: string | null;
confidence: number;
contentPreview: string; // First 200 characters
sourceFiles: string[];
}>;
skippedContent: Array<{
section: string;
reason: string;
sourceFile: string;
}>;
}Error Responses
| Status | Error | Description |
|---|---|---|
| 403 | Access denied | Job belongs to a different project |
| 404 | Job not found | Invalid jobId |
DELETE /scanner/jobs/{jobId}
Cancel a job that has not been confirmed yet. Only jobs in PENDING_UPLOAD can be cancelled; no credits are charged.
Response
{
jobId: string;
status: "FAILED";
message: "Job cancelled. No credits were charged.";
}Attempting to cancel a job in any other state returns 409 with the job’s currentStatus.
POST /scanner/commit
Create Lore Documents from approved scan extractions. Returns 202 Accepted and processes embeddings in the background.
Request Body
{
jobId: string;
extractionIds: string[]; // IDs of extractions to approve
}Response (202)
{
committed: number;
documentIds: string[];
}Example
curl -X POST https://loremind.peekgames.dev/api/loremind/v1/scanner/commit \
-H "Authorization: Bearer sk_editor_your_key" \
-H "Content-Type: application/json" \
-d '{
"jobId": "clx1abc123def456",
"extractionIds": ["ext_1", "ext_2", "ext_5"]
}'Error Responses
| Status | Error | Description |
|---|---|---|
| 400 | Invalid request | Missing jobId or empty extractionIds |
| 400 | Job is not completed yet | Job still processing |
| 400 | No matching extractions found | None of the IDs match |
| 403 | Access denied | Job belongs to a different project |
| 404 | Job not found | Invalid jobId |
POST /scanner/rescan
Compare current file hashes against previous scans to identify changes (true-up).
Request Body
{
files: Array<{
filename: string;
contentHash: string; // SHA-256 hash of file content
}>;
}Response
{
changed: string[]; // Files with different hash
added: string[]; // New files not in previous scan
deleted: string[]; // Files in previous scan but not in current
unchanged: string[]; // Files with same hash
}Example
curl -X POST https://loremind.peekgames.dev/api/loremind/v1/scanner/rescan \
-H "Authorization: Bearer sk_editor_your_key" \
-H "Content-Type: application/json" \
-d '{
"files": [
{ "filename": "story.ink", "contentHash": "a1b2c3..." },
{ "filename": "new_quest.ink", "contentHash": "d4e5f6..." }
]
}'Next Steps
- Lore Scanner - Feature overview with the full curl walkthrough
- Lore Data - Read your project’s lore data
- Credits & Billing - How scan costs and reservations work
- API Reference overview - All endpoints at a glance