Skip to Content

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

StatusErrorDescription
400Invalid manifestMissing/invalid manifest fields
400Archive too largetotalSizeBytes exceeds 200 MB
400Invalid file types in manifestResponse lists rejected files and allowed extensions
402Insufficient creditsBalance 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

StatusErrorDescription
400Archive not found in storageUpload the ZIP to the signed URL first
402Insufficient creditsNot enough credits for the reservation
404Job not foundInvalid jobId
409Job is not awaiting uploadJob 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

StatusErrorDescription
403Access deniedJob belongs to a different project
404Job not foundInvalid 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

StatusErrorDescription
400Invalid requestMissing jobId or empty extractionIds
400Job is not completed yetJob still processing
400No matching extractions foundNone of the IDs match
403Access deniedJob belongs to a different project
404Job not foundInvalid 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

Last updated on