{ "swagger": "2.0", "info": { "description": "REST API for managing a personal coin collection. Supports coin CRUD, image uploads, AI-powered analysis, user management, and admin features.", "title": "Orla Numis API", "contact": {}, "version": "1.0.2" }, "basePath": "/api", "paths": { "/admin/availability-cycles": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns paginated history of admin/scheduled availability cycles with roll-up child counts.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "List availability cycles", "parameters": [ { "type": "integer", "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", "default": 20, "description": "Items per page", "name": "limit", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/availability-cycles/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns a single availability cycle with its per-owner child run summaries (no per-coin results).", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Get availability cycle detail", "parameters": [ { "type": "integer", "description": "Cycle ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/availability-runs": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns paginated history of legacy (pre-cycle) wishlist availability check runs, including UserID=0 admin rows.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "List legacy availability check runs", "parameters": [ { "type": "integer", "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", "default": 20, "description": "Items per page", "name": "limit", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/availability-runs/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns a single legacy (pre-cycle) availability check run with all per-coin results.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Get legacy availability run detail", "parameters": [ { "type": "integer", "description": "Run ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/availability/run": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Enqueues a wishlist availability check cycle (one child run per owner) and returns immediately. Duplicate requests while a cycle is queued or running are rejected.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Trigger manual wishlist availability check", "responses": { "202": { "description": "Accepted", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/backups": { "get": { "security": [ { "BearerAuth": [] } ], "description": "The full backups of the backup folder, newest first: the automatic ones (made every day, the last ones kept), those made by hand, those made of the data a restore replaced, and the imported ones. Each says when its data was copied, its size and what it holds. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Full backups", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.BackupsResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "Starts a full backup (the whole database and every upload), made in the background: the list says when it is running, and shows it once made. Admin only.", "tags": [ "Admin" ], "summary": "Make a full backup now", "responses": { "202": { "description": "Accepted" }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/backups/upload": { "put": { "security": [ { "BearerAuth": [] } ], "description": "Keeps a backup's zip (a download of an earlier backup, of this app or another) in the backup folder, after checking it can be restored; restoring it is a second step. The body is the zip itself. Admin only.", "consumes": [ "application/zip" ], "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Import a full backup", "parameters": [ { "description": "The backup's zip", "name": "backup", "in": "body", "required": true, "schema": { "type": "string" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/services.BackupInfo" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "413": { "description": "Request Entity Too Large", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/backups/{name}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "The backup's zip: manifest.json, the database (database/ancientcoins.db) and every upload (uploads/…). It holds every user's data, password hashes included: keep it safe. Admin only.", "produces": [ "application/zip" ], "tags": [ "Admin" ], "summary": "Download a full backup", "parameters": [ { "type": "string", "description": "The backup's name", "name": "name", "in": "path", "required": true } ], "responses": { "200": { "description": "The zip" }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "Deletes a backup of the backup folder. Admin only.", "tags": [ "Admin" ], "summary": "Delete a full backup", "parameters": [ { "type": "string", "description": "The backup's name", "name": "name", "in": "path", "required": true } ], "responses": { "204": { "description": "No Content" }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/backups/{name}/restore": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Replaces all the data (every user's, the settings, the uploads) with the backup's. The backup is checked, the data of now is backed up first (a \"pre-restore\" backup, to go back to it), then the app restarts and applies the backup as it starts: in Docker its container starts it again by itself. Signed-in users may have to sign in again. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Restore a full backup", "parameters": [ { "type": "string", "description": "The backup's name", "name": "name", "in": "path", "required": true } ], "responses": { "202": { "description": "Accepted", "schema": { "type": "object", "additionalProperties": { "type": "boolean" } } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/catalogs": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Creates a new catalog registry entry.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Catalogs" ], "summary": "Create catalog", "parameters": [ { "description": "Catalog data", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.catalogCreateRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.CatalogRegistry" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/catalogs/{id}": { "put": { "security": [ { "BearerAuth": [] } ], "description": "Updates an existing catalog registry entry.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Catalogs" ], "summary": "Update catalog", "parameters": [ { "type": "integer", "description": "Catalog ID", "name": "id", "in": "path", "required": true }, { "description": "Catalog data", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.catalogUpdateRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.CatalogRegistry" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "Deletes a catalog registry entry if not in use.", "produces": [ "application/json" ], "tags": [ "Catalogs" ], "summary": "Delete catalog", "parameters": [ { "type": "integer", "description": "Catalog ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.MessageResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/collection-currency": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Switches the currency coin prices and values are kept in and every page shows (the euro or a currency of the ECB daily reference rates: USD, GBP, CHF, JPY…) and converts every stored price, value, value history and snapshot with the ECB reference rates, in one transaction. Switching to the current currency changes nothing. Admin only.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Change the collection currency", "parameters": [ { "description": "New currency", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.CollectionCurrencyRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.CollectionCurrencyChange" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "502": { "description": "Bad Gateway", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/deep-identification/observability": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns redacted aggregate job, provider, SSE, queue, cleanup, and janitor metrics. Never includes notes, queries, claims, reports, tokens, or other user content. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Get Deep Identification observability", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.DeepIdentificationObservabilitySummary" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/health/summary": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns aggregate collection health metrics across all users. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Get admin collection health summary", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.AdminHealthSummary" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/logs": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns recent in-memory application logs, optionally filtered by level. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Get application logs", "parameters": [ { "type": "integer", "default": 500, "description": "Maximum number of log entries", "name": "limit", "in": "query" }, { "enum": [ "trace", "debug", "info", "warn", "error" ], "type": "string", "description": "Filter by log level", "name": "level", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.LogsResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/mint-locations": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Creates a global mint location. Admin only.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Mint Locations" ], "summary": "Create mint location", "parameters": [ { "description": "Mint location data", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.mintLocationRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.MintLocation" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/mint-locations/{id}": { "put": { "security": [ { "BearerAuth": [] } ], "description": "Updates a global mint location. Admin only.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Mint Locations" ], "summary": "Update mint location", "parameters": [ { "type": "integer", "description": "Mint location ID", "name": "id", "in": "path", "required": true }, { "description": "Mint location data", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.mintLocationRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.MintLocation" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "Deletes a global mint location. Fails if any coin still references it. Admin only.", "produces": [ "application/json" ], "tags": [ "Mint Locations" ], "summary": "Delete mint location", "parameters": [ { "type": "integer", "description": "Mint location ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.MessageResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/numista-values": { "get": { "security": [ { "BearerAuth": [] } ], "description": "The month's Numista requests and whether they run low (from 75% of the quota) or are spent for background requests, the next batch's budget, the coins it covers (linked to Numista with a grade Numista prices) and those due a check (none in 30 days), and the latest batches. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Numista values batch status", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.NumistaValueStatus" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/numista-values/run": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Starts one batch in the background: it updates the current value of the coins due from Numista's price for their grade, within today's share of the monthly quota. The previous value stays in each coin's value history. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Run a Numista values batch now", "responses": { "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/handlers.MessageResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/numista/health": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns redacted Numista configuration validity and bounded rolling status, latency, cache, quota, and enrichment aggregates, and this month's request count against the quota. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Get Numista operational health", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.NumistaHealthSummary" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/security/events": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns persisted auth/security events with optional type, username, clientIp, and limit filters. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin Security" ], "summary": "List security events", "parameters": [ { "type": "string", "description": "Event type", "name": "type", "in": "query" }, { "type": "string", "description": "Username", "name": "username", "in": "query" }, { "type": "string", "description": "Client IP", "name": "clientIp", "in": "query" }, { "type": "integer", "default": 100, "description": "Maximum events", "name": "limit", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/security/exposure-check": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns release-readiness warnings for public-facing configuration. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin Security" ], "summary": "Get exposure check", "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } } } } }, "/admin/security/summary": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns aggregate auth/security counters and backup status. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin Security" ], "summary": "Get security summary", "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/settings": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns all application settings merged with defaults. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Get application settings", "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "Updates one or more application settings. Syncs log level if changed. Admin only.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Update application settings", "parameters": [ { "description": "Settings to update", "name": "body", "in": "body", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/handlers.SettingInput" } } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.SettingsUpdateResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/settings/defaults": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns the built-in default values for all application settings. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Get setting defaults", "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/test-anthropic": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Validates the configured Anthropic API key by calling Anthropic's models endpoint.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Test Anthropic connection", "responses": { "200": { "description": "OK", "schema": { "type": "object" } } } } }, "/admin/test-searxng": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Validates the configured SearXNG URL by making a reachability request.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Test SearXNG connection", "responses": { "200": { "description": "OK", "schema": { "type": "object" } } } } }, "/admin/updates": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns the running version, the latest released version (from the project's GitHub releases, checked every few hours) with its notes, and whether Watchtower is set up to update the container. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Get the update status", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.UpdateStatus" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/updates/apply": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Makes a backup (kind pre-update), then asks Watchtower to pull the latest image and recreate the container; the app restarts on the new version a moment later and migrates its data. Needs Watchtower's address and token in the settings. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Update the app now", "responses": { "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/handlers.MessageResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "502": { "description": "Bad Gateway", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/updates/check": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Asks GitHub for the latest release now, instead of using the last check. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Check for a new version now", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.UpdateStatus" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/users": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns a list of all registered users. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "List all users", "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { "$ref": "#/definitions/handlers.UserDTO" } } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/users/{id}": { "delete": { "security": [ { "BearerAuth": [] } ], "description": "Deletes a user and all their associated coins and images. Cannot delete yourself. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Delete a user", "parameters": [ { "type": "integer", "description": "User ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.MessageResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/users/{id}/reset-password": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Sets a new password for the specified user. Admin only.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Reset user password", "parameters": [ { "type": "integer", "description": "User ID", "name": "id", "in": "path", "required": true }, { "description": "New password", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.ResetPasswordRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.MessageResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/users/{id}/role": { "put": { "security": [ { "BearerAuth": [] } ], "description": "Updates the role for the specified user. Cannot update your own role. Admin only.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Update user role", "parameters": [ { "type": "integer", "description": "User ID", "name": "id", "in": "path", "required": true }, { "description": "Role update payload", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.UpdateUserRoleRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/users/{id}/unlock": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Clears a user's account lock state. Admin only.", "produces": [ "application/json" ], "tags": [ "Admin Security" ], "summary": "Unlock user", "parameters": [ { "type": "integer", "description": "User ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.MessageResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/valuation-runs": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns paginated history of scheduled collection valuation runs.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "List valuation runs", "parameters": [ { "type": "integer", "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", "default": 20, "description": "Items per page", "name": "limit", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/valuation-runs/trigger": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Manually triggers a collection valuation for all users. Runs asynchronously.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Trigger manual valuation", "responses": { "202": { "description": "Accepted", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/valuation-runs/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns a single valuation run with all per-coin results.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Get valuation run detail", "parameters": [ { "type": "integer", "description": "Run ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/valuation-runs/{id}/cancel": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Signals a running valuation to stop after the current coin.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "Cancel a valuation run", "parameters": [ { "type": "integer", "description": "Run ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/admin/wishlist-search-alert-runs": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns paginated manual and scheduled wishlist search alert runs across collectors for administrators.", "produces": [ "application/json" ], "tags": [ "Admin" ], "summary": "List all wishlist search alert runs", "parameters": [ { "type": "integer", "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", "default": 20, "description": "Items per page", "name": "limit", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.AdminWishlistSearchAlertRunListResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/agent/chat": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Send a message to the AI agent. Response is streamed as Server-Sent Events.", "consumes": [ "application/json" ], "produces": [ "text/event-stream" ], "tags": [ "Agent" ], "summary": "Chat with coin search agent (streaming)", "parameters": [ { "description": "Chat message", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.AgentChatRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.AgentChatResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "503": { "description": "Service Unavailable", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/agent/coin-search-prompt": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "Get coin search prompt", "responses": { "200": { "description": "OK", "schema": { "type": "object" } } } } }, "/agent/coin-shows-prompt": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "Get coin shows prompt", "responses": { "200": { "description": "OK", "schema": { "type": "object" } } } } }, "/agent/collection/proposals/{proposalId}/cancel": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Cancels a pending AI-proposed collection update for the authenticated user.", "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "Cancel collection proposal", "parameters": [ { "type": "string", "description": "Proposal ID", "name": "proposalId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.CancelCollectionProposalResult" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "503": { "description": "Service Unavailable", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/agent/collection/proposals/{proposalId}/commit": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Commits a pending AI-proposed collection update after explicit user confirmation.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "Commit collection proposal", "parameters": [ { "type": "string", "description": "Proposal ID", "name": "proposalId", "in": "path", "required": true }, { "description": "Commit confirmation", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.CommitCollectionProposalRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.CommitCollectionProposalResult" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "503": { "description": "Service Unavailable", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/agent/conversations": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns all saved agent conversations for the current user, newest first.", "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "List saved conversations", "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { "$ref": "#/definitions/handlers.ConversationSummary" } } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "Creates a new conversation or updates an existing one.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "Save conversation", "parameters": [ { "description": "Conversation data", "name": "body", "in": "body", "required": true, "schema": { "type": "object" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.AgentConversation" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/agent/conversations/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns a saved conversation with its full message history.", "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "Get conversation", "parameters": [ { "type": "integer", "description": "Conversation ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.AgentConversation" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "Deletes a saved agent conversation.", "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "Delete conversation", "parameters": [ { "type": "integer", "description": "Conversation ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/agent/copilot/capability": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Coin Copilot" ], "summary": "Resolve Coin Copilot or legacy mode", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.CoinCopilotCapability" } } } } }, "/agent/copilot/runs": { "post": { "security": [ { "BearerAuth": [] } ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Coin Copilot" ], "summary": "Start a durable Coin Copilot run", "parameters": [ { "type": "string", "description": "Idempotency key", "name": "Idempotency-Key", "in": "header", "required": true }, { "description": "Run request", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.copilotStartRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.copilotRunEnvelope" } }, "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/handlers.copilotRunEnvelope" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "503": { "description": "Service Unavailable", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/agent/copilot/runs/{runId}": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Coin Copilot" ], "summary": "Read an owner-scoped Coin Copilot run", "parameters": [ { "type": "string", "description": "Run ID", "name": "runId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.copilotRunEnvelope" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/agent/copilot/runs/{runId}/cancel": { "post": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Coin Copilot" ], "summary": "Cancel a Coin Copilot run", "parameters": [ { "type": "string", "description": "Run ID", "name": "runId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.copilotRunEnvelope" } }, "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/handlers.copilotRunEnvelope" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/agent/copilot/runs/{runId}/events": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "text/event-stream" ], "tags": [ "Coin Copilot" ], "summary": "Replay and follow Coin Copilot events", "parameters": [ { "type": "string", "description": "Run ID", "name": "runId", "in": "path", "required": true }, { "type": "integer", "description": "Resume after sequence", "name": "since", "in": "query" }, { "type": "integer", "description": "Resume after sequence", "name": "Last-Event-ID", "in": "header" } ], "responses": { "200": { "description": "OK" }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "429": { "description": "Too Many Requests", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/agent/copilot/runs/{runId}/resume": { "post": { "security": [ { "BearerAuth": [] } ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Coin Copilot" ], "summary": "Resume a paused Coin Copilot run", "parameters": [ { "type": "string", "description": "Run ID", "name": "runId", "in": "path", "required": true }, { "type": "string", "description": "Idempotency key", "name": "Idempotency-Key", "in": "header", "required": true }, { "description": "Resume request", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.copilotResumeRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.copilotRunEnvelope" } }, "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/handlers.copilotRunEnvelope" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "503": { "description": "Service Unavailable", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/agent/copilot/threads/{threadId}": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Coin Copilot" ], "summary": "Read an owner-scoped Coin Copilot thread", "parameters": [ { "type": "string", "description": "Thread ID", "name": "threadId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.copilotThreadDTO" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/agent/models": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns a curated list of Anthropic models suitable for the coin search agent.", "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "List available Anthropic models", "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { "type": "object" } } } } } }, "/agent/portfolio-summary": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "Get portfolio summary for AI analysis", "responses": { "200": { "description": "OK", "schema": { "type": "object" } } } } }, "/agent/status": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns whether an AI provider is selected for agent features.", "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "Get agent status", "responses": { "200": { "description": "OK", "schema": { "type": "object" } } } } }, "/agent/valuation-prompt": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "Get valuation prompt", "responses": { "200": { "description": "OK", "schema": { "type": "object" } } } } }, "/ai-jobs/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns user-scoped AI job status and result details.", "produces": [ "application/json" ], "tags": [ "AI Jobs" ], "summary": "Get AI job", "parameters": [ { "type": "integer", "description": "AI Job ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.AIJob" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/ai-status": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns whether the configured AI provider (Anthropic or Ollama) is usable for coin analysis.", "produces": [ "application/json" ], "tags": [ "Analysis" ], "summary": "Check AI provider status", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.AIStatusResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/auth/change-password": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Change the authenticated user's password. Requires the current password for verification.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "User" ], "summary": "Change password", "parameters": [ { "description": "Password change request", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.ChangePasswordRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.PasswordChangedResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/auth/login": { "post": { "description": "Authenticate with username and password. Returns access token (15min) and refresh token (30 days).", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Auth" ], "summary": "Login", "parameters": [ { "description": "Login credentials", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.loginRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.AuthResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/auth/me": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns profile information for the authenticated user.", "produces": [ "application/json" ], "tags": [ "User" ], "summary": "Get current user", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.UserInfoResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/auth/refresh": { "post": { "description": "Exchange a valid refresh token for new access and refresh tokens. The old refresh token is revoked (rolling refresh).", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Auth" ], "summary": "Refresh tokens", "parameters": [ { "description": "Refresh token", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.refreshRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.AuthResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/auth/register": { "post": { "description": "Create a new user account. The first registered user is assigned the admin role. Returns access token (15min) and refresh token (30 days).", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Auth" ], "summary": "Register a new user", "parameters": [ { "description": "Registration credentials", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.registerRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/handlers.AuthResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/catalogs": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns all catalog registry entries.", "produces": [ "application/json" ], "tags": [ "Catalogs" ], "summary": "List catalogs", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.catalogListResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coin-catalogue": { "get": { "security": [ { "BearerAuth": [] } ], "description": "A country's coins in one currency, newest first: in euros, its €2 commemoratives (every one the ECB has published since 2004), its €5, €10 and €20 coins and the collector coins its mint sells (Portugal, France, Austria); in escudos (currency PTE), Portugal's commemorative and collector coins of 1911–2001. Each says whether a coin of the collection is it. The coins the user hid, or joined to another, are listed apart.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "A country's coin catalogue", "parameters": [ { "type": "string", "description": "The country's ISO code, e.g. PT", "name": "country", "in": "query", "required": true }, { "enum": [ "PTE" ], "type": "string", "description": "PTE for Portugal's coins in escudos; the euro coins when omitted", "name": "currency", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.CoinCatalogueResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coin-catalogue/countries": { "get": { "security": [ { "BearerAuth": [] } ], "description": "The countries of the catalogue, each in its currency: the euro (every €2 commemorative the ECB has published since 2004, the €5, €10 and €20 coins, and the collector coins the mints of Portugal, France and Austria sell) and Portugal's escudo (its commemorative and collector coins, 1911–2001). Each says how many commemorative and collector coins it lists, and how many of them the user has.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Countries of the coin catalogue", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.CoinCatalogueCountriesResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coin-catalogue/joint-issues": { "get": { "security": [ { "BearerAuth": [] } ], "description": "The €2 coins the euro area's countries issued together (the Treaty of Rome in 2007, the EU flag in 2015…), newest first, each with every country's version.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Joint issues of the euro area", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.JointIssuesResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coin-catalogue/overrides": { "put": { "security": [ { "BearerAuth": [] } ], "description": "Hides a coin of the catalogue from the user's catalogue and checklist sets; or, with sameAs, says it is the same coin as another of the same country and currency, which is then listed in its place (and is the user's when the user has either). It replaces what the user had chosen for the coin.", "consumes": [ "application/json" ], "tags": [ "Coins" ], "summary": "Hide a coin of the catalogue, or join it to another", "parameters": [ { "description": "The coin's id, and the other's", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.CatalogueOverrideRequest" } } ], "responses": { "204": { "description": "No Content" }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "Forgets the user's choice for a coin of the catalogue: hidden, it is listed again; joined to another, it is listed apart again.", "tags": [ "Coins" ], "summary": "Show a coin of the catalogue again", "parameters": [ { "type": "string", "description": "The coin's id", "name": "id", "in": "query", "required": true } ], "responses": { "204": { "description": "No Content" }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coin-options": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns the admin-configured category and era choices with translated display names, and the collection currency (EUR, USD, GBP…) coin prices and values are kept in", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Get coin category and era options", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.CoinOptionsResponse" } } } } }, "/coin-photos/search": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns real photos of a coin with their author and license: the ECB's image of a €2 commemorative coin (when year is given and the coin is one; match=exact when the ECB lists exactly this coin), the Numista catalogue photos of numistaTypeId, then Wikimedia Commons files matching q. Commons searches are cached for a week, ECB pages for longer. Import a chosen photo with POST /coins/{id}/images/import.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Search real coin photos", "parameters": [ { "type": "string", "description": "Search text, e.g. title and year (required unless numistaTypeId, or year and country, are set)", "name": "q", "in": "query" }, { "type": "integer", "description": "Numista type whose catalogue photos to include", "name": "numistaTypeId", "in": "query" }, { "type": "string", "default": "en", "description": "Language of catalogue data", "name": "lang", "in": "query" }, { "type": "integer", "description": "Coin year, to look for a €2 commemorative on the ECB site", "name": "year", "in": "query" }, { "type": "string", "description": "Coin country (English or Portuguese name)", "name": "country", "in": "query" }, { "type": "string", "description": "Coin denomination, e.g. 2 euro", "name": "denomination", "in": "query" }, { "type": "string", "description": "Coin subject, for a coin not linked to Numista", "name": "subject", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.CoinPhotoSearchResult" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coin-releases": { "get": { "security": [ { "BearerAuth": [] } ], "description": "The ECB's €2 commemorative coins of the latest year it lists, and the collector coins the mints of Portugal (INCM), France (Monnaie de Paris) and Austria (Münze Österreich) sell, of the last twelve months and to come: upcoming first, then newest first. Each names its country and says whether a coin of the collection is it.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "List new and upcoming coins", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.CoinReleasesResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coin-releases/numista-type": { "get": { "security": [ { "BearerAuth": [] } ], "description": "The Numista type of a coin of the releases or the catalogue, to fill in its details when it is added: its own when it comes from Numista, the one the catalogue knows it by, else the one of Numista's coins of its country, year and face value whose ECB image, mint page or name is the coin's. The candidates are those coins, to choose from when none is sure. Numista is asked once a day at most for a country, year and face value.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Find the Numista type of a new coin", "parameters": [ { "enum": [ "ecb", "incm", "mdp", "moe", "numista" ], "type": "string", "description": "Where the coin comes from", "name": "source", "in": "query", "required": true }, { "type": "string", "description": "ISO code of its country; none for a joint issue", "name": "country", "in": "query" }, { "type": "string", "description": "Its title, as the release names it", "name": "title", "in": "query", "required": true }, { "type": "number", "description": "Its (lowest) face value", "name": "faceValue", "in": "query", "required": true }, { "type": "string", "description": "First month of issue (YYYY-MM)", "name": "from", "in": "query", "required": true }, { "type": "string", "description": "Its page", "name": "pageUrl", "in": "query" }, { "type": "string", "description": "Its picture", "name": "imageUrl", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.ReleaseNumistaType" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns a paginated, filterable list of coins belonging to the authenticated user.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "List coins", "parameters": [ { "enum": [ "coin", "banknote" ], "type": "string", "description": "Only coins or only banknotes (every item when omitted)", "name": "kind", "in": "query" }, { "type": "string", "description": "Filter by category, one of the admin-configured categories (Modern and Other by default)", "name": "category", "in": "query" }, { "type": "string", "description": "Filter by era (ancient, medieval, modern)", "name": "era", "in": "query" }, { "type": "string", "description": "Filter by country (exact)", "name": "country", "in": "query" }, { "type": "string", "description": "Filter by period (exact, e.g. República Portuguesa 1986–2001)", "name": "period", "in": "query" }, { "type": "string", "description": "Filter by face value, as written (\\", "name": "denomination", "in": "query" }, { "enum": [ "loose", "coincard", "capsule", "flip", "proof_set", "blister", "slab" ], "type": "string", "description": "Filter by presentation", "name": "presentation", "in": "query" }, { "type": "boolean", "description": "Only coins held more than once (a quantity above one), the copies there to trade", "name": "repeated", "in": "query" }, { "type": "boolean", "description": "Only coins of countries that no longer exist (USSR, Yugoslavia, Ottoman Empire…)", "name": "extinct", "in": "query" }, { "enum": [ "current", "demonetized" ], "type": "string", "description": "Only collection coins issued for circulation and still legal tender (current), or no longer legal tender (demonetized), as Numista says of their types; collector (non-circulating) coins are not current", "name": "circulation", "in": "query" }, { "type": "boolean", "description": "Only collection coins of commemorative types (true) or of regular ones (false), as Numista says", "name": "commemorative", "in": "query" }, { "type": "string", "description": "Only collection coins of one Numista kind of coin: standard, circulating-commemorative, non-circulating, or another kind's label (see the summary's classifications)", "name": "classification", "in": "query" }, { "enum": [ "common", "uncommon", "scarce", "rare", "very_rare", "extremely_rare" ], "type": "string", "description": "Filter by rarity", "name": "rarity", "in": "query" }, { "type": "string", "description": "Search across name, denomination, ruler, era, mint, inscriptions, notes, country and a banknote's serial number, series and signatures", "name": "search", "in": "query" }, { "enum": [ "true", "false" ], "type": "string", "description": "Filter by wishlist status", "name": "wishlist", "in": "query" }, { "type": "integer", "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", "default": 50, "description": "Items per page (max 100)", "name": "limit", "in": "query" }, { "enum": [ "created_at", "updated_at", "current_value", "purchase_date", "name", "quantity", "random" ], "type": "string", "default": "updated_at", "description": "Sort field", "name": "sort", "in": "query" }, { "enum": [ "asc", "desc" ], "type": "string", "default": "desc", "description": "Sort order", "name": "order", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.CoinListResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "Creates a new coin record for the authenticated user.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Create a coin", "parameters": [ { "description": "Coin data", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.CoinCreateRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.Coin" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/bulk": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Performs a bulk action (set, delete, export) on selected coins.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Bulk coin action", "parameters": [ { "description": "Bulk action request", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.BulkActionRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/countries": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns each country of the user's collection coins (excluding the wishlist) with how many coins it has, alphabetically.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "List collection countries", "parameters": [ { "enum": [ "coin", "banknote" ], "type": "string", "description": "Only coins or only banknotes (every item when omitted)", "name": "kind", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { "$ref": "#/definitions/repository.CountryCount" } } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/country-from-numista": { "post": { "security": [ { "BearerAuth": [] } ], "description": "For the user's coins that have no country but are linked to a Numista type, sets the country from the type's issuer. Handles up to 25 coins per call (issuer details are cached for 30 days); call again while remaining \u003e 0. Stops early when Numista is not configured or the quota is spent (see status).", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Fill coin countries from Numista", "parameters": [ { "type": "integer", "description": "Only this coin", "name": "coinId", "in": "query" }, { "type": "string", "default": "en", "description": "Language of country names", "name": "lang", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.CoinCountryFillResult" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/denominations": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns each face value of the user's collection coins (excluding the wishlist), as written, with how many coins have it, the most common first.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "List collection face values", "parameters": [ { "enum": [ "coin", "banknote" ], "type": "string", "description": "Only coins or only banknotes (every item when omitted)", "name": "kind", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { "$ref": "#/definitions/repository.DenominationCount" } } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/export": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Downloads, as an Excel workbook (xlsx) or a CSV, the user's coins and banknotes that the filters keep (the same filters and order as GET /coins, without pages; up to 10,000), one row each with its details, purchase, value, references and sets. Columns are named in the language asked for; a CSV in Portuguese uses \";\" and a decimal comma, as Excel there expects.", "produces": [ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", "text/csv" ], "tags": [ "Coins" ], "summary": "Export coins as a spreadsheet", "parameters": [ { "enum": [ "xlsx", "csv" ], "type": "string", "default": "xlsx", "description": "Spreadsheet format", "name": "format", "in": "query" }, { "enum": [ "pt", "en" ], "type": "string", "default": "pt", "description": "Language of the columns", "name": "lang", "in": "query" }, { "enum": [ "coin", "banknote" ], "type": "string", "description": "Only coins or only banknotes (every item when omitted)", "name": "kind", "in": "query" }, { "enum": [ "true", "false" ], "type": "string", "description": "Filter by wishlist status", "name": "wishlist", "in": "query" }, { "type": "string", "description": "Filter by country (exact)", "name": "country", "in": "query" }, { "type": "string", "description": "Filter by period (exact)", "name": "period", "in": "query" }, { "type": "string", "description": "Filter by face value, as written", "name": "denomination", "in": "query" }, { "enum": [ "loose", "coincard", "capsule", "flip", "proof_set", "blister", "slab" ], "type": "string", "description": "Filter by presentation", "name": "presentation", "in": "query" }, { "enum": [ "common", "uncommon", "scarce", "rare", "very_rare", "extremely_rare" ], "type": "string", "description": "Filter by rarity", "name": "rarity", "in": "query" }, { "type": "boolean", "description": "Only coins held more than once", "name": "repeated", "in": "query" }, { "type": "boolean", "description": "Only coins of countries that no longer exist", "name": "extinct", "in": "query" }, { "enum": [ "current", "demonetized" ], "type": "string", "description": "Only current or demonetized coins, as Numista says", "name": "circulation", "in": "query" }, { "type": "boolean", "description": "Only commemorative (true) or regular (false) coins, as Numista says", "name": "commemorative", "in": "query" }, { "type": "string", "description": "Only one Numista kind of coin", "name": "classification", "in": "query" }, { "type": "integer", "description": "Only the coins of a set", "name": "set", "in": "query" }, { "type": "string", "description": "Search, as in the coin list", "name": "search", "in": "query" }, { "enum": [ "created_at", "updated_at", "current_value", "purchase_date", "name", "quantity", "random" ], "type": "string", "default": "updated_at", "description": "Sort field", "name": "sort", "in": "query" }, { "enum": [ "asc", "desc" ], "type": "string", "default": "desc", "description": "Sort order", "name": "order", "in": "query" } ], "responses": { "200": { "description": "The spreadsheet" }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/health": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns paginated per-coin metadata health scores for the authenticated user's active collection.", "produces": [ "application/json" ], "tags": [ "Health" ], "summary": "List coin health scores", "parameters": [ { "enum": [ "all", "needs_attention" ], "type": "string", "default": "all", "description": "Scope filter", "name": "scope", "in": "query" }, { "type": "integer", "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", "default": 25, "description": "Page size", "name": "limit", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.CoinHealthListResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/intake/commit": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Explicitly confirms and commits an intake draft into a persistent coin record.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Commit coin intake draft", "parameters": [ { "description": "Commit request with explicit confirmation and optional overrides", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.IntakeDraftCommitRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.IntakeDraftCommitResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/intake/draft": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Uploads one or more coin images and optionally a coin card image, then returns an editable draft.", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Create coin intake draft", "parameters": [ { "type": "file", "description": "Coin images (use multiple files)", "name": "images", "in": "formData", "required": true }, { "type": "file", "description": "Optional coin card image", "name": "coinCardImage", "in": "formData" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.IntakeDraftCreateResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/lookup": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Analyzes coin/slab images, preserves NGC-first behavior, and proposes bounded Numista evidence/query without running Numista lookup.", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Coin lookup from images", "parameters": [ { "type": "file", "description": "Coin or slab images (use multiple files)", "name": "images", "in": "formData", "required": true }, { "type": "array", "items": { "type": "string" }, "collectionFormat": "multi", "description": "Semantic role for each image: obverse, reverse, or notes", "name": "imageRoles", "in": "formData" }, { "type": "string", "description": "Optional collector-provided identification context (max 2000 characters)", "name": "notes", "in": "formData" }, { "type": "boolean", "description": "Ask for a price range: current dealer comparables when available, otherwise a rough, unverified AI estimate", "name": "includePriceEstimate", "in": "formData" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.CoinLookupSwaggerResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/match-category-era": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Resolves a free-text category or era value against built-in and admin-defined lists", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "coins" ], "summary": "Match a category/era candidate against known values", "parameters": [ { "description": "Candidate value to match", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.MatchCategoryEraRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.MatchCategoryEraResponse" } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/coins/numista-fill": { "get": { "security": [ { "BearerAuth": [] } ], "description": "For the user's coins and banknotes (collection and wishlist) linked to a Numista type whose details the app has cached, lists what a fill would set: the monetary system from the type's currency when none is set; and, for coins, the material from Numista's composition when the coin's is Other or empty, the weight and the diameter when missing, and a coin card as presentation when none is set and Numista says the type is issued in one. Also lists, as corrections, the coins whose material is set but differs from the composition. Changes nothing and makes no Numista request.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Preview a fill of coin records from Numista", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.CoinNumistaFillResult" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "Saves what the preview lists (see GET /coins/numista-fill): only empty fields are filled, all coins or none, from the cached Numista types; no Numista request is made. With corrections=true, the listed material corrections are saved too. Returns what was changed.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Fill coin records from Numista", "parameters": [ { "type": "boolean", "description": "Also replace the materials that differ from Numista's composition", "name": "corrections", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.CoinNumistaFillResult" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/periods": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns the periods of the user's collection coins (excluding the wishlist) of one country, the most recent first, with how many coins each has and the years they span.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "List a country's periods", "parameters": [ { "type": "string", "description": "Country, as the collection names it", "name": "country", "in": "query", "required": true }, { "enum": [ "coin", "banknote" ], "type": "string", "description": "Only coins or only banknotes (every item when omitted)", "name": "kind", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { "$ref": "#/definitions/repository.PeriodCount" } } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/share-list": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Makes, as a PDF (with a photo of each item) or a CSV, the user's duplicates to trade (coins and banknotes held more than once, with how many there are to spare), the wishlist, or both; sorted by country, year and name, in Portuguese or English. Values are included only when asked for, and the contact, when given, is printed at the top and not stored.", "consumes": [ "application/json" ], "produces": [ "application/pdf", "text/csv" ], "tags": [ "Coins" ], "summary": "Download a list to share", "parameters": [ { "description": "Which list and how", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/services.ShareListRequest" } } ], "responses": { "200": { "description": "The file" }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/statuses": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns the IDs of the user's collection coins (not the wishlist's) that are circulation coins still legal tender (current), no longer legal tender (demonetized) or collector coins (nonCirculating), and that are commemorative or not, by the same rules as the summary and the coin list filters: a circulation set on the coin wins, else the cached Numista type says (never asking Numista); coins of extinct countries are not current. A coin nobody knows about is in neither list of a pair.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "List coins by legal tender and commemorative status", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.CoinStatusGroups" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/summary": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Counts the user's collection coins (excluding the wishlist), their distinct types (coins linked to the same Numista type count once; other coins once per distinct name), their distinct countries (and how many of them no longer exist), how many circulation coins are still legal tender (from their cached Numista types; collector coins and coins of extinct countries are not) how many are commemorative and how many are of each Numista kind of coin (standard circulation, circulating commemorative, non-circulating…), how many coins have no country, and how many coins are in a coincard. Also sums the coins' current values (kept in the collection currency) and gives the total in euros, converted with the ECB daily reference rates (cached; null when unavailable), and names the rarest (fixed rarity scale; ties go to the more valuable), oldest (lowest year) and most valuable (current value) coins.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Get collection summary", "parameters": [ { "enum": [ "coin", "banknote" ], "type": "string", "description": "Only the coins or only the banknotes (every item when omitted)", "name": "kind", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.CollectionSummaryResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns a single coin with its images, owned by the authenticated user.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Get a coin", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.Coin" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "Updates an existing coin record. Only the coin owner can update it.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Update a coin", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "description": "Updated coin data", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.CoinUpdateRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.Coin" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "Deletes a coin and all associated image records. Only the coin owner can delete it.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Delete a coin", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.CoinDeletedResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/ai-jobs": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns user-scoped AI jobs for a coin. Set activeOnly=true to include only queued/running jobs.", "produces": [ "application/json" ], "tags": [ "AI Jobs" ], "summary": "List coin AI jobs", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "type": "boolean", "description": "Only queued or running jobs", "name": "activeOnly", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { "$ref": "#/definitions/models.AIJob" } } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/analyze": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Queues asynchronous AI-powered analysis for the obverse or reverse image of a coin. If side is omitted, all coin images are analyzed.", "produces": [ "application/json" ], "tags": [ "Analysis" ], "summary": "Queue coin image analysis", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "enum": [ "obverse", "reverse" ], "type": "string", "description": "Analyze a specific side", "name": "side", "in": "query" } ], "responses": { "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/services.AIJobSubmissionResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "Clears the obverse or reverse AI analysis text for a specific coin.", "produces": [ "application/json" ], "tags": [ "Analysis" ], "summary": "Delete coin analysis", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "enum": [ "obverse", "reverse" ], "type": "string", "description": "Which side's analysis to clear", "name": "side", "in": "query", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.DeleteAnalysisResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/duplicate": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Creates a copy of an existing coin owned by the authenticated user. Images and card-related media/state are not copied.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Duplicate a coin", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.Coin" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/estimate-value": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Queues asynchronous AI-powered current value estimation for a coin owned by the authenticated user.", "produces": [ "application/json" ], "tags": [ "Agent" ], "summary": "Queue coin value estimate", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true } ], "responses": { "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/services.AIJobSubmissionResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/grade": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Queues asynchronous AI-powered coin grade estimation for a coin owned by the authenticated user. The grading report is stored in the AI job result and does not update the coin's saved grade.", "produces": [ "application/json" ], "tags": [ "Analysis" ], "summary": "Queue coin grading", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true } ], "responses": { "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/services.AIJobSubmissionResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/health": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns the computed metadata health score, grade, dimension scores, and checklist of missing items for a specific coin owned by the authenticated user", "produces": [ "application/json" ], "tags": [ "Health" ], "summary": "Get metadata health for a single coin", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.CoinHealthItem" } }, "400": { "description": "Bad Request", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "404": { "description": "Not Found", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": { "type": "string" } } } } } }, "/coins/{id}/images": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Upload an image file for a specific coin. Supports setting image type and primary flag.", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "tags": [ "Images" ], "summary": "Upload a coin image", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "type": "file", "description": "Image file", "name": "image", "in": "formData", "required": true }, { "enum": [ "obverse", "reverse", "detail", "other" ], "type": "string", "default": "other", "description": "Image type", "name": "imageType", "in": "formData" }, { "enum": [ "true", "false" ], "type": "string", "default": "false", "description": "Set as primary image", "name": "isPrimary", "in": "formData" }, { "enum": [ "true", "false" ], "type": "string", "default": "false", "description": "Clip to circular transparent PNG", "name": "circleClip", "in": "formData" } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.CoinImage" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/images/import": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Downloads a photo from Wikimedia Commons or the Numista catalogue (as returned by GET /coin-photos/search) and saves it as a coin image, with thumbnails. The credit, if given, is appended to the coin's notes. A source that refuses the download (e.g. bot protection) returns 502 with code source_refused.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Import a real coin photo", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "description": "Photo to import", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.importImageRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.CoinImage" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "502": { "description": "Bad Gateway", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/images/swap-sides": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Swaps the obverse and reverse labels of a coin's images (e.g. official photos whose source names the sides the other way round) and returns the coin's images. The primary image stays the same.", "produces": [ "application/json" ], "tags": [ "Images" ], "summary": "Swap a coin's obverse and reverse images", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { "$ref": "#/definitions/models.CoinImage" } } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/images/{imageId}": { "delete": { "security": [ { "BearerAuth": [] } ], "description": "Commits metadata deletion before file cleanup. A 202 cleanupPending response can be retried with the same DELETE; startup also retries. Missing files are accepted; fully completed deletions subsequently return 404.", "produces": [ "application/json" ], "tags": [ "Images" ], "summary": "Delete a coin image", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "type": "integer", "description": "Image ID", "name": "imageId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.ImageDeletedResponse" } }, "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/handlers.ImageCleanupPendingResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/journal": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns all activity log entries for a coin, newest first.", "produces": [ "application/json" ], "tags": [ "Journal" ], "summary": "List journal entries", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { "$ref": "#/definitions/models.CoinJournal" } } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "Creates a new activity log entry for a coin.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Journal" ], "summary": "Add journal entry", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "description": "Journal entry", "name": "body", "in": "body", "required": true, "schema": { "type": "object" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.CoinJournal" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/journal/{entryId}": { "delete": { "security": [ { "BearerAuth": [] } ], "description": "Deletes an activity log entry. Only the entry owner can delete it.", "produces": [ "application/json" ], "tags": [ "Journal" ], "summary": "Delete journal entry", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "type": "integer", "description": "Entry ID", "name": "entryId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/listing-status": { "put": { "security": [ { "BearerAuth": [] } ], "description": "Updates the listing status of a coin (e.g., dismiss an unavailable notice).", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Wishlist" ], "summary": "Update coin listing status", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "description": "Status update", "name": "body", "in": "body", "required": true, "schema": { "type": "object" } } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/market-value": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns Numista's estimated price per grade for the issue of the coin's linked Numista type (from its \"Numista\" catalogue reference), plus the type's catalogue photos with credit. Responses are cached for days to stay inside Numista's free plan; when Numista cannot be reached the last stored values are returned with stale=true. Provider problems are reported in status, not as HTTP errors.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Get a coin's Numista market value", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "type": "integer", "description": "Numista issue ID; defaults to the coin's own issue, else the one matching its date, else the first", "name": "issueId", "in": "query" }, { "type": "string", "default": "EUR", "description": "ISO 4217 currency code", "name": "currency", "in": "query" }, { "type": "string", "default": "en", "description": "Language of catalogue data", "name": "lang", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.CoinMarketValue" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/official-photos": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Adds a coin's official images to the sides asked for that have no image yet: the European Central Bank's images of a euro coin's national side (obverse) and common side (reverse), or, for a Portuguese, French or Austrian collector coin, the photos of its mint (INCM, Monnaie de Paris, Münze Österreich). Their credit is appended to the coin's notes. A coin neither has an image of (older collector coins, other currencies) gets none.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Add a coin's official images", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "description": "Sides to fill", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.OfficialPhotosRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.OfficialPhotosResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/purchase": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Sets isWishlist to false, moving the coin from wishlist to collection. Optionally accepts purchase details.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Mark coin as purchased", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "description": "Optional purchase details", "name": "body", "in": "body", "schema": { "$ref": "#/definitions/handlers.PurchaseRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.Coin" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/recommendations": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns suggested missing set assignments with confidence and reasons", "produces": [ "application/json" ], "tags": [ "coins" ], "summary": "List coin recommendations", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "recommendations": { "type": "array", "items": { "$ref": "#/definitions/services.CoinRecommendationItem" } } } } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/coins/{id}/recommendations/{recommendationId}/accept": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Applies a recommended set assignment and marks the recommendation accepted", "produces": [ "application/json" ], "tags": [ "coins" ], "summary": "Accept recommendation", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "type": "integer", "description": "Recommendation ID", "name": "recommendationId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "message": { "type": "string" } } } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/coins/{id}/recommendations/{recommendationId}/reject": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Marks recommendation rejected and records feedback", "produces": [ "application/json" ], "tags": [ "coins" ], "summary": "Reject recommendation", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "type": "integer", "description": "Recommendation ID", "name": "recommendationId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "message": { "type": "string" } } } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/coins/{id}/references": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Adds a structured catalog reference to a user-owned coin.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Coin References" ], "summary": "Create coin reference", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "description": "Reference payload", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.CoinReferenceUpsertRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/handlers.CoinReferenceDTO" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/references/{referenceId}": { "put": { "security": [ { "BearerAuth": [] } ], "description": "Updates one structured reference on a user-owned coin.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Coin References" ], "summary": "Update coin reference", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "type": "integer", "description": "Reference ID", "name": "referenceId", "in": "path", "required": true }, { "description": "Reference payload", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.CoinReferenceUpsertRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.CoinReferenceDTO" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "Deletes one structured reference from a user-owned coin.", "produces": [ "application/json" ], "tags": [ "Coin References" ], "summary": "Delete coin reference", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true }, { "type": "integer", "description": "Reference ID", "name": "referenceId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.MessageResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/status": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Tells whether one of the user's coins is still legal tender (current), whether it was issued for collectors rather than for circulation (nonCirculating) and whether it is commemorative, from its cached Numista type (never asking Numista); coins of countries that no longer exist are not current. A value is null when nobody knows (the coin is not linked to a cached Numista type).", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Get a coin's legal tender and commemorative status", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.CoinStatus" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/value-history": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns value tracking entries for a coin, ordered oldest first.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Get coin value history", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { "$ref": "#/definitions/models.CoinValueHistory" } } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/coins/{id}/wishlist-matches": { "get": { "security": [ { "BearerAuth": [] } ], "description": "The wishlist coins that are the collection coin, so that one just added to the collection can leave the wishlist: of the same Numista type or official photo (the ECB's image of a €2 commemorative, a mint's page of a collector coin), or of the same kind and year under the same name. Numista is not asked. A wishlist coin has none.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Find a collection coin on the wishlist", "parameters": [ { "type": "integer", "description": "Coin ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.WishlistMatchesResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/deep-identification/capability": { "get": { "description": "Returns the Deep Analysis feature-flag state so the client can hide or disable the entry point. The backend remains authoritative; job creation is independently gated.", "produces": [ "application/json" ], "tags": [ "Deep Identification" ], "summary": "Report whether Deep Analysis is enabled for the current user", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.deepCapabilityResponse" } } } } }, "/deep-identification/jobs": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Deep Identification" ], "summary": "List the owner's Deep Analysis jobs", "parameters": [ { "type": "integer", "description": "Filter by coin id", "name": "coinId", "in": "query" }, { "type": "boolean", "description": "Only queued/running jobs", "name": "activeOnly", "in": "query" }, { "type": "string", "description": "Filter by exact job status", "name": "status", "in": "query" }, { "type": "integer", "description": "Page size (default 20, max 100)", "name": "limit", "in": "query" }, { "type": "string", "description": "Opaque pagination cursor from a previous response", "name": "cursor", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.deepJobListResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "Accepts obverse/reverse coin images (uploaded or reused from a saved coin), optional notes, optional ephemeral hint images, and an optional provider override. Idempotent duplicate submissions (same owner, coin, image content hashes, notes, provider override) return the existing in-flight job with reused=true. A distinct submission that would exceed the per-user concurrency limit is refused with 409 job_at_capacity rather than being matched to an unrelated in-flight job.", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "tags": [ "Deep Identification" ], "summary": "Start a Deep Analysis job", "parameters": [ { "type": "integer", "description": "Saved coin to analyse; omit for new-coin intake", "name": "coinId", "in": "formData" }, { "type": "file", "description": "Obverse image; required unless the saved coin already has one", "name": "obverse", "in": "formData" }, { "type": "file", "description": "Reverse image; required unless the saved coin already has one", "name": "reverse", "in": "formData" }, { "type": "file", "description": "Ephemeral hint images (max 3)", "name": "hints", "in": "formData" }, { "type": "string", "description": "Free-text notes (max 2000 chars)", "name": "notes", "in": "formData" }, { "type": "string", "description": "Comma-separated provider override", "name": "providers", "in": "formData" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.deepJobEnvelope" } }, "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/handlers.deepJobEnvelope" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "413": { "description": "Request Entity Too Large", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "415": { "description": "Unsupported Media Type", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "429": { "description": "Too Many Requests", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "503": { "description": "Service Unavailable", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/deep-identification/jobs/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Deep Identification" ], "summary": "Get a Deep Analysis job", "parameters": [ { "type": "integer", "description": "Job ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.deepJobEnvelope" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "tags": [ "Deep Identification" ], "summary": "Delete a Deep Analysis job", "parameters": [ { "type": "integer", "description": "Job ID", "name": "id", "in": "path", "required": true } ], "responses": { "204": { "description": "No Content" }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/deep-identification/jobs/{id}/apply": { "post": { "security": [ { "BearerAuth": [] } ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Deep Identification" ], "summary": "Confirm a Deep Analysis proposal through the existing write path", "parameters": [ { "type": "integer", "description": "Job ID", "name": "id", "in": "path", "required": true }, { "description": "Apply target (draft, coin, or wishlist) and optional field subset", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.deepApplyRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.deepApplyResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/deep-identification/jobs/{id}/cancel": { "post": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Deep Identification" ], "summary": "Cancel a Deep Analysis job", "parameters": [ { "type": "integer", "description": "Job ID", "name": "id", "in": "path", "required": true } ], "responses": { "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/handlers.deepJobEnvelope" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.deepJobEnvelope" } } } } }, "/deep-identification/jobs/{id}/events": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "text/event-stream" ], "tags": [ "Deep Identification" ], "summary": "Stream a Deep Analysis job's events (SSE)", "parameters": [ { "type": "integer", "description": "Job ID", "name": "id", "in": "path", "required": true }, { "type": "integer", "description": "Resume after this sequence number", "name": "since", "in": "query" } ], "responses": { "200": { "description": "OK" }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "410": { "description": "Gone", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "429": { "description": "Too Many Requests", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/deep-identification/jobs/{id}/proposal": { "patch": { "security": [ { "BearerAuth": [] } ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Deep Identification" ], "summary": "Save owner edits and per-field decisions on a Deep Analysis proposal", "parameters": [ { "type": "integer", "description": "Job ID", "name": "id", "in": "path", "required": true }, { "description": "Field edits", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.deepProposalPatchRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/deep-identification/jobs/{id}/retry": { "post": { "security": [ { "BearerAuth": [] } ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Deep Identification" ], "summary": "Retry a Deep Analysis job", "parameters": [ { "type": "integer", "description": "Job ID", "name": "id", "in": "path", "required": true }, { "description": "Optional notes/provider override", "name": "request", "in": "body", "schema": { "$ref": "#/definitions/handlers.deepRetryRequest" } } ], "responses": { "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/handlers.deepJobEnvelope" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "403": { "description": "Forbidden", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/exchange-rates": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns the ECB daily euro reference rates (how much of each currency one euro buys; cached for a few hours, the last stored rates when the ECB cannot be reached) and the currencies a collection can be kept in, to convert amounts between them.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Get exchange rates", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.ExchangeRatesResponse" } }, "503": { "description": "Service Unavailable", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/extract-text": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Uploads an image and uses Ollama to extract visible text (e.g., coin inscriptions).", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "tags": [ "Analysis" ], "summary": "Extract text from image", "parameters": [ { "type": "file", "description": "Image file", "name": "image", "in": "formData", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.ExtractTextResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/internal/tools/collection_summary": { "post": { "description": "Retrieve aggregate statistics for the authenticated user's collection", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Internal" ], "summary": "Get collection aggregate summary", "parameters": [ { "type": "string", "description": "Bearer {internal_token}", "name": "Authorization", "in": "header", "required": true } ], "responses": { "200": { "description": "summary: aggregate summary", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": true } } } } }, "/internal/tools/commit_update": { "post": { "description": "Commit a previously created proposal with explicit confirmation", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Internal" ], "summary": "Commit an update proposal", "parameters": [ { "type": "string", "description": "Bearer {internal_token}", "name": "Authorization", "in": "header", "required": true }, { "description": "Proposal ID, token, and confirmation", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.CommitUpdateRequest" } } ], "responses": { "200": { "description": "result: commit result", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "additionalProperties": true } }, "404": { "description": "Not Found", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": true } } } } }, "/internal/tools/get_coin": { "post": { "description": "Retrieve a coin from the authenticated user's collection", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Internal" ], "summary": "Get a single coin by ID", "parameters": [ { "type": "string", "description": "Bearer {internal_token}", "name": "Authorization", "in": "header", "required": true }, { "description": "Coin ID", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.GetCoinRequest" } } ], "responses": { "200": { "description": "coin: coin summary", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "additionalProperties": true } }, "404": { "description": "Not Found", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": true } } } } }, "/internal/tools/numista_detail": { "post": { "description": "Job-scoped, call-budget-limited Numista detail lookup for the Python deep identification pipeline", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Internal" ], "summary": "Deep-identification Numista detail tool", "parameters": [ { "type": "string", "description": "******", "name": "Authorization", "in": "header", "required": true }, { "description": "Numista catalogue ID", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.NumistaDetailRequest" } } ], "responses": { "200": { "description": "status, candidate, identifier", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "additionalProperties": true } } } } }, "/internal/tools/numista_search": { "post": { "description": "Job-scoped, call-budget-limited Numista search for the Python deep identification pipeline", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Internal" ], "summary": "Deep-identification Numista search tool", "parameters": [ { "type": "string", "description": "******", "name": "Authorization", "in": "header", "required": true }, { "description": "Search query", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.NumistaSearchRequest" } } ], "responses": { "200": { "description": "status, candidates, attribution", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "additionalProperties": true } } } } }, "/internal/tools/propose_update": { "post": { "description": "Create a proposal to update allowlisted fields on a coin", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Internal" ], "summary": "Create an update proposal", "parameters": [ { "type": "string", "description": "Bearer {internal_token}", "name": "Authorization", "in": "header", "required": true }, { "description": "Coin ID and changes", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.ProposeUpdateRequest" } } ], "responses": { "200": { "description": "proposal: proposal preview with token", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "additionalProperties": true } }, "404": { "description": "Not Found", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": true } } } } }, "/internal/tools/search_my_collection": { "post": { "description": "Search the authenticated user's collection by query filters", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Internal" ], "summary": "Search user's collection", "parameters": [ { "type": "string", "description": "Bearer {internal_token}", "name": "Authorization", "in": "header", "required": true }, { "description": "Search parameters", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.SearchMyCollectionRequest" } } ], "responses": { "200": { "description": "coins: array of coin summaries", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": true } } } } }, "/internal/tools/top_coins_by_value": { "post": { "description": "Retrieve the top coins by current value from the authenticated user's collection", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Internal" ], "summary": "Get top coins by current value", "parameters": [ { "type": "string", "description": "Bearer {internal_token}", "name": "Authorization", "in": "header", "required": true }, { "description": "Limit (default 3, max 10)", "name": "body", "in": "body", "schema": { "$ref": "#/definitions/handlers.TopCoinsByValueRequest" } } ], "responses": { "200": { "description": "coins: array of coin summaries", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "additionalProperties": true } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "additionalProperties": true } } } } }, "/mint-locations": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns global mint locations plus the authenticated user's own private ones.", "produces": [ "application/json" ], "tags": [ "Mint Locations" ], "summary": "List mint locations", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.mintLocationListResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "Creates a mint location visible only to the authenticated user.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Mint Locations" ], "summary": "Create a private mint location", "parameters": [ { "description": "Mint location data", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.mintLocationRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.MintLocation" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/mint-locations/geocode": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Looks up coordinate candidates for a place name via OpenStreetMap Nominatim. Only the typed name is sent - no coin, collection, or account data.", "produces": [ "application/json" ], "tags": [ "Mint Locations" ], "summary": "Geocode a mint name", "parameters": [ { "type": "string", "description": "Place name to look up", "name": "query", "in": "query", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.geocodeMintResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/mint-locations/{id}": { "put": { "security": [ { "BearerAuth": [] } ], "description": "Updates a mint location owned by the authenticated user.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Mint Locations" ], "summary": "Update a private mint location", "parameters": [ { "type": "integer", "description": "Mint location ID", "name": "id", "in": "path", "required": true }, { "description": "Mint location data", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.mintLocationRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.MintLocation" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "Deletes a mint location owned by the authenticated user. Fails if any of their coins still reference it.", "produces": [ "application/json" ], "tags": [ "Mint Locations" ], "summary": "Delete a private mint location", "parameters": [ { "type": "integer", "description": "Mint location ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.MessageResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/numista/catalog/issuers": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns up to 20 Numista issuers (countries and authorities) whose name matches q, best matches first. The issuer list is cached for 30 days.", "produces": [ "application/json" ], "tags": [ "Numista" ], "summary": "Search Numista issuers", "parameters": [ { "type": "string", "description": "Part of the issuer name", "name": "q", "in": "query", "required": true }, { "type": "string", "default": "en", "description": "Language of issuer names", "name": "lang", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.NumistaCatalogIssuersResult" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/numista/catalog/search": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns one page (20 types unless count is set) of Numista coin types (or banknote types, with category=banknote) matching the text, issuer and/or catalogue reference (e.g. catalogue=KM, number=84), optionally limited to a year. Searches are cached for a day.", "produces": [ "application/json" ], "tags": [ "Numista" ], "summary": "Search the Numista coin catalogue", "parameters": [ { "type": "string", "description": "Search text (required unless issuer or catalogue is set)", "name": "q", "in": "query" }, { "type": "string", "description": "Numista issuer code, e.g. portugal", "name": "issuer", "in": "query" }, { "enum": [ "KM", "Y" ], "type": "string", "description": "Reference catalogue code (KM or Y); requires number", "name": "catalogue", "in": "query" }, { "type": "string", "description": "Number in the reference catalogue, e.g. 84", "name": "number", "in": "query" }, { "type": "integer", "description": "Year the type was issued (negative for BC)", "name": "year", "in": "query" }, { "type": "integer", "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", "default": 20, "description": "Types per page (1-50)", "name": "count", "in": "query" }, { "type": "string", "default": "en", "description": "Language of results", "name": "lang", "in": "query" }, { "enum": [ "coin", "banknote" ], "type": "string", "default": "coin", "description": "Coins or banknotes", "name": "category", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.NumistaCatalogSearchResult" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/numista/catalog/types/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns a Numista coin type's catalogue details (denomination, rulers, composition, weight, size, mints, designs, references, reference photos with credit) and its issues. Cached for 30 days.", "produces": [ "application/json" ], "tags": [ "Numista" ], "summary": "Get a Numista coin type", "parameters": [ { "type": "integer", "description": "Numista type ID", "name": "id", "in": "path", "required": true }, { "type": "string", "default": "en", "description": "Language of catalogue data", "name": "lang", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.NumistaCatalogTypeResult" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/numista/catalog/types/{id}/issues": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns the issues of a Numista type: year, mint mark, mintage and, for a banknote, the date printed on it, its signatories, its own Pick number (\"176a\") and whether its notes are replacement or specimen notes. Cached for 30 days; no prices are fetched.", "produces": [ "application/json" ], "tags": [ "Numista" ], "summary": "List a Numista type's issues", "parameters": [ { "type": "integer", "description": "Numista type ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.NumistaCatalogIssuesResult" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/numista/catalog/types/{id}/market-value": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns Numista's estimated price per grade for an issue of a coin type, before a coin exists. Same caching and status rules as the coin market value.", "produces": [ "application/json" ], "tags": [ "Numista" ], "summary": "Get a Numista type's market value", "parameters": [ { "type": "integer", "description": "Numista type ID", "name": "id", "in": "path", "required": true }, { "type": "integer", "description": "Numista issue ID; defaults to the issue of year, else the first", "name": "issueId", "in": "query" }, { "type": "integer", "description": "Year used to pick the default issue (negative for BC)", "name": "year", "in": "query" }, { "type": "string", "default": "EUR", "description": "ISO 4217 currency code", "name": "currency", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.CoinMarketValue" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/numista/enrich": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Reranks the complete broad candidate set, enriches at most five server-selected candidates with concurrency two, and retains candidates when details fail.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Numista" ], "summary": "Enrich and rerank Numista candidates", "parameters": [ { "description": "Enrichment request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.NumistaEnrichmentRequestSwagger" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.NumistaLookupOutcomeSwagger" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/numista/lookup": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Searches Numista through the shared application lookup service and returns explained, application-owned candidates.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Numista" ], "summary": "Run broad Numista lookup", "parameters": [ { "description": "Lookup request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.NumistaLookupRequestSwagger" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.NumistaLookupOutcomeSwagger" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/numista/query-proposal": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Builds a generated text-query proposal locally without contacting Numista.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Numista" ], "summary": "Build a Numista query proposal", "parameters": [ { "description": "Proposal request", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.NumistaQueryProposalRequestSwagger" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.NumistaQueryProposalSwagger" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/numista/search": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Deprecated compatibility route preserving the legacy count/types response.", "produces": [ "application/json" ], "tags": [ "Numista" ], "summary": "Search Numista catalog", "parameters": [ { "maxLength": 500, "minLength": 1, "type": "string", "description": "Search query", "name": "q", "in": "query", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.LegacyNumistaSearchResponseSwagger" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "503": { "description": "Service Unavailable", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/ollama-status": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns whether the configured Ollama instance is reachable and the model is available.", "produces": [ "application/json" ], "tags": [ "Analysis" ], "summary": "Check Ollama status", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.OllamaStatusResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/proxy-image": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Fetches an image from an external URL and streams it to the client. Limited to 20MB. Only http/https URLs with image content types are allowed.", "produces": [ "image/*" ], "tags": [ "Images" ], "summary": "Proxy an external image", "parameters": [ { "type": "string", "description": "External image URL", "name": "url", "in": "query", "required": true } ], "responses": { "200": { "description": "Image binary data" }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "502": { "description": "Bad Gateway", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/quick-capture/drafts": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Lists owner-scoped Quick Capture drafts, defaulting to active drafts.", "produces": [ "application/json" ], "tags": [ "Quick Capture" ], "summary": "List Quick Capture drafts", "parameters": [ { "enum": [ "active", "promoted", "discarded" ], "type": "string", "description": "Draft status", "name": "status", "in": "query" }, { "type": "integer", "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", "description": "Page size", "name": "limit", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.quickCaptureDraftListResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "Creates an owner-scoped sparse draft with optional obverse/reverse/detail images.", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "tags": [ "Quick Capture" ], "summary": "Create Quick Capture draft", "parameters": [ { "type": "string", "description": "Working title", "name": "workingTitle", "in": "formData" }, { "type": "string", "description": "Freeform date range", "name": "dateRange", "in": "formData" }, { "type": "string", "description": "Era", "name": "era", "in": "formData" }, { "type": "string", "description": "Acquisition source", "name": "acquisitionSource", "in": "formData" }, { "type": "number", "description": "Purchase price", "name": "purchasePrice", "in": "formData" }, { "type": "string", "description": "Notes", "name": "notes", "in": "formData" }, { "type": "string", "description": "Draft source", "name": "source", "in": "formData" }, { "type": "string", "description": "NGC certification number", "name": "ngcCertNumber", "in": "formData" }, { "type": "string", "description": "NGC lookup URL", "name": "ngcLookupUrl", "in": "formData" }, { "type": "string", "description": "NGC grade", "name": "ngcGrade", "in": "formData" }, { "type": "string", "description": "Visible label text", "name": "labelText", "in": "formData" }, { "type": "string", "description": "AI confidence", "name": "aiConfidence", "in": "formData" }, { "type": "string", "description": "Selected positive Numista type ID", "name": "selectedNumistaId", "in": "formData" }, { "type": "string", "description": "Canonical selected Numista URL", "name": "selectedNumistaUrl", "in": "formData" }, { "type": "file", "description": "Obverse image", "name": "obverseImage", "in": "formData" }, { "type": "file", "description": "Reverse image", "name": "reverseImage", "in": "formData" }, { "type": "file", "description": "Detail images", "name": "detailImages", "in": "formData" } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.QuickCaptureDraft" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/quick-capture/drafts/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns one owner-scoped Quick Capture draft with image metadata.", "produces": [ "application/json" ], "tags": [ "Quick Capture" ], "summary": "Get Quick Capture draft", "parameters": [ { "type": "integer", "description": "Draft ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.QuickCaptureDraft" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "Updates an active owner-scoped draft. Supports field changes and image replacement/removal.", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "tags": [ "Quick Capture" ], "summary": "Update Quick Capture draft", "parameters": [ { "type": "integer", "description": "Draft ID", "name": "id", "in": "path", "required": true }, { "type": "string", "description": "Working title", "name": "workingTitle", "in": "formData" }, { "type": "string", "description": "Notes", "name": "notes", "in": "formData" }, { "type": "string", "description": "Date range", "name": "dateRange", "in": "formData" }, { "type": "string", "description": "Era", "name": "era", "in": "formData" }, { "type": "string", "description": "Acquisition source", "name": "acquisitionSource", "in": "formData" }, { "type": "number", "description": "Purchase price", "name": "purchasePrice", "in": "formData" }, { "type": "string", "description": "Draft source", "name": "source", "in": "formData" }, { "type": "string", "description": "NGC certification number", "name": "ngcCertNumber", "in": "formData" }, { "type": "string", "description": "NGC lookup URL", "name": "ngcLookupUrl", "in": "formData" }, { "type": "string", "description": "NGC grade", "name": "ngcGrade", "in": "formData" }, { "type": "string", "description": "Visible label text", "name": "labelText", "in": "formData" }, { "type": "string", "description": "AI confidence", "name": "aiConfidence", "in": "formData" }, { "type": "string", "description": "Replacement positive Numista type ID; omit to preserve", "name": "selectedNumistaId", "in": "formData" }, { "type": "string", "description": "Replacement canonical Numista URL; omit to preserve", "name": "selectedNumistaUrl", "in": "formData" }, { "type": "boolean", "description": "Remove the retained Numista selection", "name": "clearSelectedNumista", "in": "formData" }, { "type": "string", "description": "Comma-separated image IDs to remove", "name": "removeImageIds", "in": "formData" }, { "type": "boolean", "description": "Replace existing obverse images", "name": "replaceObverse", "in": "formData" }, { "type": "boolean", "description": "Replace existing reverse images", "name": "replaceReverse", "in": "formData" }, { "type": "file", "description": "New obverse image", "name": "obverseImage", "in": "formData" }, { "type": "file", "description": "New reverse image", "name": "reverseImage", "in": "formData" }, { "type": "file", "description": "New detail images", "name": "detailImages", "in": "formData" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.QuickCaptureDraft" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/quick-capture/drafts/{id}/discard": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Marks an active owner-scoped draft as discarded. Idempotent for already-discarded drafts.", "produces": [ "application/json" ], "tags": [ "Quick Capture" ], "summary": "Discard Quick Capture draft", "parameters": [ { "type": "integer", "description": "Draft ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.QuickCaptureDraft" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/quick-capture/drafts/{id}/promote": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Transactionally promotes a valid active draft into a normal Coin. Optional target accepts \"collection\" or \"wishlist\" and defaults to \"collection\". Idempotent on repeat.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Quick Capture" ], "summary": "Promote Quick Capture draft", "parameters": [ { "type": "integer", "description": "Draft ID", "name": "id", "in": "path", "required": true }, { "description": "Promotion request", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.promoteDraftRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "type": "object", "additionalProperties": true } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/references/migrate-legacy": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Migrates legacy rarity_rating text to structured CoinReference records for the authenticated user's coins. Non-destructive operation.", "produces": [ "application/json" ], "tags": [ "Coin References" ], "summary": "Migrate legacy references", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.MigrationResultDTO" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/scrape-image": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Fetches a web page and extracts image URL from og:image, twitter:image, or other meta tags. Useful as a fallback when direct image URLs are unavailable.", "produces": [ "application/json" ], "tags": [ "Images" ], "summary": "Scrape image URL from a web page", "parameters": [ { "type": "string", "description": "Web page URL to scrape", "name": "url", "in": "query", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": { "type": "string" } } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "502": { "description": "Bad Gateway", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/set-builder/proposals/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns one human-reviewable Agentic set proposal with slots and run summary.", "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Get Agentic set proposal", "parameters": [ { "type": "integer", "description": "Proposal ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.SetProposal" } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "Edits proposal metadata and roster slots while the proposal is pending.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Update Agentic set proposal", "parameters": [ { "type": "integer", "description": "Proposal ID", "name": "id", "in": "path", "required": true }, { "description": "Proposal edits", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.updateSetProposalRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.SetProposal" } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "409": { "description": "Conflict", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/set-builder/proposals/{id}/approve": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Approves a pending Agentic set proposal and transactionally creates the Agentic set with target slots.", "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Approve Agentic set proposal", "parameters": [ { "type": "integer", "description": "Proposal ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "set": { "type": "object" } } } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/set-builder/proposals/{id}/regenerate": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Rejects the current pending proposal as superseded and queues a new Python workflow with review feedback.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Regenerate Agentic set proposal", "parameters": [ { "type": "integer", "description": "Proposal ID", "name": "id", "in": "path", "required": true }, { "description": "Regeneration feedback", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.regenerateSetProposalRequest" } } ], "responses": { "202": { "description": "Accepted", "schema": { "type": "object", "properties": { "run": { "$ref": "#/definitions/models.SetBuilderRun" } } } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "409": { "description": "Conflict", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/set-builder/proposals/{id}/reject": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Rejects a pending Agentic set proposal. No set is created.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Reject Agentic set proposal", "parameters": [ { "type": "integer", "description": "Proposal ID", "name": "id", "in": "path", "required": true }, { "description": "Rejection reason", "name": "body", "in": "body", "schema": { "$ref": "#/definitions/handlers.rejectSetProposalRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "status": { "type": "string" } } } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/set-builder/runs": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Submits a natural-language Agentic set prompt for asynchronous proposal generation. No set is created by this endpoint.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Queue Agentic set proposal", "parameters": [ { "description": "Agentic set prompt", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.createSetBuilderRunRequest" } } ], "responses": { "202": { "description": "Accepted", "schema": { "type": "object", "properties": { "run": { "type": "object" } } } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/sets": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Get all sets for the authenticated user with aggregate summaries. A checklist set also has countries (its slots counted country by country: country, owned, total) and partial (true while its list is partly known).", "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "List user sets", "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "sets": { "type": "array", "items": { "type": "object" } } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "Create a new coin set", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Create a set", "parameters": [ { "description": "Set creation data", "name": "body", "in": "body", "required": true, "schema": { "type": "object", "properties": { "color": { "type": "string" }, "description": { "type": "string" }, "icon": { "type": "string" }, "name": { "type": "string" }, "parentSetId": { "type": "number" }, "setType": { "type": "string" } } } } ], "responses": { "201": { "description": "Created", "schema": { "type": "object" } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "409": { "description": "Conflict", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/sets/import-csv": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Create a goal set using custom CSV target definitions", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Create set from CSV", "parameters": [ { "description": "Set data and CSV content", "name": "body", "in": "body", "required": true, "schema": { "type": "object", "properties": { "color": { "type": "string" }, "csv": { "type": "string" }, "description": { "type": "string" }, "name": { "type": "string" }, "setType": { "type": "string" } } } } ], "responses": { "201": { "description": "Created", "schema": { "type": "object" } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/sets/preview-smart": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Preview matching coins before saving a smart set", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Preview smart set", "parameters": [ { "description": "Smart criteria", "name": "body", "in": "body", "required": true, "schema": { "type": "object" } } ], "responses": { "200": { "description": "OK", "schema": { "type": "object" } } } } }, "/sets/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Get detailed information and aggregates for a specific set", "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Get set details", "parameters": [ { "type": "integer", "description": "Set ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object" } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } }, "put": { "security": [ { "BearerAuth": [] } ], "description": "Update set metadata", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Update a set", "parameters": [ { "type": "integer", "description": "Set ID", "name": "id", "in": "path", "required": true }, { "description": "Updated fields", "name": "body", "in": "body", "required": true, "schema": { "type": "object", "properties": { "color": { "type": "string" }, "description": { "type": "string" }, "name": { "type": "string" } } } } ], "responses": { "200": { "description": "OK", "schema": { "type": "object" } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "Delete a set and all its memberships", "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Delete a set", "parameters": [ { "type": "integer", "description": "Set ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "message": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/sets/{id}/analytics": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Get ROI and velocity analytics for a set", "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Get set analytics", "parameters": [ { "type": "integer", "description": "Set ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object" } } } } }, "/sets/{id}/checklist": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Every coin a checklist set lists (a country's coins in the catalogue, or a challenge's), each filled by a coin of the collection when it has one.", "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Get a checklist set's slots", "parameters": [ { "type": "integer", "description": "Set ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.SetChecklistResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/sets/{id}/coins": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Get all coins that belong to a specific set", "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "List coins in a set", "parameters": [ { "type": "integer", "description": "Set ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "coins": { "type": "array", "items": { "type": "object" } } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "Add a coin to a manual set (not smart sets)", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Add coin to set", "parameters": [ { "type": "integer", "description": "Set ID", "name": "id", "in": "path", "required": true }, { "description": "Coin to add", "name": "body", "in": "body", "required": true, "schema": { "type": "object", "properties": { "coinId": { "type": "integer" }, "notes": { "type": "string" }, "targetId": { "type": "integer" } } } } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "message": { "type": "string" } } } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/sets/{id}/coins/order": { "put": { "security": [ { "BearerAuth": [] } ], "description": "Persist the full manual order for all coins in a non-smart set", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Reorder set coins", "parameters": [ { "type": "integer", "description": "Set ID", "name": "id", "in": "path", "required": true }, { "description": "Ordered coin IDs", "name": "body", "in": "body", "required": true, "schema": { "type": "object", "properties": { "coinIds": { "type": "array", "items": { "type": "integer" } } } } } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "message": { "type": "string" } } } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/sets/{id}/coins/{coinId}": { "delete": { "security": [ { "BearerAuth": [] } ], "description": "Remove a coin from a manual set (not smart sets)", "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Remove coin from set", "parameters": [ { "type": "integer", "description": "Set ID", "name": "id", "in": "path", "required": true }, { "type": "integer", "description": "Coin ID", "name": "coinId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "message": { "type": "string" } } } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/sets/{id}/completion": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Get completion metrics including target count, completed count, and missing targets", "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Get set completion", "parameters": [ { "type": "integer", "description": "Set ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "completedTargets": { "type": "integer" }, "completionPercentage": { "type": "number" }, "missingTargets": { "type": "array", "items": { "type": "object" } }, "totalTargets": { "type": "integer" } } } }, "400": { "description": "Bad Request", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "401": { "description": "Unauthorized", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "404": { "description": "Not Found", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } }, "500": { "description": "Internal Server Error", "schema": { "type": "object", "properties": { "error": { "type": "string" } } } } } } }, "/sets/{id}/snapshot": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Capture a manual valuation snapshot for a set", "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Create set snapshot", "parameters": [ { "type": "integer", "description": "Set ID", "name": "id", "in": "path", "required": true } ], "responses": { "201": { "description": "Created", "schema": { "type": "object" } } } } }, "/sets/{id}/trends": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Get valuation snapshots for a set", "produces": [ "application/json" ], "tags": [ "sets" ], "summary": "Get set trends", "parameters": [ { "type": "integer", "description": "Set ID", "name": "id", "in": "path", "required": true }, { "type": "string", "description": "Range: 1m, 3m, 1y, all", "name": "range", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "properties": { "snapshots": { "type": "array", "items": { "type": "object" } } } } } } } }, "/stats": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns counts by category/material, total/average values, and wishlist count.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Get collection statistics", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.StatsResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/stats/distribution": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns era × category counts for the heat map visualization.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Get collection distribution", "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { "$ref": "#/definitions/repository.DistributionCell" } } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/stats/health": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns collection-level metadata health score, dimensions, and checklist summary for the authenticated user.", "produces": [ "application/json" ], "tags": [ "Health" ], "summary": "Get collection health summary", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.CollectionHealthSummary" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/stats/time-machine": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Reconstructs the collection as it stood on the requested date from purchase dates and recorded valuation history. Coins without a purchase date cannot be placed on the timeline and are reported separately in undatedCoinCount.", "produces": [ "application/json" ], "tags": [ "Stats" ], "summary": "Get the collection as of a past date", "parameters": [ { "type": "string", "description": "Target date (YYYY-MM-DD)", "name": "date", "in": "query", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.TimeMachineSnapshot" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/stats/time-machine/bounds": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns the earliest acquisition on record and today, defining the range the timeline scrubber can span. hasData is false when no coin has a purchase date.", "produces": [ "application/json" ], "tags": [ "Stats" ], "summary": "Get the time machine's date range", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.TimeMachineBounds" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/suggestions": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns distinct values for the specified field, optionally filtered by a search query. Limited to 20 results.", "produces": [ "application/json" ], "tags": [ "Coins" ], "summary": "Get autocomplete suggestions", "parameters": [ { "enum": [ "name", "denomination", "ruler", "era", "purchaseLocation", "country", "monetarySystem" ], "type": "string", "description": "Field to get suggestions for", "name": "field", "in": "query", "required": true }, { "type": "string", "description": "Search filter", "name": "q", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "type": "array", "items": { "type": "string" } } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/translate": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Translates a short text (up to 500 bytes) with MyMemory, a free translation service; only the text is sent. Answers are cached for a year. Used to compare coin subjects written in the user's language with Numista's English titles.", "produces": [ "application/json" ], "tags": [ "Numista" ], "summary": "Translate a short text", "parameters": [ { "type": "string", "description": "Text to translate", "name": "q", "in": "query", "required": true }, { "enum": [ "pt", "en", "fr", "es", "de", "it", "nl" ], "type": "string", "description": "Source language", "name": "from", "in": "query", "required": true }, { "enum": [ "pt", "en", "fr", "es", "de", "it", "nl" ], "type": "string", "default": "en", "description": "Target language", "name": "to", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.TranslationResult" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/uploads/{filepath}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Serves an uploaded coin image or avatar only when the authenticated user is authorized to view it. Use the optional size query parameter to request a pre-generated variant (thumb = max 300 px, medium = max 800 px, full = original). If the requested variant does not exist the original is served.", "produces": [ "image/*" ], "tags": [ "Images" ], "summary": "Serve authorized uploaded media", "parameters": [ { "type": "string", "description": "Upload-relative file path", "name": "filepath", "in": "path", "required": true }, { "enum": [ "thumb", "medium", "full" ], "type": "string", "default": "full", "description": "Image size variant", "name": "size", "in": "query" } ], "responses": { "200": { "description": "Image binary data" }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/user/avatar": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Uploads and stores a profile avatar image for the authenticated user.", "consumes": [ "multipart/form-data" ], "produces": [ "application/json" ], "tags": [ "User" ], "summary": "Upload avatar", "parameters": [ { "type": "file", "description": "Avatar image", "name": "avatar", "in": "formData", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "Removes the authenticated user's profile avatar file and clears the avatar path.", "produces": [ "application/json" ], "tags": [ "User" ], "summary": "Delete avatar", "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.MessageResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/user/export": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Downloads all coins and images as a ZIP archive containing coins.json (including era and structured references) and image files.", "produces": [ "application/zip" ], "tags": [ "User" ], "summary": "Export collection", "responses": { "200": { "description": "ZIP archive" }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/user/export/catalog": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Generates a PDF catalog with photos, grades, provenance, valuations, and structured references.", "produces": [ "application/pdf" ], "tags": [ "User" ], "summary": "Export PDF catalog", "responses": { "200": { "description": "PDF document" }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/user/import": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Imports coins from a JSON array. Each coin is created fresh with a new ID for the authenticated user.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "User" ], "summary": "Import collection", "parameters": [ { "description": "Array of coins to import", "name": "body", "in": "body", "required": true, "schema": { "type": "array", "items": { "$ref": "#/definitions/models.Coin" } } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.ImportResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/user/profile": { "put": { "security": [ { "BearerAuth": [] } ], "description": "Updates the profile and the PWA swipe navigation preference of the authenticated user.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "User" ], "summary": "Update profile", "parameters": [ { "description": "Profile fields to update", "name": "body", "in": "body", "required": true, "schema": { "type": "object" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.UserInfoResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/availability-runs": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns paginated availability check run history scoped to the authenticated user.", "produces": [ "application/json" ], "tags": [ "Wishlist" ], "summary": "List my wishlist availability runs", "parameters": [ { "type": "integer", "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", "default": 20, "description": "Items per page", "name": "limit", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/availability-runs/{id}": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Returns a single availability check run with per-coin results, scoped to the authenticated user.", "produces": [ "application/json" ], "tags": [ "Wishlist" ], "summary": "Get my wishlist availability run detail", "parameters": [ { "type": "integer", "description": "Run ID", "name": "id", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/check-availability": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Checks all wishlist items with reference URLs to see if they are still available for purchase.", "produces": [ "application/json" ], "tags": [ "Wishlist" ], "summary": "Check wishlist availability", "responses": { "200": { "description": "OK", "schema": { "type": "object", "additionalProperties": true } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "500": { "description": "Internal Server Error", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/search-alerts": { "get": { "security": [ { "BearerAuth": [] } ], "description": "Lists acquisition discovery search alerts. This is separate from wishlist availability checking.", "produces": [ "application/json" ], "tags": [ "Wishlist Search Alerts" ], "summary": "List wishlist search alerts", "parameters": [ { "type": "boolean", "description": "Filter by active state", "name": "active", "in": "query" }, { "type": "integer", "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", "default": 20, "description": "Items per page", "name": "limit", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.WishlistSearchAlertListResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "post": { "security": [ { "BearerAuth": [] } ], "description": "Creates a saved discovery alert without creating wishlist items or availability rows.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Wishlist Search Alerts" ], "summary": "Create wishlist search alert", "parameters": [ { "description": "Alert", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.WishlistSearchAlertRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/models.WishlistSearchAlert" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/search-alerts/{alertId}": { "put": { "security": [ { "BearerAuth": [] } ], "description": "Updates saved discovery criteria/cadence/active metadata without changing availability state.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Wishlist Search Alerts" ], "summary": "Update wishlist search alert", "parameters": [ { "type": "integer", "description": "Alert ID", "name": "alertId", "in": "path", "required": true }, { "description": "Alert", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.WishlistSearchAlertRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.WishlistSearchAlert" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } }, "delete": { "security": [ { "BearerAuth": [] } ], "description": "Soft-deletes a saved discovery alert while preserving history.", "produces": [ "application/json" ], "tags": [ "Wishlist Search Alerts" ], "summary": "Delete wishlist search alert", "parameters": [ { "type": "integer", "description": "Alert ID", "name": "alertId", "in": "path", "required": true } ], "responses": { "204": { "description": "No Content" }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/search-alerts/{alertId}/candidates": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Wishlist Search Alerts" ], "summary": "List wishlist search alert candidates", "parameters": [ { "type": "integer", "description": "Alert ID", "name": "alertId", "in": "path", "required": true }, { "type": "string", "description": "Candidate state", "name": "state", "in": "query" }, { "type": "string", "description": "Provenance status", "name": "provenanceStatus", "in": "query" }, { "type": "integer", "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", "default": 20, "description": "Items per page", "name": "limit", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.WishlistSearchAlertCandidateListResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/search-alerts/{alertId}/candidates/{candidateId}/convert": { "post": { "security": [ { "BearerAuth": [] } ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Wishlist Search Alerts" ], "summary": "Convert wishlist search alert candidate", "parameters": [ { "type": "integer", "description": "Alert ID", "name": "alertId", "in": "path", "required": true }, { "type": "integer", "description": "Candidate ID", "name": "candidateId", "in": "path", "required": true }, { "description": "Convert request", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.WishlistSearchAlertConvertRequest" } } ], "responses": { "201": { "description": "Created", "schema": { "$ref": "#/definitions/handlers.WishlistSearchAlertConvertResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "409": { "description": "Conflict", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/search-alerts/{alertId}/candidates/{candidateId}/dismiss": { "post": { "security": [ { "BearerAuth": [] } ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Wishlist Search Alerts" ], "summary": "Dismiss wishlist search alert candidate", "parameters": [ { "type": "integer", "description": "Alert ID", "name": "alertId", "in": "path", "required": true }, { "type": "integer", "description": "Candidate ID", "name": "candidateId", "in": "path", "required": true }, { "description": "Dismiss request", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.WishlistSearchAlertDismissRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.AlertCandidate" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/search-alerts/{alertId}/candidates/{candidateId}/restore": { "post": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Wishlist Search Alerts" ], "summary": "Restore wishlist search alert candidate", "parameters": [ { "type": "integer", "description": "Alert ID", "name": "alertId", "in": "path", "required": true }, { "type": "integer", "description": "Candidate ID", "name": "candidateId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.AlertCandidate" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/search-alerts/{alertId}/criteria-adjustments": { "post": { "security": [ { "BearerAuth": [] } ], "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Wishlist Search Alerts" ], "summary": "Adjust wishlist search alert criteria", "parameters": [ { "type": "integer", "description": "Alert ID", "name": "alertId", "in": "path", "required": true }, { "description": "Criteria adjustment", "name": "body", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.WishlistSearchAlertCriteriaAdjustRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.WishlistSearchAlert" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/search-alerts/{alertId}/run": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Queues a saved discovery alert run and stores candidates separately from availability checks when processing completes.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Wishlist Search Alerts" ], "summary": "Run wishlist search alert", "parameters": [ { "type": "integer", "description": "Alert ID", "name": "alertId", "in": "path", "required": true }, { "description": "Run request", "name": "body", "in": "body", "schema": { "$ref": "#/definitions/handlers.WishlistSearchAlertRunRequest" } } ], "responses": { "202": { "description": "Accepted", "schema": { "$ref": "#/definitions/handlers.WishlistSearchAlertRunResponse" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "503": { "description": "Service Unavailable", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/search-alerts/{alertId}/runs": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Wishlist Search Alerts" ], "summary": "List wishlist search alert runs", "parameters": [ { "type": "integer", "description": "Alert ID", "name": "alertId", "in": "path", "required": true }, { "type": "integer", "default": 1, "description": "Page number", "name": "page", "in": "query" }, { "type": "integer", "default": 20, "description": "Items per page", "name": "limit", "in": "query" } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/handlers.WishlistSearchAlertRunListResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/search-alerts/{alertId}/runs/{runId}": { "get": { "security": [ { "BearerAuth": [] } ], "produces": [ "application/json" ], "tags": [ "Wishlist Search Alerts" ], "summary": "Get wishlist search alert run", "parameters": [ { "type": "integer", "description": "Alert ID", "name": "alertId", "in": "path", "required": true }, { "type": "integer", "description": "Run ID", "name": "runId", "in": "path", "required": true } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/models.AlertRun" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "404": { "description": "Not Found", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } }, "/wishlist/url-intake/analyze": { "post": { "security": [ { "BearerAuth": [] } ], "description": "Retrieves one bounded public HTML page and returns an editable transient coin proposal. No coin is created.", "consumes": [ "application/json" ], "produces": [ "application/json" ], "tags": [ "Wishlist" ], "summary": "Analyze wishlist listing URL", "parameters": [ { "description": "Public listing URL", "name": "request", "in": "body", "required": true, "schema": { "$ref": "#/definitions/handlers.wishlistURLRequest" } } ], "responses": { "200": { "description": "OK", "schema": { "$ref": "#/definitions/services.WishlistURLAnalysis" } }, "400": { "description": "Bad Request", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "401": { "description": "Unauthorized", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "413": { "description": "Request Entity Too Large", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "422": { "description": "Unprocessable Entity", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } }, "502": { "description": "Bad Gateway", "schema": { "$ref": "#/definitions/handlers.ErrorResponse" } } } } } }, "definitions": { "handlers.AIStatusResponse": { "type": "object", "properties": { "available": { "type": "boolean", "example": true }, "message": { "type": "string", "example": "Anthropic provider configured (model: claude-sonnet-5)" }, "model": { "type": "string", "example": "claude-sonnet-5" }, "provider": { "type": "string", "example": "anthropic" } } }, "handlers.AdminWishlistSearchAlertRun": { "type": "object", "properties": { "alertId": { "type": "integer" }, "alertName": { "type": "string" }, "completedAt": { "type": "string" }, "dismissedCount": { "type": "integer" }, "duplicateCount": { "type": "integer" }, "durationMs": { "type": "integer" }, "errorMessage": { "type": "string" }, "id": { "type": "integer" }, "newCount": { "type": "integer" }, "partialWarnings": { "type": "array", "items": { "type": "string" } }, "rateLimitStatus": { "type": "string" }, "resultCount": { "type": "integer" }, "startedAt": { "type": "string" }, "status": { "$ref": "#/definitions/models.AlertRunStatus" }, "triggerType": { "$ref": "#/definitions/models.AlertRunTriggerType" }, "userId": { "type": "integer" }, "userName": { "type": "string" } } }, "handlers.AdminWishlistSearchAlertRunListResponse": { "type": "object", "properties": { "limit": { "type": "integer" }, "page": { "type": "integer" }, "runs": { "type": "array", "items": { "$ref": "#/definitions/handlers.AdminWishlistSearchAlertRun" } }, "total": { "type": "integer" } } }, "handlers.AgentChatMessage": { "type": "object", "properties": { "content": { "type": "string" }, "role": { "type": "string" } } }, "handlers.AgentChatRequest": { "type": "object", "required": [ "message" ], "properties": { "appContext": { "$ref": "#/definitions/services.CollectionChatContext" }, "history": { "type": "array", "items": { "$ref": "#/definitions/handlers.AgentChatMessage" } }, "message": { "type": "string" } } }, "handlers.AgentChatResponse": { "type": "object", "properties": { "message": { "type": "string" }, "suggestions": { "type": "array", "items": { "$ref": "#/definitions/handlers.CoinSuggestion" } } } }, "handlers.AuthResponse": { "type": "object", "properties": { "refreshToken": { "type": "string", "example": "rt_a1b2c3d4..." }, "token": { "type": "string", "example": "eyJhbGciOiJIUzI1NiIs..." }, "user": { "$ref": "#/definitions/handlers.AuthUserInfo" } } }, "handlers.AuthUserInfo": { "type": "object", "properties": { "id": { "type": "integer", "example": 1 }, "role": { "type": "string", "example": "admin" }, "username": { "type": "string", "example": "admin" } } }, "handlers.BackupsResponse": { "type": "object", "properties": { "backups": { "type": "array", "items": { "$ref": "#/definitions/services.BackupInfo" } }, "enabled": { "description": "Enabled, StartTime and Keep are the automatic backup's settings: on, its\ndaily time, how many automatic backups are kept.", "type": "boolean" }, "folder": { "description": "Folder is where the backups are kept.", "type": "string", "example": "~/Aurearia-backups/aurearia-dev" }, "keep": { "type": "integer", "example": 7 }, "lastRestore": { "$ref": "#/definitions/services.RestoreResult" }, "nextRunIn": { "description": "NextRunIn is the time to the next automatic backup, in seconds.", "type": "integer", "example": 3600 }, "running": { "description": "Running is true while a backup is being made.", "type": "boolean" }, "startTime": { "type": "string", "example": "03:00" } } }, "handlers.BulkActionRequest": { "type": "object", "required": [ "action", "coinIds" ], "properties": { "action": { "type": "string" }, "coinIds": { "type": "array", "items": { "type": "integer" } }, "setId": { "type": "integer" } } }, "handlers.CandidateReferenceDTORef": { "type": "object", "properties": { "catalog": { "type": "string" }, "number": { "type": "string" }, "uri": { "type": "string" }, "volume": { "type": "string" } } }, "handlers.CandidateReferenceSwagger": { "type": "object", "properties": { "catalog": { "type": "string", "example": "NGC" }, "number": { "type": "string", "example": "823160-093" }, "uri": { "type": "string", "example": "https://www.ngccoin.com/certlookup/823160-093/" }, "volume": { "type": "string", "example": "" } } }, "handlers.CatalogueCountry": { "type": "object", "properties": { "collector": { "type": "integer" }, "commemoratives": { "type": "integer" }, "country": { "type": "string", "example": "PT" }, "currency": { "description": "Currency is \"PTE\" for Portugal's coins in escudos; empty for the euro.", "type": "string", "enum": [ "", "PTE" ] }, "ownedCollector": { "type": "integer" }, "ownedCommemoratives": { "type": "integer" } } }, "handlers.CatalogueOverrideRequest": { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "string", "maxLength": 600 }, "sameAs": { "type": "string", "maxLength": 600 } } }, "handlers.CategoryCount": { "type": "object", "properties": { "category": { "type": "string", "example": "Modern" }, "count": { "type": "integer", "example": 10 } } }, "handlers.ChangePasswordRequest": { "type": "object", "required": [ "currentPassword", "newPassword" ], "properties": { "currentPassword": { "type": "string", "example": "oldpass123" }, "newPassword": { "type": "string", "minLength": 6, "example": "newpass456" } } }, "handlers.CoinCatalogueCountriesResponse": { "type": "object", "properties": { "countries": { "type": "array", "items": { "$ref": "#/definitions/handlers.CatalogueCountry" } } } }, "handlers.CoinCatalogueResponse": { "type": "object", "properties": { "coins": { "type": "array", "items": { "$ref": "#/definitions/services.CoinRelease" } }, "complete": { "type": "boolean" }, "country": { "type": "string", "example": "PT" }, "hidden": { "description": "Hidden are the coins the user hid, and those the user joined to\nanother (sameAs), which is listed in their place.", "type": "array", "items": { "$ref": "#/definitions/services.CoinRelease" } } } }, "handlers.CoinCreateRequest": { "type": "object", "properties": { "category": { "maxLength": 64, "allOf": [ { "$ref": "#/definitions/models.Category" } ] }, "certificationNumber": { "type": "string", "maxLength": 60 }, "certifiedGrade": { "type": "string", "maxLength": 60 }, "circulation": { "description": "Circulation set by hand; empty means \"as Numista says\".", "type": "string", "maxLength": 30, "enum": [ "", "current", "current_commemorative", "demonetized", "demonetized_commemorative", "non_circulating", "non_circulating_commemorative" ] }, "country": { "type": "string", "maxLength": 120 }, "currentValue": { "type": "number" }, "dateRange": { "type": "string", "maxLength": 200 }, "denomination": { "type": "string", "maxLength": 200 }, "diameterMm": { "type": "number" }, "era": { "maxLength": 64, "allOf": [ { "$ref": "#/definitions/models.Era" } ] }, "grade": { "type": "string", "maxLength": 100 }, "heightMm": { "type": "number" }, "isWishlist": { "type": "boolean" }, "kind": { "description": "Kind is a coin (default) or a banknote.", "type": "string", "maxLength": 16, "enum": [ "coin", "banknote" ] }, "material": { "$ref": "#/definitions/models.Material" }, "mint": { "type": "string", "maxLength": 200 }, "mintLocationId": { "type": "integer" }, "monetarySystem": { "description": "MonetarySystem as Numista names it (\"Escudo (1911-2001)\", \"Euro\").", "type": "string", "maxLength": 120 }, "name": { "type": "string", "maxLength": 200 }, "noteVariant": { "type": "string", "maxLength": 20, "enum": [ "", "replacement", "specimen" ] }, "notes": { "type": "string", "maxLength": 5000 }, "numistaIssueId": { "type": "integer", "maximum": 2147483647, "minimum": 0 }, "obverseDescription": { "type": "string", "maxLength": 2000 }, "obverseInscription": { "type": "string", "maxLength": 1000 }, "presentation": { "type": "string", "maxLength": 20, "enum": [ "loose", "coincard", "capsule", "flip", "proof_set", "blister", "slab" ] }, "printer": { "type": "string", "maxLength": 200 }, "purchaseDate": { "type": "string" }, "purchaseLocation": { "type": "string", "maxLength": 500 }, "purchasePrice": { "type": "number" }, "quantity": { "description": "Quantity is how many the collector has; 1 when omitted.", "type": "integer", "maximum": 9999, "minimum": 1 }, "rarity": { "type": "string", "maxLength": 20, "enum": [ "common", "uncommon", "scarce", "rare", "very_rare", "extremely_rare" ] }, "rarityRating": { "type": "string", "maxLength": 100 }, "referenceText": { "type": "string", "maxLength": 2000 }, "referenceUrl": { "type": "string", "maxLength": 2000 }, "references": { "type": "array", "items": { "$ref": "#/definitions/handlers.CoinReferenceRequest" } }, "reverseDescription": { "type": "string", "maxLength": 2000 }, "reverseInscription": { "type": "string", "maxLength": 1000 }, "ruler": { "type": "string", "maxLength": 200 }, "serialNumber": { "type": "string", "maxLength": 100 }, "series": { "description": "Banknote details: series, serial number, signatories, printer, size,\nwatermark, replacement or specimen, and a grading company's grade.\nNumistaIssueID is the issue of the linked Numista type the item is (0: none).", "type": "string", "maxLength": 200 }, "signatures": { "type": "string", "maxLength": 300 }, "vendorInvoice": { "type": "string", "maxLength": 200 }, "vendorSku": { "type": "string", "maxLength": 200 }, "watermark": { "type": "string", "maxLength": 500 }, "weightGrams": { "type": "number" }, "widthMm": { "type": "number" }, "year": { "description": "Year is the coin's year (negative for BC); when omitted it is read from dateRange.", "type": "integer", "maximum": 3000, "minimum": -5000 } } }, "handlers.CoinDeletedResponse": { "type": "object", "properties": { "message": { "type": "string", "example": "Coin deleted" } } }, "handlers.CoinListResponse": { "type": "object", "properties": { "coins": { "type": "array", "items": { "$ref": "#/definitions/models.Coin" } }, "limit": { "type": "integer", "example": 50 }, "page": { "type": "integer", "example": 1 }, "total": { "type": "integer", "example": 42 } } }, "handlers.CoinLookupSwaggerResponse": { "type": "object", "properties": { "candidateReferences": { "type": "array", "items": { "$ref": "#/definitions/handlers.CandidateReferenceSwagger" } }, "extractedData": { "$ref": "#/definitions/handlers.LookupExtractedDataSwagger" }, "numistaCandidates": { "type": "array", "items": { "$ref": "#/definitions/handlers.NumistaCandidateSwagger" } }, "numistaEvidence": { "$ref": "#/definitions/models.NumistaEvidence" }, "numistaLookup": { "allOf": [ { "$ref": "#/definitions/models.NumistaLookupOutcome" } ], "x-nullable": true }, "prefilledDraft": { "type": "object", "additionalProperties": {} }, "priceComparables": { "$ref": "#/definitions/handlers.LookupPriceComparablesSwagger" }, "priceEstimate": { "$ref": "#/definitions/handlers.LookupPriceEstimateSwagger" }, "proposedNumistaQuery": { "type": "string" } } }, "handlers.CoinOptionLabels": { "type": "object", "properties": { "categories": { "type": "object", "additionalProperties": { "type": "string" } }, "eras": { "type": "object", "additionalProperties": { "type": "string" } } } }, "handlers.CoinOptionsResponse": { "type": "object", "properties": { "categories": { "type": "array", "items": { "type": "string" } }, "currency": { "description": "Currency is the collection currency coin prices and values are kept in.", "type": "string", "example": "EUR" }, "eras": { "type": "array", "items": { "type": "string" } }, "labels": { "type": "object", "additionalProperties": { "$ref": "#/definitions/handlers.CoinOptionLabels" } } } }, "handlers.CoinReferenceDTO": { "type": "object", "properties": { "catalog": { "type": "string", "example": "RIC" }, "coinId": { "type": "integer", "example": 1 }, "createdAt": { "type": "string", "example": "2025-01-15T12:00:00Z" }, "id": { "type": "integer", "example": 42 }, "number": { "type": "string", "example": "234" }, "updatedAt": { "type": "string", "example": "2025-01-15T12:00:00Z" }, "uri": { "type": "string", "example": "https://en.numista.com/catalogue/pieces1490.html" }, "volume": { "type": "string", "example": "III" } } }, "handlers.CoinReferenceRequest": { "type": "object", "properties": { "catalog": { "type": "string", "maxLength": 32 }, "number": { "type": "string", "maxLength": 128 }, "uri": { "type": "string", "maxLength": 2000 }, "volume": { "type": "string", "maxLength": 64 } } }, "handlers.CoinReferenceUpsertRequest": { "type": "object", "properties": { "catalog": { "type": "string", "example": "RIC" }, "number": { "type": "string", "example": "234" }, "uri": { "type": "string", "example": "https://en.numista.com/catalogue/pieces1490.html" }, "volume": { "type": "string", "example": "III" } } }, "handlers.CoinReleasesResponse": { "type": "object", "properties": { "groups": { "type": "array", "items": { "$ref": "#/definitions/services.CoinReleaseGroup" } } } }, "handlers.CoinStatusGroups": { "type": "object", "properties": { "commemorative": { "type": "array", "items": { "type": "integer" } }, "current": { "description": "Current are circulation coins still legal tender.", "type": "array", "items": { "type": "integer" } }, "demonetized": { "type": "array", "items": { "type": "integer" } }, "nonCirculating": { "description": "NonCirculating are collector coins (not demonetized).", "type": "array", "items": { "type": "integer" } }, "regular": { "type": "array", "items": { "type": "integer" } } } }, "handlers.CoinSuggestion": { "type": "object", "properties": { "candidateReferences": { "type": "array", "items": { "$ref": "#/definitions/handlers.CandidateReferenceDTORef" } }, "category": { "type": "string" }, "denomination": { "type": "string" }, "description": { "type": "string" }, "era": { "type": "string" }, "estPrice": { "type": "string" }, "imageUrl": { "type": "string" }, "material": { "type": "string" }, "name": { "type": "string" }, "ruler": { "type": "string" }, "sourceName": { "type": "string" }, "sourceUrl": { "type": "string" } } }, "handlers.CoinUpdateRequest": { "type": "object", "properties": { "category": { "maxLength": 64, "allOf": [ { "$ref": "#/definitions/models.Category" } ] }, "certificationNumber": { "type": "string", "maxLength": 60 }, "certifiedGrade": { "type": "string", "maxLength": 60 }, "circulation": { "description": "Circulation set by hand; empty means \"as Numista says\".", "type": "string", "maxLength": 30, "enum": [ "", "current", "current_commemorative", "demonetized", "demonetized_commemorative", "non_circulating", "non_circulating_commemorative" ] }, "country": { "type": "string", "maxLength": 120 }, "currentValue": { "type": "number" }, "dateRange": { "type": "string", "maxLength": 200 }, "denomination": { "type": "string", "maxLength": 200 }, "diameterMm": { "type": "number" }, "era": { "maxLength": 64, "allOf": [ { "$ref": "#/definitions/models.Era" } ] }, "grade": { "type": "string", "maxLength": 100 }, "heightMm": { "type": "number" }, "isWishlist": { "type": "boolean" }, "kind": { "description": "Kind is a coin or a banknote.", "type": "string", "maxLength": 16, "enum": [ "coin", "banknote" ] }, "material": { "$ref": "#/definitions/models.Material" }, "mint": { "type": "string", "maxLength": 200 }, "mintLocationId": { "type": "integer" }, "monetarySystem": { "description": "MonetarySystem as Numista names it (\"Escudo (1911-2001)\", \"Euro\").", "type": "string", "maxLength": 120 }, "name": { "type": "string", "maxLength": 200 }, "noteVariant": { "type": "string", "maxLength": 20, "enum": [ "", "replacement", "specimen" ] }, "notes": { "type": "string", "maxLength": 5000 }, "numistaIssueId": { "type": "integer", "maximum": 2147483647, "minimum": 0 }, "obverseDescription": { "type": "string", "maxLength": 2000 }, "obverseInscription": { "type": "string", "maxLength": 1000 }, "presentation": { "type": "string", "maxLength": 20, "enum": [ "loose", "coincard", "capsule", "flip", "proof_set", "blister", "slab" ] }, "printer": { "type": "string", "maxLength": 200 }, "purchaseDate": { "type": "string" }, "purchaseLocation": { "type": "string", "maxLength": 500 }, "purchasePrice": { "type": "number" }, "quantity": { "description": "Quantity is how many the collector has (1 or more).", "type": "integer", "maximum": 9999, "minimum": 1 }, "rarity": { "type": "string", "maxLength": 20, "enum": [ "common", "uncommon", "scarce", "rare", "very_rare", "extremely_rare" ] }, "rarityRating": { "type": "string", "maxLength": 100 }, "referenceText": { "type": "string", "maxLength": 2000 }, "referenceUrl": { "type": "string", "maxLength": 2000 }, "references": { "type": "array", "items": { "$ref": "#/definitions/handlers.CoinReferenceRequest" } }, "reverseDescription": { "type": "string", "maxLength": 2000 }, "reverseInscription": { "type": "string", "maxLength": 1000 }, "ruler": { "type": "string", "maxLength": 200 }, "serialNumber": { "type": "string", "maxLength": 100 }, "series": { "description": "Banknote details.", "type": "string", "maxLength": 200 }, "signatures": { "type": "string", "maxLength": 300 }, "vendorInvoice": { "type": "string", "maxLength": 200 }, "vendorSku": { "type": "string", "maxLength": 200 }, "watermark": { "type": "string", "maxLength": 500 }, "weightGrams": { "type": "number" }, "widthMm": { "type": "number" }, "year": { "description": "Year (negative for BC); null means \"read it from dateRange\".", "type": "integer", "maximum": 3000, "minimum": -5000 } } }, "handlers.CollectionClassification": { "type": "object", "properties": { "count": { "type": "integer" }, "key": { "description": "Key is \"standard\", \"circulating-commemorative\", \"non-circulating\" or,\nfor another kind, its label; it is the coin list's classification filter.", "type": "string" }, "label": { "description": "Label is Numista's name for it, in the language it was looked up in.", "type": "string" } } }, "handlers.CollectionCurrencyRequest": { "type": "object", "required": [ "currency" ], "properties": { "currency": { "description": "Currency is EUR or a currency of the ECB reference rates (USD, GBP, CHF…).", "type": "string", "example": "EUR" } } }, "handlers.CollectionSummaryResponse": { "type": "object", "properties": { "circulationUnknown": { "type": "integer" }, "classifications": { "description": "Classifications are Numista's kinds of coin found in the collection,\nthe usual ones first; coins Numista says nothing of are in none. Each\ncoin counts once, as the gallery lists it.", "type": "array", "items": { "$ref": "#/definitions/handlers.CollectionClassification" } }, "coincards": { "description": "Coincards counts coins whose presentation is \"coincard\".", "type": "integer" }, "coins": { "type": "integer" }, "commemorativeCoins": { "description": "CommemorativeCoins are of types Numista says commemorate something;\nCommemorativeUnknown are coins it says nothing of.", "type": "integer" }, "commemorativeUnknown": { "type": "integer" }, "countries": { "type": "integer" }, "currency": { "description": "Currency is the collection currency (an ISO code: EUR, USD, GBP…):\nTotalValue and the highlights' values are in it.", "type": "string", "example": "EUR" }, "currentCoins": { "description": "CurrentCoins are circulation coins still legal tender, as Numista says\nof their types (collector coins are not);\nCirculationUnknown are coins it says nothing of (not linked to Numista).\nThese four count every coin held, with its quantity.", "type": "integer" }, "exchangeRate": { "description": "ExchangeRate is the latest ECB rate for the US dollar, when available.", "allOf": [ { "$ref": "#/definitions/services.ExchangeRate" } ] }, "extinctCountries": { "description": "ExtinctCountries counts the collection's countries that no longer exist\n(USSR, Yugoslavia, Ottoman Empire…); they are among Countries.", "type": "integer" }, "highlights": { "$ref": "#/definitions/repository.CollectionHighlights" }, "repeatedCoins": { "description": "RepeatedCoins are the different coins held more than once; SpareCoins\nthe copies beyond the first, there to trade.", "type": "integer" }, "spareCoins": { "type": "integer" }, "totalValue": { "description": "TotalValue sums the coins' current values times their quantities, in\nthe collection currency.", "type": "number" }, "totalValueEur": { "description": "TotalValueEUR is TotalValue in euros: as is for a euro collection, else\nconverted with the ECB rate of its currency; null when no rate is available.", "type": "number" }, "types": { "description": "Types counts distinct coin types: coins linked to the same Numista type\ncount once; other coins count once per distinct name.", "type": "integer" }, "uniqueCoins": { "description": "UniqueCoins counts each coin once, whatever its quantity.", "type": "integer" }, "unvaluedCoins": { "description": "UnvaluedCoins have no current value and are left out of TotalValue.", "type": "integer" }, "withoutCountry": { "type": "integer" } } }, "handlers.CommitCollectionProposalRequest": { "type": "object", "required": [ "proposalToken" ], "properties": { "confirm": { "type": "boolean" }, "proposalToken": { "type": "string" } } }, "handlers.CommitUpdateRequest": { "type": "object", "required": [ "confirm", "proposal_id", "token" ], "properties": { "confirm": { "type": "boolean" }, "proposal_id": { "type": "string" }, "token": { "type": "string" } } }, "handlers.ConversationSummary": { "type": "object", "properties": { "createdAt": { "type": "string" }, "id": { "type": "integer" }, "title": { "type": "string" }, "updatedAt": { "type": "string" } } }, "handlers.DeleteAnalysisResponse": { "type": "object", "properties": { "coin": { "$ref": "#/definitions/models.Coin" } } }, "handlers.ErrorResponse": { "type": "object", "properties": { "code": { "type": "string", "example": "validation_error" }, "count": { "type": "integer", "minimum": 0, "x-nullable": true }, "error": { "type": "string", "example": "Something went wrong" }, "field": { "type": "string", "example": "name" }, "message": { "type": "string", "example": "Choose another name and try again." } } }, "handlers.ExchangeRatesResponse": { "type": "object", "properties": { "currencies": { "description": "Currencies are the collection currencies (the euro first).", "type": "array", "items": { "type": "string" } }, "date": { "description": "Date is the ECB reference date, YYYY-MM-DD.", "type": "string" }, "perEur": { "description": "PerEUR is how much of each currency one euro buys (\"USD\": 1.1206).", "type": "object", "additionalProperties": { "type": "number", "format": "float64" } } } }, "handlers.ExtractTextResponse": { "type": "object", "properties": { "text": { "type": "string", "example": "CAESAR AVGVSTVS" } } }, "handlers.GetCoinRequest": { "type": "object", "required": [ "coin_id" ], "properties": { "coin_id": { "type": "integer" } } }, "handlers.ImageCleanupPendingResponse": { "type": "object", "properties": { "cleanupPending": { "type": "boolean" }, "message": { "type": "string" } } }, "handlers.ImageDeletedResponse": { "type": "object", "properties": { "message": { "type": "string", "example": "Image deleted" } } }, "handlers.ImportResponse": { "type": "object", "properties": { "imported": { "type": "integer", "example": 10 }, "message": { "type": "string", "example": "Import complete" } } }, "handlers.IntakeConfidenceSummary": { "type": "object", "properties": { "overall": { "type": "string", "example": "medium" }, "uncertainFields": { "type": "array", "items": { "type": "string" } } } }, "handlers.IntakeDraftCommitRequest": { "type": "object", "required": [ "confirm", "draftId" ], "properties": { "confirm": { "type": "boolean", "example": true }, "draftId": { "type": "integer", "example": 42 }, "overrides": { "type": "object", "additionalProperties": true } } }, "handlers.IntakeDraftCommitResponse": { "type": "object", "properties": { "coinId": { "type": "integer", "example": 314 }, "draftId": { "type": "integer", "example": 42 }, "status": { "type": "string", "example": "confirmed" } } }, "handlers.IntakeDraftCreateResponse": { "type": "object", "properties": { "coin": { "type": "object", "additionalProperties": true }, "confidenceSummary": { "$ref": "#/definitions/handlers.IntakeConfidenceSummary" }, "draftId": { "type": "integer", "example": 42 }, "evidence": { "type": "array", "items": { "$ref": "#/definitions/handlers.IntakeEvidenceItem" } }, "expiresAt": { "type": "string", "example": "2026-01-15T12:00:00Z" }, "status": { "type": "string", "example": "drafted" }, "unresolvedFields": { "type": "array", "items": { "type": "string" } } } }, "handlers.IntakeEvidenceItem": { "type": "object", "properties": { "confidence": { "type": "string", "example": "medium" }, "field": { "type": "string", "example": "ruler" }, "notes": { "type": "string", "example": "Legend partially visible" }, "source": { "type": "string", "example": "obverse" }, "type": { "type": "string", "example": "vision" }, "value": { "type": "string", "example": "Trajan" } } }, "handlers.JointIssuesResponse": { "type": "object", "properties": { "issues": { "type": "array", "items": { "$ref": "#/definitions/services.JointIssue" } } } }, "handlers.LegacyNumistaSearchResponseSwagger": { "type": "object", "properties": { "count": { "type": "integer" }, "types": { "type": "array", "items": { "$ref": "#/definitions/models.LegacyNumistaType" } } } }, "handlers.LogEntry": { "type": "object", "properties": { "level": { "type": "string", "example": "info" }, "message": { "type": "string", "example": "Application starting" }, "timestamp": { "type": "string", "example": "2025-01-01T12:00:00Z" } } }, "handlers.LogsResponse": { "type": "object", "properties": { "count": { "type": "integer", "example": 100 }, "logLevel": { "type": "string", "example": "info" }, "logs": { "type": "array", "items": { "$ref": "#/definitions/handlers.LogEntry" } } } }, "handlers.LookupComparableListingSwagger": { "type": "object", "properties": { "currency": { "type": "string", "example": "USD" }, "price": { "type": "number", "example": 180 }, "sourceName": { "type": "string", "example": "Example Ancient Coins" }, "title": { "type": "string", "example": "Trajan Denarius VF" }, "url": { "type": "string", "example": "https://www.vcoins.com/en/stores/example/item" } } }, "handlers.LookupExtractedDataSwagger": { "type": "object", "properties": { "coinFields": { "type": "object", "additionalProperties": {} }, "confidence": { "type": "string", "example": "medium" }, "labelText": { "type": "string", "example": "NGC Ch AU 5/5 4/5" }, "ngc": { "$ref": "#/definitions/handlers.NGCDataSwagger" }, "rawAnalysis": { "type": "string", "example": "Vision analysis text..." } } }, "handlers.LookupPriceComparablesSwagger": { "type": "object", "properties": { "count": { "type": "integer", "example": 2 }, "currency": { "type": "string", "example": "USD" }, "high": { "type": "number", "example": 420 }, "listings": { "type": "array", "items": { "$ref": "#/definitions/handlers.LookupComparableListingSwagger" } }, "low": { "type": "number", "example": 180 }, "query": { "type": "string", "example": "Trajan Denarius Roman" } } }, "handlers.LookupPriceEstimateSwagger": { "type": "object", "properties": { "basis": { "type": "string", "example": "Common type in VF" }, "currency": { "type": "string", "example": "USD" }, "high": { "type": "number", "example": 250 }, "low": { "type": "number", "example": 120 } } }, "handlers.MatchCategoryEraRequest": { "type": "object", "required": [ "type", "value" ], "properties": { "type": { "type": "string", "enum": [ "category", "era" ] }, "value": { "type": "string", "maxLength": 200 } } }, "handlers.MatchCategoryEraResponse": { "type": "object", "properties": { "match": { "type": "string" }, "matched": { "type": "boolean" } } }, "handlers.MaterialCount": { "type": "object", "properties": { "count": { "type": "integer", "example": 8 }, "material": { "type": "string", "example": "Silver" } } }, "handlers.MessageResponse": { "type": "object", "properties": { "message": { "type": "string", "example": "Operation successful" } } }, "handlers.MigrationResultDTO": { "type": "object", "properties": { "failed": { "type": "integer", "example": 3 }, "skipped": { "type": "integer", "example": 45 }, "succeeded": { "type": "integer", "example": 12 } } }, "handlers.MonetarySystemCount": { "type": "object", "properties": { "count": { "type": "integer", "example": 137 }, "monetarySystem": { "type": "string", "example": "Escudo (1911-2001)" } } }, "handlers.NGCDataSwagger": { "type": "object", "properties": { "certNumber": { "type": "string", "example": "823160-093" }, "description": { "type": "string", "example": "Roman Empire, Trajan Decius" }, "grade": { "type": "string", "example": "Ch AU" }, "lookupURL": { "type": "string", "example": "https://www.ngccoin.com/certlookup/823160-093/" }, "normalizedCert": { "type": "string", "example": "823160-093" } } }, "handlers.NumistaCandidateSwagger": { "type": "object", "properties": { "id": { "type": "string", "example": "12345" }, "issuer": { "type": "string", "example": "Roman Empire" }, "thumbnail": { "type": "string", "example": "https://en.numista.com/..." }, "title": { "type": "string", "example": "Denarius - Trajan (98-117)" }, "url": { "type": "string", "example": "https://en.numista.com/catalogue/pieces12345.html" }, "year": { "type": "string", "example": "101-102" } } }, "handlers.NumistaDetailRequest": { "type": "object", "required": [ "id" ], "properties": { "id": { "type": "integer" } } }, "handlers.NumistaEnrichmentRequestSwagger": { "type": "object", "required": [ "evidence", "path", "query", "querySource" ], "properties": { "candidates": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaCandidate" } }, "evidence": { "$ref": "#/definitions/models.NumistaEvidence" }, "generationVersion": { "type": "string", "enum": [ "numista-query-v2" ] }, "path": { "$ref": "#/definitions/models.NumistaLookupPath" }, "query": { "type": "string" }, "querySource": { "enum": [ "generated", "user-edited", "manual" ], "allOf": [ { "$ref": "#/definitions/models.NumistaQuerySource" } ] } } }, "handlers.NumistaLookupOutcomeSwagger": { "type": "object", "required": [ "candidates", "effectiveQuery", "querySource", "searchAttempt", "searchAttemptCount", "stage", "status" ], "properties": { "cache": { "$ref": "#/definitions/models.NumistaCacheMetadata" }, "candidates": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaCandidate" } }, "effectiveQuery": { "type": "string" }, "guidanceCode": { "type": "string" }, "querySource": { "$ref": "#/definitions/models.NumistaQuerySource" }, "retryAfterSeconds": { "type": "integer" }, "searchAttempt": { "$ref": "#/definitions/models.NumistaSearchAttempt" }, "searchAttemptCount": { "type": "integer" }, "stage": { "type": "string" }, "status": { "$ref": "#/definitions/models.NumistaLookupStatus" } } }, "handlers.NumistaLookupRequestSwagger": { "type": "object", "required": [ "evidence", "path", "query", "querySource" ], "properties": { "evidence": { "$ref": "#/definitions/models.NumistaEvidence" }, "generationVersion": { "type": "string", "enum": [ "numista-query-v2" ] }, "path": { "$ref": "#/definitions/models.NumistaLookupPath" }, "query": { "type": "string" }, "querySource": { "enum": [ "generated", "user-edited", "manual" ], "allOf": [ { "$ref": "#/definitions/models.NumistaQuerySource" } ] } } }, "handlers.NumistaQueryProposalRequestSwagger": { "type": "object", "required": [ "evidence", "path" ], "properties": { "evidence": { "$ref": "#/definitions/models.NumistaEvidence" }, "path": { "$ref": "#/definitions/models.NumistaLookupPath" } } }, "handlers.NumistaQueryProposalSwagger": { "type": "object", "required": [ "generationVersion", "query", "querySource" ], "properties": { "generationVersion": { "type": "string", "enum": [ "numista-query-v2" ] }, "query": { "type": "string" }, "querySource": { "enum": [ "generated" ], "allOf": [ { "$ref": "#/definitions/models.NumistaQuerySource" } ] } } }, "handlers.NumistaSearchRequest": { "type": "object", "required": [ "query" ], "properties": { "limit": { "type": "integer" }, "query": { "type": "string" } } }, "handlers.OfficialPhotosRequest": { "type": "object", "required": [ "sides" ], "properties": { "sides": { "type": "array", "maxItems": 2, "minItems": 1, "items": { "type": "string" }, "example": [ "obverse", "reverse" ] } } }, "handlers.OfficialPhotosResponse": { "type": "object", "properties": { "images": { "type": "array", "items": { "$ref": "#/definitions/models.CoinImage" } } } }, "handlers.OllamaStatusResponse": { "type": "object", "properties": { "available": { "type": "boolean", "example": true }, "message": { "type": "string", "example": "Model llava is available" }, "model": { "type": "string", "example": "llava" }, "url": { "type": "string", "example": "http://localhost:11434" } } }, "handlers.PasswordChangedResponse": { "type": "object", "properties": { "message": { "type": "string", "example": "Password changed" } } }, "handlers.ProposeUpdateRequest": { "type": "object", "required": [ "changes", "coin_id" ], "properties": { "changes": { "type": "object", "additionalProperties": {} }, "coin_id": { "type": "integer" } } }, "handlers.PurchaseRequest": { "type": "object", "properties": { "purchaseDate": { "type": "string" }, "purchaseLocation": { "type": "string" }, "purchasePrice": { "type": "number" } } }, "handlers.ResetPasswordRequest": { "type": "object", "required": [ "newPassword" ], "properties": { "newPassword": { "type": "string", "minLength": 6, "example": "newpass456" } } }, "handlers.SearchMyCollectionRequest": { "type": "object", "required": [ "query" ], "properties": { "limit": { "type": "integer" }, "query": { "type": "string" } } }, "handlers.SetChecklistResponse": { "type": "object", "properties": { "complete": { "type": "boolean" }, "slots": { "type": "array", "items": { "$ref": "#/definitions/services.ChecklistSlot" } } } }, "handlers.SettingInput": { "type": "object", "properties": { "key": { "type": "string", "example": "OllamaURL" }, "value": { "type": "string", "example": "http://localhost:11434" } } }, "handlers.SettingsUpdateResponse": { "type": "object", "properties": { "message": { "type": "string", "example": "Settings updated" } } }, "handlers.StatsResponse": { "type": "object", "properties": { "byCategory": { "type": "array", "items": { "$ref": "#/definitions/handlers.CategoryCount" } }, "byMaterial": { "type": "array", "items": { "$ref": "#/definitions/handlers.MaterialCount" } }, "byMonetarySystem": { "description": "ByMonetarySystem counts the coins of each monetary system, most first.", "type": "array", "items": { "$ref": "#/definitions/handlers.MonetarySystemCount" } }, "totalCoins": { "type": "integer", "example": 25 }, "totalWishlist": { "type": "integer", "example": 5 }, "values": { "$ref": "#/definitions/handlers.ValueSummary" } } }, "handlers.TopCoinsByValueRequest": { "type": "object", "properties": { "limit": { "type": "integer" } } }, "handlers.TranslationResult": { "type": "object", "properties": { "status": { "type": "string", "enum": [ "ok", "unavailable" ] }, "text": { "type": "string" } } }, "handlers.UpdateUserRoleRequest": { "type": "object", "required": [ "role" ], "properties": { "role": { "allOf": [ { "$ref": "#/definitions/models.UserRole" } ], "example": "admin" } } }, "handlers.UserDTO": { "type": "object", "properties": { "createdAt": { "type": "string", "example": "2025-01-01T00:00:00Z" }, "id": { "type": "integer", "example": 1 }, "role": { "allOf": [ { "$ref": "#/definitions/models.UserRole" } ], "example": "admin" }, "username": { "type": "string", "example": "admin" } } }, "handlers.UserInfoResponse": { "type": "object", "properties": { "createdAt": { "type": "string", "example": "2025-01-01T00:00:00Z" }, "id": { "type": "integer", "example": 1 }, "role": { "type": "string", "example": "admin" }, "username": { "type": "string", "example": "admin" } } }, "handlers.ValueSummary": { "type": "object", "properties": { "avgCurrentValue": { "type": "number", "example": 100 }, "avgPurchasePrice": { "type": "number", "example": 50 }, "totalCurrentValue": { "type": "number", "example": 2500 }, "totalPurchasePrice": { "type": "number", "example": 1250 } } }, "handlers.WishlistMatch": { "type": "object", "properties": { "id": { "type": "integer" }, "name": { "type": "string" } } }, "handlers.WishlistMatchesResponse": { "type": "object", "properties": { "coins": { "type": "array", "items": { "$ref": "#/definitions/handlers.WishlistMatch" } } } }, "handlers.WishlistSearchAlertCandidateListResponse": { "type": "object", "properties": { "candidates": { "type": "array", "items": { "$ref": "#/definitions/models.AlertCandidate" } }, "limit": { "type": "integer", "example": 20 }, "page": { "type": "integer", "example": 1 }, "total": { "type": "integer", "example": 1 } } }, "handlers.WishlistSearchAlertConvertRequest": { "type": "object", "properties": { "acknowledgeDuplicateWarning": { "type": "boolean", "example": false }, "coin": { "$ref": "#/definitions/models.Coin" } } }, "handlers.WishlistSearchAlertConvertResponse": { "type": "object", "properties": { "candidate": { "$ref": "#/definitions/models.AlertCandidate" }, "coin": { "$ref": "#/definitions/models.Coin" }, "warnings": { "type": "array", "items": { "type": "string" } } } }, "handlers.WishlistSearchAlertCriteriaAdjustRequest": { "type": "object", "properties": { "candidateIds": { "type": "array", "items": { "type": "integer" } }, "criteria": { "$ref": "#/definitions/handlers.WishlistSearchAlertCriteriaRequest" } } }, "handlers.WishlistSearchAlertCriteriaRequest": { "type": "object", "properties": { "coinType": { "type": "string", "example": "Denarius" }, "currency": { "type": "string", "example": "USD" }, "dateFrom": { "type": "integer", "example": 81 }, "dateTo": { "type": "integer", "example": 96 }, "dealerPreference": { "type": "string", "example": "VCoins or MA-Shops" }, "gradeOrCondition": { "type": "string", "example": "VF or better" }, "keywords": { "type": "string", "example": "RIC Minerva" }, "material": { "type": "string", "example": "Silver" }, "mint": { "type": "string", "example": "Rome" }, "mintLocationId": { "type": "integer", "example": 42 }, "notes": { "type": "string", "example": "Prefer clear legends" }, "priceMax": { "type": "number", "example": 300 }, "priceMin": { "type": "number", "example": 0 }, "rulerOrIssuer": { "type": "string", "example": "Domitian" }, "sourceFilters": { "type": "array", "items": { "type": "string" }, "example": [ "vcoins.com", "ma-shops.com" ] } } }, "handlers.WishlistSearchAlertDismissRequest": { "type": "object", "properties": { "notes": { "type": "string", "example": "Too expensive with shipping" }, "reason": { "type": "string", "example": "price_too_high" } } }, "handlers.WishlistSearchAlertListResponse": { "type": "object", "properties": { "alerts": { "type": "array", "items": { "$ref": "#/definitions/models.WishlistSearchAlert" } }, "limit": { "type": "integer", "example": 20 }, "page": { "type": "integer", "example": 1 }, "total": { "type": "integer", "example": 1 } } }, "handlers.WishlistSearchAlertRequest": { "type": "object", "properties": { "cadence": { "type": "string", "example": "manual" }, "criteria": { "$ref": "#/definitions/handlers.WishlistSearchAlertCriteriaRequest" }, "isActive": { "type": "boolean", "example": true }, "name": { "type": "string", "example": "Domitian denarius under $300" } } }, "handlers.WishlistSearchAlertRunListResponse": { "type": "object", "properties": { "limit": { "type": "integer", "example": 20 }, "page": { "type": "integer", "example": 1 }, "runs": { "type": "array", "items": { "$ref": "#/definitions/models.AlertRun" } }, "total": { "type": "integer", "example": 1 } } }, "handlers.WishlistSearchAlertRunRequest": { "type": "object", "properties": { "maxCandidates": { "type": "integer", "example": 20 } } }, "handlers.WishlistSearchAlertRunResponse": { "type": "object", "properties": { "alertId": { "type": "integer", "example": 1 }, "candidates": { "type": "array", "items": { "$ref": "#/definitions/models.AlertCandidate" } }, "completedAt": { "type": "string", "example": "2026-06-29T17:00:10Z" }, "dismissedCount": { "type": "integer", "example": 0 }, "duplicateCount": { "type": "integer", "example": 3 }, "newCount": { "type": "integer", "example": 5 }, "partialWarnings": { "type": "array", "items": { "type": "string" } }, "rateLimitStatus": { "type": "string", "example": "ok" }, "resultCount": { "type": "integer", "example": 8 }, "runId": { "type": "integer", "example": 42 }, "startedAt": { "type": "string", "example": "2026-06-29T17:00:00Z" }, "status": { "allOf": [ { "$ref": "#/definitions/models.AlertRunStatus" } ], "example": "completed" } } }, "handlers.catalogCreateRequest": { "type": "object", "required": [ "catalog", "displayName", "era" ], "properties": { "catalog": { "type": "string" }, "displayName": { "type": "string" }, "era": { "$ref": "#/definitions/models.Era" }, "volumeRequired": { "type": "boolean" } } }, "handlers.catalogListResponse": { "type": "object", "properties": { "catalogs": { "type": "array", "items": { "$ref": "#/definitions/models.CatalogRegistry" } } } }, "handlers.catalogUpdateRequest": { "type": "object", "required": [ "catalog", "displayName", "era" ], "properties": { "catalog": { "type": "string" }, "displayName": { "type": "string" }, "era": { "$ref": "#/definitions/models.Era" }, "volumeRequired": { "type": "boolean" } } }, "handlers.copilotResumeRequest": { "type": "object", "properties": { "answer": { "type": "string" }, "expectedCheckpointVersion": { "type": "integer" } } }, "handlers.copilotRunDTO": { "type": "object", "properties": { "attempt": { "type": "integer" }, "checkpointVersion": { "type": "integer" }, "createdAt": { "type": "string" }, "failureCode": { "type": "string" }, "failureMessage": { "type": "string" }, "finalAnswer": { "type": "string" }, "goal": { "type": "string" }, "id": { "type": "string" }, "lastSeq": { "type": "integer" }, "resumeDeadline": { "type": "string" }, "status": { "type": "string" }, "threadId": { "type": "string" }, "updatedAt": { "type": "string" }, "usage": { "$ref": "#/definitions/handlers.copilotUsageDTO" } } }, "handlers.copilotRunEnvelope": { "type": "object", "properties": { "reused": { "type": "boolean" }, "run": { "$ref": "#/definitions/handlers.copilotRunDTO" } } }, "handlers.copilotStartRequest": { "type": "object", "properties": { "appContext": { "type": "object", "additionalProperties": {} }, "goal": { "type": "string" }, "threadId": { "type": "string" } } }, "handlers.copilotThreadDTO": { "type": "object", "properties": { "createdAt": { "type": "string" }, "id": { "type": "string" }, "messages": { "type": "array", "items": { "$ref": "#/definitions/handlers.copilotThreadMessageDTO" } }, "runs": { "type": "array", "items": { "$ref": "#/definitions/handlers.copilotRunDTO" } }, "title": { "type": "string" }, "updatedAt": { "type": "string" } } }, "handlers.copilotThreadMessageDTO": { "type": "object", "properties": { "content": { "type": "string" }, "createdAt": { "type": "string" }, "role": { "type": "string" }, "runId": { "type": "string" } } }, "handlers.copilotUsageDTO": { "type": "object", "properties": { "inputTokens": { "type": "integer" }, "iterations": { "type": "integer" }, "outputTokens": { "type": "integer" }, "toolCalls": { "type": "integer" } } }, "handlers.createSetBuilderRunRequest": { "type": "object", "properties": { "prompt": { "type": "string" } } }, "handlers.deepApplyRequest": { "type": "object", "required": [ "target" ], "properties": { "fields": { "type": "array", "items": { "type": "string" } }, "target": { "type": "string", "enum": [ "draft", "coin", "wishlist" ] } } }, "handlers.deepApplyResponse": { "type": "object", "properties": { "appliedAt": { "type": "string" }, "appliedFields": { "type": "array", "items": { "type": "string" } }, "coinId": { "type": "integer" }, "draftId": { "type": "integer" }, "jobId": { "type": "integer" } } }, "handlers.deepCapabilityResponse": { "type": "object", "properties": { "enabled": { "type": "boolean" }, "providers": { "type": "array", "items": { "type": "string" } } } }, "handlers.deepJobDTO": { "type": "object", "properties": { "appliedAt": { "type": "string" }, "appliedCoinExists": { "type": "boolean" }, "appliedCoinId": { "type": "integer" }, "appliedDraftId": { "type": "integer" }, "cancelRequested": { "type": "boolean" }, "coinId": { "type": "integer" }, "completedAt": { "type": "string" }, "createdAt": { "type": "string" }, "eventsAvailable": { "type": "boolean" }, "expiresAt": { "type": "string" }, "failureCode": { "type": "string" }, "failureMessage": { "type": "string" }, "id": { "type": "integer" }, "lastSeq": { "type": "integer" }, "partialSuccess": { "type": "boolean" }, "requestedProviders": { "type": "array", "items": { "type": "string" } }, "retryOfJobId": { "type": "integer" }, "routerRationale": { "type": "string" }, "selectedProviders": { "type": "array", "items": { "type": "string" } }, "source": { "type": "string" }, "startedAt": { "type": "string" }, "status": { "type": "string" } } }, "handlers.deepJobEnvelope": { "type": "object", "properties": { "job": { "$ref": "#/definitions/handlers.deepJobDTO" }, "proposal": { "type": "object" }, "report": { "type": "object" }, "reused": { "type": "boolean" } } }, "handlers.deepJobListResponse": { "type": "object", "properties": { "jobs": { "type": "array", "items": { "$ref": "#/definitions/handlers.deepJobDTO" } }, "nextCursor": { "type": "string" } } }, "handlers.deepProposalPatchRequest": { "type": "object", "properties": { "fields": { "type": "object", "additionalProperties": { "type": "object", "properties": { "accepted": { "type": "boolean" }, "ownerValue": { "type": "object" } } } } } }, "handlers.deepRetryRequest": { "type": "object", "properties": { "notes": { "type": "string" }, "providers": { "type": "array", "items": { "type": "string" } } } }, "handlers.geocodeMintResponse": { "type": "object", "properties": { "candidates": { "type": "array", "items": { "$ref": "#/definitions/services.GeocodeCandidate" } } } }, "handlers.importImageRequest": { "type": "object", "required": [ "imageType", "url" ], "properties": { "credit": { "description": "Credit is appended to the coin's notes after a successful import.", "type": "string", "maxLength": 500, "example": "Obverse photo: Jane Doe, CC BY-SA 4.0 (Wikimedia Commons)" }, "imageType": { "type": "string", "example": "obverse" }, "isPrimary": { "type": "boolean", "example": true }, "url": { "type": "string", "maxLength": 2000, "example": "https://upload.wikimedia.org/wikipedia/commons/5/54/Coin.jpg" } } }, "handlers.loginRequest": { "type": "object", "required": [ "password", "username" ], "properties": { "password": { "type": "string", "example": "password123" }, "username": { "type": "string", "example": "admin" } } }, "handlers.mintLocationListResponse": { "type": "object", "properties": { "mintLocations": { "type": "array", "items": { "$ref": "#/definitions/models.MintLocation" } } } }, "handlers.mintLocationRequest": { "type": "object", "required": [ "displayName", "lat", "lng" ], "properties": { "aliases": { "type": "array", "items": { "type": "string" } }, "displayName": { "type": "string" }, "lat": { "type": "number" }, "lng": { "type": "number" }, "region": { "type": "string" } } }, "handlers.promoteDraftOverridesRequest": { "type": "object", "properties": { "category": { "type": "string" }, "era": { "type": "string" }, "material": { "type": "string" }, "name": { "type": "string" }, "notes": { "type": "string" }, "purchaseLocation": { "type": "string" }, "purchasePrice": { "type": "number" } } }, "handlers.promoteDraftRequest": { "type": "object", "properties": { "confirm": { "type": "boolean" }, "overrides": { "$ref": "#/definitions/handlers.promoteDraftOverridesRequest" }, "target": { "type": "string" } } }, "handlers.quickCaptureDraftListResponse": { "type": "object", "properties": { "drafts": { "type": "array", "items": { "$ref": "#/definitions/models.QuickCaptureDraft" } }, "limit": { "type": "integer" }, "page": { "type": "integer" }, "total": { "type": "integer" } } }, "handlers.refreshRequest": { "type": "object", "required": [ "refreshToken" ], "properties": { "refreshToken": { "type": "string" } } }, "handlers.regenerateSetProposalRequest": { "type": "object", "properties": { "feedback": { "type": "string" } } }, "handlers.registerRequest": { "type": "object", "required": [ "email", "password", "username" ], "properties": { "email": { "type": "string", "example": "user@example.com" }, "password": { "type": "string", "minLength": 6, "example": "password123" }, "username": { "type": "string", "minLength": 3, "example": "admin" } } }, "handlers.rejectSetProposalRequest": { "type": "object", "properties": { "reason": { "type": "string" } } }, "handlers.updateSetProposalRequest": { "type": "object", "properties": { "color": { "type": "string" }, "description": { "type": "string" }, "proposedName": { "type": "string" }, "selectedScope": { "type": "string" }, "slots": { "type": "array", "items": { "$ref": "#/definitions/handlers.updateSetProposalSlotRequest" } } } }, "handlers.updateSetProposalSlotRequest": { "type": "object", "properties": { "criteria": { "type": "object", "additionalProperties": true }, "group": { "type": "string" }, "label": { "type": "string" }, "sortOrder": { "type": "integer" }, "sourceNote": { "type": "string" }, "validationNote": { "type": "string" }, "verificationStatus": { "type": "string" } } }, "handlers.wishlistURLRequest": { "type": "object", "properties": { "url": { "type": "string" } } }, "models.AIJob": { "type": "object", "properties": { "attempts": { "type": "integer" }, "coinId": { "type": "integer" }, "completedAt": { "type": "string" }, "createdAt": { "type": "string" }, "errorMessage": { "type": "string" }, "id": { "type": "integer" }, "jobType": { "$ref": "#/definitions/models.AIJobType" }, "result": { "type": "string" }, "side": { "type": "string" }, "startedAt": { "type": "string" }, "status": { "$ref": "#/definitions/models.AIJobStatus" }, "updatedAt": { "type": "string" }, "userId": { "type": "integer" } } }, "models.AIJobStatus": { "type": "string", "enum": [ "queued", "running", "completed", "failed" ], "x-enum-varnames": [ "AIJobStatusQueued", "AIJobStatusRunning", "AIJobStatusCompleted", "AIJobStatusFailed" ] }, "models.AIJobType": { "type": "string", "enum": [ "analysis", "value_estimate", "coin_grading" ], "x-enum-varnames": [ "AIJobTypeAnalysis", "AIJobTypeValueEstimate", "AIJobTypeCoinGrading" ] }, "models.AgentConversation": { "type": "object", "properties": { "createdAt": { "type": "string" }, "id": { "type": "integer" }, "messages": { "type": "string" }, "title": { "type": "string" }, "updatedAt": { "type": "string" }, "userId": { "type": "integer" } } }, "models.AlertCandidate": { "type": "object", "properties": { "alertId": { "type": "integer" }, "canonicalSourceUrl": { "type": "string" }, "convertedCoinId": { "type": "integer" }, "createdAt": { "type": "string" }, "dismissalReason": { "type": "string" }, "duplicateKey": { "type": "string" }, "duplicateOfCandidateId": { "type": "integer" }, "fields": { "$ref": "#/definitions/models.StringMap" }, "firstSeenAt": { "type": "string" }, "id": { "type": "integer" }, "lastSeenAt": { "type": "string" }, "lifecycleState": { "$ref": "#/definitions/models.AlertCandidateState" }, "matchingWishlistCoinId": { "type": "integer" }, "normalizedTitle": { "type": "string" }, "observedCurrency": { "type": "string" }, "observedPrice": { "type": "number" }, "provenance": { "type": "array", "items": { "$ref": "#/definitions/models.CandidateProvenance" } }, "provenanceStatus": { "$ref": "#/definitions/models.CandidateProvenanceStatus" }, "reasonForMatch": { "type": "string" }, "runId": { "type": "integer" }, "sourceName": { "type": "string" }, "sourceUrl": { "type": "string" }, "title": { "type": "string" }, "updatedAt": { "type": "string" }, "userId": { "type": "integer" } } }, "models.AlertCandidateState": { "type": "string", "enum": [ "active", "dismissed", "converted", "suppressed", "needs_review" ], "x-enum-varnames": [ "AlertCandidateStateActive", "AlertCandidateStateDismissed", "AlertCandidateStateConverted", "AlertCandidateStateSuppressed", "AlertCandidateStateNeedsReview" ] }, "models.AlertRun": { "type": "object", "properties": { "alertId": { "type": "integer" }, "candidates": { "type": "array", "items": { "$ref": "#/definitions/models.AlertCandidate" } }, "completedAt": { "type": "string" }, "createdAt": { "type": "string" }, "criteriaSnapshot": { "type": "string" }, "dismissedCount": { "type": "integer" }, "duplicateCount": { "type": "integer" }, "durationMs": { "type": "integer" }, "errorMessage": { "type": "string" }, "id": { "type": "integer" }, "newCount": { "type": "integer" }, "partialWarnings": { "type": "array", "items": { "type": "string" } }, "rateLimitStatus": { "type": "string" }, "resultCount": { "type": "integer" }, "startedAt": { "type": "string" }, "status": { "$ref": "#/definitions/models.AlertRunStatus" }, "triggerType": { "$ref": "#/definitions/models.AlertRunTriggerType" }, "userId": { "type": "integer" } } }, "models.AlertRunStatus": { "type": "string", "enum": [ "queued", "running", "completed", "failed", "partial", "rate_limited", "cancelled" ], "x-enum-varnames": [ "AlertRunStatusQueued", "AlertRunStatusRunning", "AlertRunStatusCompleted", "AlertRunStatusFailed", "AlertRunStatusPartial", "AlertRunStatusRateLimited", "AlertRunStatusCancelled" ] }, "models.AlertRunTriggerType": { "type": "string", "enum": [ "manual", "scheduled" ], "x-enum-varnames": [ "AlertRunTriggerManual", "AlertRunTriggerScheduled" ] }, "models.CandidateProvenance": { "type": "object", "properties": { "candidateId": { "type": "integer" }, "confidence": { "type": "string" }, "field": { "type": "string" }, "id": { "type": "integer" }, "notes": { "type": "string" }, "observedAt": { "type": "string" }, "sourceUrl": { "type": "string" }, "value": { "type": "string" }, "verificationState": { "$ref": "#/definitions/models.CandidateProvenanceStatus" } } }, "models.CandidateProvenanceStatus": { "type": "string", "enum": [ "verified", "partial", "unverified" ], "x-enum-varnames": [ "CandidateProvenanceVerified", "CandidateProvenancePartial", "CandidateProvenanceUnverified" ] }, "models.CatalogRegistry": { "type": "object", "required": [ "catalog", "displayName", "era" ], "properties": { "catalog": { "type": "string", "maxLength": 32 }, "createdAt": { "type": "string" }, "displayName": { "type": "string", "maxLength": 128 }, "era": { "maxLength": 64, "allOf": [ { "$ref": "#/definitions/models.Era" } ] }, "id": { "type": "integer" }, "updatedAt": { "type": "string" }, "volumeRequired": { "type": "boolean" } } }, "models.Category": { "type": "string", "enum": [ "Modern", "Other" ], "x-enum-varnames": [ "CategoryModern", "CategoryOther" ] }, "models.Coin": { "type": "object", "properties": { "aiAnalysis": { "type": "string" }, "category": { "maxLength": 64, "allOf": [ { "$ref": "#/definitions/models.Category" } ] }, "certificationNumber": { "type": "string", "maxLength": 60 }, "certifiedGrade": { "type": "string", "maxLength": 60 }, "circulation": { "description": "Circulation, when set by hand (see CoinCirculations), says whether the\ncoin is still legal tender and whether it is commemorative, instead of\nwhat Numista says of its type.", "type": "string", "enum": [ "", "current", "current_commemorative", "demonetized", "demonetized_commemorative", "non_circulating", "non_circulating_commemorative" ] }, "country": { "description": "Country is the issuing country or authority (e.g. from the Numista issuer).", "type": "string", "maxLength": 120 }, "createdAt": { "type": "string" }, "currentValue": { "type": "number" }, "currentValueUpdatedAt": { "type": "string" }, "dateRange": { "type": "string", "maxLength": 200 }, "denomination": { "type": "string", "maxLength": 200 }, "diameterMm": { "type": "number" }, "era": { "maxLength": 64, "allOf": [ { "$ref": "#/definitions/models.Era" } ] }, "grade": { "type": "string", "maxLength": 100 }, "heightMm": { "type": "number" }, "id": { "type": "integer" }, "images": { "type": "array", "items": { "$ref": "#/definitions/models.CoinImage" } }, "isWishlist": { "type": "boolean" }, "kind": { "description": "Kind is what the item is: a coin or a banknote (see CoinKind…).", "type": "string", "enum": [ "coin", "banknote" ] }, "listingCheckReason": { "type": "string" }, "listingCheckedAt": { "type": "string" }, "listingStatus": { "type": "string" }, "material": { "$ref": "#/definitions/models.Material" }, "mint": { "type": "string", "maxLength": 200 }, "mintLocation": { "$ref": "#/definitions/models.MintLocation" }, "mintLocationId": { "type": "integer" }, "monetarySystem": { "description": "MonetarySystem is the currency system the coin belongs to, as Numista\nnames it (\"Escudo (1911-2001)\", \"Real (decimalizado, 1835-1910)\"); every\neuro coin, whatever its country, is \"Euro\".", "type": "string", "maxLength": 120 }, "name": { "type": "string", "maxLength": 200 }, "noteVariant": { "type": "string", "enum": [ "", "replacement", "specimen" ] }, "notes": { "type": "string", "maxLength": 5000 }, "numistaIssueId": { "type": "integer" }, "obverseAnalysis": { "type": "string" }, "obverseDescription": { "type": "string", "maxLength": 2000 }, "obverseInscription": { "type": "string", "maxLength": 1000 }, "period": { "description": "Period is the coin's period, as uCoin names a country's periods\n(\"República Portuguesa 1986–2001\", \"Rainha Isabel II\"), worked out by\nthe app (see services.CoinPeriodService), never typed.", "type": "string" }, "presentation": { "description": "Presentation is how the coin is kept or was issued (see CoinPresentations).", "type": "string" }, "printer": { "type": "string", "maxLength": 200 }, "purchaseDate": { "type": "string" }, "purchaseLocation": { "type": "string", "maxLength": 500 }, "purchasePrice": { "type": "number" }, "quantity": { "description": "Quantity is how many of this coin or note the collector has (1 or more).", "type": "integer", "maximum": 9999, "minimum": 1 }, "rarity": { "description": "Rarity is a fixed scale (see CoinRarities). RarityRating is a legacy free-text field.", "type": "string" }, "rarityRating": { "type": "string", "maxLength": 100 }, "referenceText": { "type": "string", "maxLength": 2000 }, "referenceUrl": { "type": "string", "maxLength": 2000 }, "references": { "type": "array", "items": { "$ref": "#/definitions/models.CoinReference" } }, "reverseAnalysis": { "type": "string" }, "reverseDescription": { "type": "string", "maxLength": 2000 }, "reverseInscription": { "type": "string", "maxLength": 1000 }, "ruler": { "type": "string", "maxLength": 200 }, "serialNumber": { "type": "string", "maxLength": 100 }, "series": { "description": "Banknote details (see Kind): the series or issue (\"Chapa 13\"), the\nnote's own serial number, its signatories, printer, size, watermark,\nwhether it is a replacement or specimen note, and a grading company's\ngrade (\"PMG 66 EPQ\") with its certificate. NumistaIssueID is the issue of\nthe linked Numista type the item is (a banknote's printed date and\nsignatures), 0 when none was chosen; its market value is that issue's.", "type": "string", "maxLength": 200 }, "sets": { "type": "array", "items": { "$ref": "#/definitions/models.CoinSet" } }, "signatures": { "type": "string", "maxLength": 300 }, "sourceAlertCandidateId": { "type": "integer" }, "updatedAt": { "type": "string" }, "userId": { "type": "integer" }, "vendorInvoice": { "type": "string", "maxLength": 200 }, "vendorSku": { "type": "string", "maxLength": 200 }, "watermark": { "type": "string", "maxLength": 500 }, "weightGrams": { "type": "number" }, "widthMm": { "type": "number" }, "year": { "description": "Year is derived from DateRange (first year, negative for BC) for sorting and grouping.", "type": "integer" } } }, "models.CoinCountryFillResult": { "type": "object", "properties": { "failed": { "description": "Failed coins could not be resolved (e.g. Numista did not answer).", "type": "integer" }, "remaining": { "description": "Remaining linked coins still have no country (call again to continue).", "type": "integer" }, "status": { "type": "string" }, "updated": { "description": "Updated coins got a country in this call.", "type": "integer" } } }, "models.CoinImage": { "type": "object", "properties": { "coinId": { "type": "integer" }, "createdAt": { "type": "string" }, "filePath": { "type": "string" }, "id": { "type": "integer" }, "imageType": { "$ref": "#/definitions/models.ImageType" }, "isPrimary": { "type": "boolean" } } }, "models.CoinJournal": { "type": "object", "properties": { "coinId": { "type": "integer" }, "createdAt": { "type": "string" }, "entry": { "type": "string" }, "id": { "type": "integer" }, "userId": { "type": "integer" } } }, "models.CoinMarketValue": { "type": "object", "properties": { "catalogPhotos": { "description": "CatalogPhotos are Numista's reference photos of the type, with credit.", "type": "array", "items": { "$ref": "#/definitions/models.NumistaCatalogPhoto" } }, "currency": { "type": "string" }, "fetchedAt": { "description": "FetchedAt is when Numista last returned these prices.", "type": "string" }, "issues": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaIssueSummary" } }, "numistaTypeId": { "type": "integer" }, "numistaUrl": { "type": "string" }, "prices": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaGradePrice" } }, "selectedIssueId": { "type": "integer" }, "stale": { "description": "Stale is true when a refresh failed and stored values are being shown.", "type": "boolean" }, "status": { "type": "string" } } }, "models.CoinNumistaFillChange": { "type": "object", "properties": { "coinId": { "type": "integer" }, "composition": { "type": "string" }, "diameterMm": { "type": "number" }, "isWishlist": { "type": "boolean" }, "material": { "description": "Material is set from Composition, Numista's words for the metal.", "allOf": [ { "$ref": "#/definitions/models.Material" } ] }, "monetarySystem": { "description": "MonetarySystem is set from the type's currency (see MonetarySystemFromNumista).", "type": "string" }, "name": { "type": "string" }, "presentation": { "type": "string" }, "previousMaterial": { "description": "PreviousMaterial is the material a correction replaces.", "allOf": [ { "$ref": "#/definitions/models.Material" } ] }, "weightGrams": { "type": "number" } } }, "models.CoinNumistaFillResult": { "type": "object", "properties": { "applied": { "description": "Applied is true once the changes were saved.", "type": "boolean" }, "byMaterial": { "description": "ByMaterial counts the materials set, by material.", "type": "object", "additionalProperties": { "type": "integer" } }, "changes": { "description": "Changes lists every coin the fill changes, by name.", "type": "array", "items": { "$ref": "#/definitions/models.CoinNumistaFillChange" } }, "coins": { "description": "Coins is how many coins get at least one field.", "type": "integer" }, "corrected": { "description": "Corrected is how many corrections were saved.", "type": "integer" }, "corrections": { "description": "Corrections lists the coins whose material is set but is not what\nNumista's composition says (e.g. Silver for silver plated copper).\nThey change only when asked for.", "type": "array", "items": { "$ref": "#/definitions/models.CoinNumistaFillChange" } }, "diameters": { "type": "integer" }, "materials": { "type": "integer" }, "monetarySystems": { "description": "MonetarySystems is how many coins and banknotes get a monetary system.", "type": "integer" }, "notCached": { "description": "NotCached coins are linked to a Numista type whose details the app\nhas not cached yet; they are left as they are.", "type": "integer" }, "presentations": { "type": "integer" }, "unmatched": { "description": "Unmatched coins still have no material: Numista's composition fits\nnone of the app's (e.g. plain steel or iron).", "type": "integer" }, "weights": { "type": "integer" } } }, "models.CoinPhotoCandidate": { "type": "object", "properties": { "author": { "type": "string" }, "imageUrl": { "description": "ImageURL is the full-size photo to import into a coin.", "type": "string" }, "license": { "type": "string" }, "match": { "description": "Match is \"exact\" when the source identified this very coin, so the\nphoto can be added without asking.", "type": "string", "enum": [ "exact" ] }, "pageUrl": { "type": "string" }, "side": { "description": "Side is \"obverse\" or \"reverse\" when the source states it.", "type": "string" }, "source": { "type": "string", "enum": [ "numista", "wikimedia", "ecb", "incm", "mdp", "moe" ] }, "thumbnail": { "type": "string" }, "title": { "type": "string" } } }, "models.CoinPhotoSearchResult": { "type": "object", "properties": { "photos": { "type": "array", "items": { "$ref": "#/definitions/models.CoinPhotoCandidate" } }, "query": { "type": "string" }, "status": { "type": "string" } } }, "models.CoinReference": { "type": "object", "required": [ "catalog", "number" ], "properties": { "catalog": { "type": "string", "maxLength": 32 }, "coinId": { "type": "integer" }, "createdAt": { "type": "string" }, "id": { "type": "integer" }, "number": { "type": "string", "maxLength": 128 }, "updatedAt": { "type": "string" }, "uri": { "type": "string", "maxLength": 2000 }, "volume": { "type": "string", "maxLength": 64 } } }, "models.CoinSet": { "type": "object", "properties": { "agenticPrompt": { "type": "string" }, "agenticStatus": { "type": "string" }, "checklist": { "description": "Checklist defines a checklist set's slots (see services.ChecklistDefinition).", "allOf": [ { "$ref": "#/definitions/models.JSONObject" } ] }, "color": { "type": "string" }, "createdAt": { "type": "string" }, "creationMode": { "$ref": "#/definitions/models.CoinSetCreationMode" }, "description": { "type": "string" }, "icon": { "type": "string" }, "id": { "type": "integer" }, "name": { "type": "string" }, "parentSetId": { "type": "integer" }, "setType": { "$ref": "#/definitions/models.CoinSetType" }, "shareToken": { "type": "string" }, "smartCriteria": { "$ref": "#/definitions/models.JSONObject" }, "targetCompletionDate": { "type": "string" }, "updatedAt": { "type": "string" }, "userId": { "type": "integer" } } }, "models.CoinSetCreationMode": { "type": "string", "enum": [ "manual", "dynamic" ], "x-enum-varnames": [ "CoinSetCreationModeManual", "CoinSetCreationModeDynamic" ] }, "models.CoinSetType": { "type": "string", "enum": [ "standard", "smart", "goal", "agentic", "checklist", "standard", "goal", "agentic" ], "x-enum-varnames": [ "CoinSetTypeStandard", "CoinSetTypeSmart", "CoinSetTypeGoal", "CoinSetTypeAgentic", "CoinSetTypeChecklist", "CoinSetTypeOpen", "CoinSetTypeDefined", "CoinSetTypeTracker" ] }, "models.CoinValueHistory": { "type": "object", "properties": { "coinId": { "type": "integer" }, "confidence": { "type": "string" }, "id": { "type": "integer" }, "recordedAt": { "type": "string" }, "source": { "description": "Source identifies the origin of this value entry: manual, ai_scheduled, ai_estimate or numista.\nLegacy rows with no source default to 'manual' via backfill in database.go.", "type": "string" }, "userId": { "type": "integer" }, "value": { "type": "number" } } }, "models.DeepIdentificationHintDeletionMetrics": { "type": "object", "properties": { "failure": { "type": "integer" }, "success": { "type": "integer" } } }, "models.DeepIdentificationJanitorMetrics": { "type": "object", "properties": { "failures": { "type": "integer" }, "recoverySweeps": { "type": "integer" }, "retentionSweeps": { "type": "integer" } } }, "models.DeepIdentificationLatencySummary": { "type": "object", "properties": { "p50Ms": { "type": "integer" }, "p95Ms": { "type": "integer" } } }, "models.DeepIdentificationObservabilitySummary": { "type": "object", "properties": { "activeSseStreams": { "type": "integer" }, "duration": { "$ref": "#/definitions/models.DeepIdentificationLatencySummary" }, "hintDeletion": { "$ref": "#/definitions/models.DeepIdentificationHintDeletionMetrics" }, "janitor": { "$ref": "#/definitions/models.DeepIdentificationJanitorMetrics" }, "jobsByTerminalStatus": { "type": "object", "additionalProperties": { "type": "integer", "format": "int64" } }, "partialSuccessRate": { "type": "number" }, "providers": { "type": "object", "additionalProperties": { "$ref": "#/definitions/models.DeepIdentificationProviderMetrics" } }, "queueDepth": { "type": "integer" }, "reconnectCount": { "type": "integer" }, "truncationCount": { "type": "integer" } } }, "models.DeepIdentificationProviderMetrics": { "type": "object", "properties": { "latency": { "$ref": "#/definitions/models.DeepIdentificationLatencySummary" }, "statusCounts": { "type": "object", "additionalProperties": { "type": "integer", "format": "int64" } } } }, "models.Era": { "type": "string", "enum": [ "ancient", "medieval", "modern" ], "x-enum-varnames": [ "EraAncient", "EraMedieval", "EraModern" ] }, "models.ImageType": { "type": "string", "enum": [ "obverse", "reverse", "detail", "other" ], "x-enum-varnames": [ "ImageTypeObverse", "ImageTypeReverse", "ImageTypeDetail", "ImageTypeOther" ] }, "models.JSONObject": { "type": "object", "additionalProperties": true }, "models.LegacyNumistaIssuer": { "type": "object", "properties": { "name": { "type": "string" } } }, "models.LegacyNumistaType": { "type": "object", "properties": { "id": { "type": "integer" }, "issuer": { "$ref": "#/definitions/models.LegacyNumistaIssuer" }, "max_year": { "type": "integer" }, "min_year": { "type": "integer" }, "obverse_thumbnail": { "type": "string" }, "reverse_thumbnail": { "type": "string" }, "title": { "type": "string" } } }, "models.Material": { "type": "string", "enum": [ "Gold", "Silver", "Bronze", "Copper", "Electrum", "Other", "Billon", "Copper-nickel", "Nickel brass", "Nordic gold", "Aluminium bronze", "Brass", "Nickel", "Aluminium", "Zinc", "Stainless steel", "Plated steel", "Bimetallic" ], "x-enum-varnames": [ "MaterialGold", "MaterialSilver", "MaterialBronze", "MaterialCopper", "MaterialElectrum", "MaterialOther", "MaterialBillon", "MaterialCopperNickel", "MaterialNickelBrass", "MaterialNordicGold", "MaterialAluminiumBronze", "MaterialBrass", "MaterialNickel", "MaterialAluminium", "MaterialZinc", "MaterialStainlessSteel", "MaterialPlatedSteel", "MaterialBimetallic" ] }, "models.MintLocation": { "type": "object", "properties": { "aliases": { "type": "array", "items": { "type": "string" } }, "createdAt": { "type": "string" }, "displayName": { "type": "string" }, "id": { "type": "integer" }, "lat": { "type": "number" }, "lng": { "type": "number" }, "region": { "type": "string" }, "updatedAt": { "type": "string" }, "userId": { "type": "integer" } } }, "models.NumistaCacheMetadata": { "type": "object", "properties": { "ageSeconds": { "type": "integer" }, "coalesced": { "type": "boolean" }, "createdAt": { "type": "string" }, "expiresAt": { "type": "string" }, "hit": { "type": "boolean" } } }, "models.NumistaCandidate": { "type": "object", "properties": { "assessment": { "$ref": "#/definitions/models.NumistaRelevanceAssessment" }, "canonicalUrl": { "type": "string" }, "denomination": { "type": "string" }, "enrichmentState": { "$ref": "#/definitions/models.NumistaEnrichmentState" }, "id": { "type": "integer" }, "issuer": { "type": "string" }, "material": { "type": "string" }, "maxYear": { "type": "integer" }, "minYear": { "type": "integer" }, "mint": { "type": "string" }, "obverseInscription": { "type": "string" }, "obverseThumbnail": { "type": "string" }, "providerPosition": { "type": "integer" }, "reverseInscription": { "type": "string" }, "reverseThumbnail": { "type": "string" }, "title": { "type": "string" }, "yearDisplay": { "type": "string" } } }, "models.NumistaCatalogIssuer": { "type": "object", "properties": { "code": { "type": "string" }, "name": { "type": "string" }, "parent": { "type": "string" } } }, "models.NumistaCatalogIssuersResult": { "type": "object", "properties": { "issuers": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaCatalogIssuer" } }, "status": { "type": "string" } } }, "models.NumistaCatalogIssuesResult": { "type": "object", "properties": { "issues": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaIssueSummary" } }, "stale": { "description": "Stale is true when Numista could not be reached and stored issues are shown.", "type": "boolean" }, "status": { "type": "string" } } }, "models.NumistaCatalogPhoto": { "type": "object", "properties": { "copyright": { "type": "string" }, "license": { "type": "string" }, "picture": { "type": "string" }, "side": { "type": "string" }, "thumbnail": { "type": "string" } } }, "models.NumistaCatalogReference": { "type": "object", "properties": { "catalogue": { "type": "string" }, "number": { "type": "string" } } }, "models.NumistaCatalogSearchItem": { "type": "object", "properties": { "id": { "type": "integer" }, "issuer": { "$ref": "#/definitions/models.NumistaCatalogIssuer" }, "maxYear": { "type": "integer" }, "minYear": { "type": "integer" }, "obverseThumbnail": { "type": "string" }, "reverseThumbnail": { "type": "string" }, "title": { "type": "string" } } }, "models.NumistaCatalogSearchResult": { "type": "object", "properties": { "count": { "type": "integer" }, "page": { "type": "integer" }, "status": { "type": "string" }, "types": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaCatalogSearchItem" } } } }, "models.NumistaCatalogSide": { "type": "object", "properties": { "description": { "type": "string" }, "designers": { "type": "array", "items": { "type": "string" } }, "engravers": { "type": "array", "items": { "type": "string" } }, "lettering": { "type": "string" } } }, "models.NumistaCatalogType": { "type": "object", "properties": { "category": { "description": "Category is \"coin\", \"banknote\" or \"exonumia\".", "type": "string" }, "classification": { "description": "Classification is Numista's kind of coin (e.g. \"Moedas circulantes\ncomuns\"), in the language asked for.", "type": "string" }, "commemoratedTopic": { "description": "CommemoratedTopic is what a commemorative coin celebrates, in the\nlanguage asked for when Numista has it (e.g. \"O conhecimento\").", "type": "string" }, "composition": { "type": "string" }, "currency": { "type": "string" }, "demonetizationDate": { "type": "string" }, "demonetized": { "description": "Demonetized tells whether the type is no longer legal tender (nil\nwhen Numista does not say); DemonetizationDate is when, if known.", "type": "boolean" }, "denomination": { "type": "string" }, "diameterMm": { "description": "DiameterMm is a coin's diameter, or a banknote's width.", "type": "number" }, "edge": { "$ref": "#/definitions/models.NumistaCatalogSide" }, "faceValue": { "description": "FaceValue is the denomination as a number (5 for \"5 Euros\").", "type": "number" }, "heightMm": { "description": "HeightMm is a banknote's height.", "type": "number" }, "id": { "type": "integer" }, "issuedInCoincard": { "description": "IssuedInCoincard is true when Numista's comments say the type's coins\nare issued in a coin card (not just a special version of it).", "type": "boolean" }, "issuer": { "$ref": "#/definitions/models.NumistaCatalogIssuer" }, "issuingEntity": { "type": "string" }, "maxYear": { "type": "integer" }, "minYear": { "type": "integer" }, "mints": { "type": "array", "items": { "type": "string" } }, "monetarySystem": { "description": "MonetarySystem is the type's currency system, as a coin stores it\n(see MonetarySystemFromNumista): \"Escudo (1911-2001)\", \"Euro\".", "type": "string" }, "obverse": { "$ref": "#/definitions/models.NumistaCatalogSide" }, "photos": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaCatalogPhoto" } }, "printers": { "description": "Printers, Watermark and IssuingEntity (the issuing bank) describe banknotes.", "type": "array", "items": { "type": "string" } }, "references": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaCatalogReference" } }, "reverse": { "$ref": "#/definitions/models.NumistaCatalogSide" }, "rulers": { "type": "array", "items": { "type": "string" } }, "series": { "type": "string" }, "shape": { "type": "string" }, "technique": { "type": "string" }, "thicknessMm": { "type": "number" }, "title": { "type": "string" }, "url": { "type": "string" }, "watermark": { "type": "string" }, "weightGrams": { "type": "number" } } }, "models.NumistaCatalogTypeResult": { "type": "object", "properties": { "issues": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaIssueSummary" } }, "status": { "type": "string" }, "type": { "$ref": "#/definitions/models.NumistaCatalogType" } } }, "models.NumistaEnrichmentState": { "type": "string", "enum": [ "not_requested", "enriched", "cached", "failed" ], "x-enum-varnames": [ "NumistaEnrichmentNotRequested", "NumistaEnrichmentEnriched", "NumistaEnrichmentCached", "NumistaEnrichmentFailed" ] }, "models.NumistaEvidence": { "type": "object", "properties": { "dateText": { "type": "string" }, "denomination": { "type": "string" }, "exactNumistaId": { "type": "integer" }, "issuer": { "type": "string" }, "material": { "type": "string" }, "mint": { "type": "string" }, "obverseInscription": { "type": "string" }, "reverseInscription": { "type": "string" }, "reverseType": { "type": "string" }, "title": { "type": "string" }, "visibleText": { "type": "string" } } }, "models.NumistaGradePrice": { "type": "object", "properties": { "grade": { "type": "string" }, "price": { "type": "number" } } }, "models.NumistaHealthSummary": { "type": "object", "properties": { "broadRequestCount": { "type": "integer" }, "cancelledRequestCount": { "type": "integer" }, "coalescedRequestCount": { "type": "integer" }, "configurationValid": { "type": "boolean" }, "configured": { "type": "boolean" }, "detailRequestCount": { "type": "integer" }, "enrichmentAttempted": { "type": "integer" }, "enrichmentFailed": { "type": "integer" }, "enrichmentSucceeded": { "type": "integer" }, "freshCacheHitCount": { "type": "integer" }, "freshCacheHitRate": { "type": "number" }, "generatedQueryCount": { "type": "integer" }, "lastCheckedAt": { "type": "string" }, "lastOutcome": { "$ref": "#/definitions/models.NumistaLookupStatus" }, "lastQuotaLimitedAt": { "type": "string" }, "lastRetryAfterSeconds": { "type": "integer" }, "manualQueryCount": { "type": "integer" }, "p50ElapsedMs": { "description": "R-7 linearly interpolated percentile, rounded to milliseconds.", "type": "integer" }, "p95ElapsedMs": { "description": "R-7 linearly interpolated percentile, rounded to milliseconds.", "type": "integer" }, "providerFailureCount": { "type": "integer" }, "providerLoadCount": { "type": "integer" }, "relaxedAttemptCount": { "type": "integer" }, "statusCounts": { "description": "Sparse rolling counts; absent statuses have zero events.", "type": "object", "additionalProperties": { "type": "integer" } }, "usage": { "description": "Usage is this month's request count against the quota.", "allOf": [ { "$ref": "#/definitions/models.NumistaUsageSummary" } ] }, "userEditedQueryCount": { "type": "integer" } } }, "models.NumistaIssueSignature": { "type": "object", "properties": { "name": { "type": "string", "example": "José da Silva Lopes" }, "title": { "type": "string", "example": "Governor" } } }, "models.NumistaIssueSummary": { "type": "object", "properties": { "comment": { "type": "string" }, "day": { "type": "integer", "example": 13 }, "id": { "type": "integer" }, "maxYear": { "type": "integer" }, "minYear": { "type": "integer" }, "mintLetter": { "type": "string" }, "mintage": { "type": "integer" }, "month": { "description": "A banknote's issues are told apart by the date printed on the note\n(Month and Day with Year), its signatories, the issue's own Pick number\n(\"176a\") and whether its notes are replacement or specimen notes.", "type": "integer", "example": 9 }, "pick": { "type": "string", "example": "176a" }, "signatures": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaIssueSignature" } }, "variant": { "type": "string", "enum": [ "replacement", "specimen" ] }, "year": { "description": "Year is the Gregorian year for dated issues (negative for BC).", "type": "integer" } } }, "models.NumistaLookupOutcome": { "type": "object", "required": [ "candidates", "effectiveQuery", "querySource", "searchAttempt", "searchAttemptCount", "stage", "status" ], "properties": { "cache": { "$ref": "#/definitions/models.NumistaCacheMetadata" }, "candidates": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaCandidate" } }, "effectiveQuery": { "type": "string" }, "guidanceCode": { "type": "string" }, "querySource": { "$ref": "#/definitions/models.NumistaQuerySource" }, "retryAfterSeconds": { "type": "integer" }, "searchAttempt": { "$ref": "#/definitions/models.NumistaSearchAttempt" }, "searchAttemptCount": { "type": "integer" }, "stage": { "type": "string" }, "status": { "$ref": "#/definitions/models.NumistaLookupStatus" } } }, "models.NumistaLookupPath": { "type": "string", "enum": [ "direct", "photo" ], "x-enum-varnames": [ "NumistaLookupPathDirect", "NumistaLookupPathPhoto" ] }, "models.NumistaLookupStatus": { "type": "string", "enum": [ "success", "empty", "unconfigured", "quota-limited", "timeout", "unavailable" ], "x-enum-varnames": [ "NumistaStatusSuccess", "NumistaStatusEmpty", "NumistaStatusUnconfigured", "NumistaStatusQuotaLimited", "NumistaStatusTimeout", "NumistaStatusUnavailable" ] }, "models.NumistaQuerySource": { "type": "string", "enum": [ "generated", "user-edited", "manual" ], "x-enum-varnames": [ "NumistaQuerySourceGenerated", "NumistaQuerySourceUserEdited", "NumistaQuerySourceManual" ] }, "models.NumistaReasonKind": { "type": "string", "enum": [ "match", "conflict", "unavailable" ], "x-enum-varnames": [ "NumistaReasonMatch", "NumistaReasonConflict", "NumistaReasonUnavailable" ] }, "models.NumistaRelevanceAssessment": { "type": "object", "properties": { "band": { "type": "string" }, "reasons": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaRelevanceReason" } }, "score": { "type": "integer" }, "scoringVersion": { "type": "string" } } }, "models.NumistaRelevanceReason": { "type": "object", "properties": { "code": { "type": "string" }, "field": { "type": "string" }, "kind": { "$ref": "#/definitions/models.NumistaReasonKind" }, "label": { "type": "string" } } }, "models.NumistaSearchAttempt": { "type": "string", "enum": [ "primary", "relaxed" ], "x-enum-varnames": [ "NumistaSearchAttemptPrimary", "NumistaSearchAttemptRelaxed" ] }, "models.NumistaUsageSummary": { "type": "object", "properties": { "background": { "type": "integer", "example": 250 }, "backgroundPaused": { "type": "boolean" }, "issues": { "type": "integer", "example": 60 }, "limit": { "type": "integer", "example": 2000 }, "month": { "type": "string", "example": "2026-10" }, "other": { "type": "integer", "example": 1 }, "pauseAt": { "type": "integer", "example": 1800 }, "pausedUntil": { "description": "PausedUntil is set while Numista refuses requests for its quota: until\nthen none is sent.", "type": "string" }, "prices": { "type": "integer", "example": 300 }, "requests": { "type": "integer", "example": 1234 }, "searches": { "description": "The requests by what they asked for: searches, types, a type's issues,\nan issue's prices, the rest (the list of issuers); and how many were\nmade in the background.", "type": "integer", "example": 120 }, "types": { "type": "integer", "example": 80 } } }, "models.NumistaValueRun": { "type": "object", "properties": { "budget": { "description": "Budget is how many Numista requests the run could make; Requests, how\nmany it made (prices Numista gave in the last days cost none).", "type": "integer" }, "checked": { "description": "Checked coins had their price looked up: Updated got a new value,\nUnchanged kept theirs, NoPrice have none on Numista, Failed could not\nbe looked up and are tried again next time.", "type": "integer" }, "completedAt": { "type": "string" }, "failed": { "type": "integer" }, "id": { "type": "integer" }, "noPrice": { "type": "integer" }, "requests": { "type": "integer" }, "startedAt": { "type": "string" }, "status": { "type": "string", "enum": [ "running", "completed", "budget_spent", "paused", "unconfigured", "failed" ] }, "triggerType": { "type": "string", "enum": [ "scheduled", "manual" ] }, "unchanged": { "type": "integer" }, "updated": { "type": "integer" } } }, "models.NumistaValueStatus": { "type": "object", "properties": { "currency": { "type": "string", "example": "EUR" }, "dailyBudget": { "description": "DailyBudget is how many requests the next batch may make.", "type": "integer" }, "due": { "type": "integer" }, "eligible": { "description": "Eligible coins are linked to Numista and have a grade Numista prices;\nDue ones have not been checked in the last 30 days.", "type": "integer" }, "enabled": { "type": "boolean" }, "isRunning": { "type": "boolean" }, "nextRunIn": { "description": "NextRunIn is the time until the next scheduled batch (when enabled).", "type": "string", "example": "5h0m0s" }, "quotaLevel": { "description": "QuotaLevel is ok, low (from 75% of the quota) or paused (from PauseAt).", "type": "string", "enum": [ "ok", "low", "paused" ] }, "resumesOn": { "description": "ResumesOn is the first day of next month (UTC), when the quota renews.", "type": "string", "example": "2026-11-01" }, "runs": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaValueRun" } }, "usage": { "$ref": "#/definitions/models.NumistaUsageSummary" }, "withoutGrade": { "description": "WithoutGrade coins are linked to Numista but have no grade Numista\nprices (none, or a proof); they wait for one.", "type": "integer" } } }, "models.ProposalSlot": { "type": "object", "properties": { "createdAt": { "type": "string" }, "criteria": { "$ref": "#/definitions/models.JSONObject" }, "group": { "type": "string" }, "id": { "type": "integer" }, "label": { "type": "string" }, "proposalId": { "type": "integer" }, "sortOrder": { "type": "integer" }, "sourceNote": { "type": "string" }, "updatedAt": { "type": "string" }, "validationNote": { "type": "string" }, "verificationStatus": { "$ref": "#/definitions/models.ProposalSlotVerificationStatus" } } }, "models.ProposalSlotVerificationStatus": { "type": "string", "enum": [ "verified", "unverified" ], "x-enum-varnames": [ "ProposalSlotVerificationVerified", "ProposalSlotVerificationUnverified" ] }, "models.QuickCaptureDraft": { "type": "object", "properties": { "acquisitionSource": { "type": "string", "maxLength": 500 }, "aiConfidence": { "type": "string", "maxLength": 20 }, "createdAt": { "type": "string" }, "dateRange": { "type": "string", "maxLength": 200 }, "discardedAt": { "type": "string" }, "era": { "type": "string", "maxLength": 64 }, "id": { "type": "integer" }, "images": { "type": "array", "items": { "$ref": "#/definitions/models.QuickCaptureDraftImage" } }, "labelText": { "type": "string", "maxLength": 5000 }, "ngcCertNumber": { "type": "string", "maxLength": 32 }, "ngcGrade": { "type": "string", "maxLength": 100 }, "ngcLookupUrl": { "type": "string", "maxLength": 500 }, "notes": { "type": "string", "maxLength": 5000 }, "promotedAt": { "type": "string" }, "promotedCoinId": { "type": "integer" }, "purchasePrice": { "type": "number" }, "selectedNumistaReference": { "allOf": [ { "$ref": "#/definitions/models.QuickCaptureDraftReference" } ], "x-nullable": true }, "source": { "type": "string", "maxLength": 40 }, "status": { "$ref": "#/definitions/models.QuickCaptureDraftStatus" }, "updatedAt": { "type": "string" }, "userId": { "type": "integer" }, "workingTitle": { "type": "string", "maxLength": 200 } } }, "models.QuickCaptureDraftImage": { "type": "object", "properties": { "createdAt": { "type": "string" }, "displayOrder": { "type": "integer" }, "draftId": { "type": "integer" }, "filePath": { "type": "string" }, "id": { "type": "integer" }, "imageType": { "$ref": "#/definitions/models.ImageType" }, "isPrimary": { "type": "boolean" } } }, "models.QuickCaptureDraftReference": { "type": "object", "properties": { "catalog": { "type": "string" }, "number": { "type": "string" }, "uri": { "type": "string" } } }, "models.QuickCaptureDraftStatus": { "type": "string", "enum": [ "active", "promoting", "promoted", "discarded" ], "x-enum-varnames": [ "QuickCaptureDraftStatusActive", "QuickCaptureDraftStatusPromoting", "QuickCaptureDraftStatusPromoted", "QuickCaptureDraftStatusDiscarded" ] }, "models.RecommendationStatus": { "type": "string", "enum": [ "pending", "accepted", "rejected", "dismissed" ], "x-enum-varnames": [ "RecommendationStatusPending", "RecommendationStatusAccepted", "RecommendationStatusRejected", "RecommendationStatusDismissed" ] }, "models.RecommendationTargetType": { "type": "string", "enum": [ "set" ], "x-enum-varnames": [ "RecommendationTargetTypeSet" ] }, "models.SetBuilderRun": { "type": "object", "properties": { "completedAt": { "type": "string" }, "createdAt": { "type": "string" }, "errorMessage": { "type": "string" }, "feedback": { "type": "string" }, "id": { "type": "integer" }, "maxTurns": { "type": "integer" }, "model": { "type": "string" }, "prompt": { "type": "string" }, "provider": { "type": "string" }, "startedAt": { "type": "string" }, "status": { "$ref": "#/definitions/models.SetBuilderRunStatus" }, "terminationReason": { "type": "string" }, "tokenBudget": { "type": "integer" }, "tokensUsed": { "type": "integer" }, "transcriptSummary": { "type": "string" }, "updatedAt": { "type": "string" }, "usedTurns": { "type": "integer" }, "userId": { "type": "integer" } } }, "models.SetBuilderRunStatus": { "type": "string", "enum": [ "queued", "running", "completed", "failed" ], "x-enum-varnames": [ "SetBuilderRunStatusQueued", "SetBuilderRunStatusRunning", "SetBuilderRunStatusCompleted", "SetBuilderRunStatusFailed" ] }, "models.SetProposal": { "type": "object", "properties": { "approvalSetId": { "type": "integer" }, "approvedAt": { "type": "string" }, "builderRunId": { "type": "integer" }, "color": { "type": "string" }, "createdAt": { "type": "string" }, "description": { "type": "string" }, "errorMessage": { "type": "string" }, "expiresAt": { "type": "string" }, "id": { "type": "integer" }, "idempotencyKey": { "type": "string" }, "originalPrompt": { "type": "string" }, "preMatchSummary": { "$ref": "#/definitions/models.JSONObject" }, "proposedName": { "type": "string" }, "proposedSlug": { "type": "string" }, "rejectedAt": { "type": "string" }, "rejectionReason": { "type": "string" }, "rosterPayload": { "$ref": "#/definitions/models.JSONObject" }, "run": { "$ref": "#/definitions/models.SetBuilderRun" }, "scopeOptions": { "$ref": "#/definitions/models.JSONObject" }, "selectedScope": { "type": "string" }, "slots": { "type": "array", "items": { "$ref": "#/definitions/models.ProposalSlot" } }, "status": { "$ref": "#/definitions/models.SetProposalStatus" }, "updatedAt": { "type": "string" }, "userId": { "type": "integer" } } }, "models.SetProposalStatus": { "type": "string", "enum": [ "pending", "approved", "rejected", "expired", "creation_failed" ], "x-enum-varnames": [ "SetProposalStatusPending", "SetProposalStatusApproved", "SetProposalStatusRejected", "SetProposalStatusExpired", "SetProposalStatusCreationFailed" ] }, "models.StringMap": { "type": "object", "additionalProperties": { "type": "string" } }, "models.UserRole": { "type": "string", "enum": [ "admin", "user" ], "x-enum-varnames": [ "RoleAdmin", "RoleUser" ] }, "models.WishlistAlertCadence": { "type": "string", "enum": [ "manual", "daily", "weekly", "monthly" ], "x-enum-varnames": [ "WishlistAlertCadenceManual", "WishlistAlertCadenceDaily", "WishlistAlertCadenceWeekly", "WishlistAlertCadenceMonthly" ] }, "models.WishlistSearchAlert": { "type": "object", "properties": { "cadence": { "$ref": "#/definitions/models.WishlistAlertCadence" }, "coinType": { "type": "string" }, "createdAt": { "type": "string" }, "currency": { "type": "string" }, "dateFrom": { "type": "integer" }, "dateTo": { "type": "integer" }, "dealerPreference": { "type": "string" }, "gradeOrCondition": { "type": "string" }, "id": { "type": "integer" }, "isActive": { "type": "boolean" }, "keywords": { "type": "string" }, "lastRunAt": { "type": "string" }, "material": { "type": "string" }, "mint": { "type": "string" }, "mintLocationId": { "type": "integer" }, "name": { "type": "string" }, "notes": { "type": "string" }, "priceMax": { "type": "number" }, "priceMin": { "type": "number" }, "rulerOrIssuer": { "type": "string" }, "sourceFilters": { "type": "array", "items": { "type": "string" } }, "updatedAt": { "type": "string" }, "userId": { "type": "integer" } } }, "repository.CoinHighlight": { "type": "object", "properties": { "country": { "type": "string" }, "currentValue": { "type": "number" }, "dateRange": { "type": "string" }, "grade": { "type": "string" }, "id": { "type": "integer" }, "imagePath": { "description": "ImagePath is the primary (or obverse, or first) image, if any.", "type": "string" }, "name": { "type": "string" }, "rarity": { "type": "string" }, "year": { "type": "integer" } } }, "repository.CollectionHighlights": { "type": "object", "properties": { "mostValuable": { "$ref": "#/definitions/repository.CoinHighlight" }, "oldest": { "$ref": "#/definitions/repository.CoinHighlight" }, "rarest": { "$ref": "#/definitions/repository.CoinHighlight" } } }, "repository.CountryCount": { "type": "object", "properties": { "count": { "type": "integer" }, "country": { "type": "string" } } }, "repository.DenominationCount": { "type": "object", "properties": { "count": { "type": "integer" }, "denomination": { "type": "string" } } }, "repository.DistributionCell": { "type": "object", "properties": { "category": { "type": "string" }, "count": { "type": "integer" }, "era": { "type": "string" } } }, "repository.PeriodCount": { "type": "object", "properties": { "count": { "type": "integer" }, "fromYear": { "type": "integer" }, "period": { "type": "string", "example": "República Portuguesa 1986–2001" }, "toYear": { "type": "integer" } } }, "services.AIJobSubmissionResponse": { "type": "object", "properties": { "job": { "$ref": "#/definitions/models.AIJob" } } }, "services.AdminHealthSummary": { "type": "object", "properties": { "eligibleCoinCount": { "type": "integer" }, "lowScorePercentage": { "type": "number" }, "lowScoreThreshold": { "type": "integer" }, "medianScore": { "type": "integer" }, "topMissingFields": { "type": "array", "items": { "$ref": "#/definitions/services.MissingFieldStat" } } } }, "services.BackupInfo": { "type": "object", "properties": { "appVersion": { "type": "string" }, "coins": { "type": "integer" }, "createdAt": { "description": "CreatedAt is when its data was copied (for an imported backup, when it\nwas made, not imported).", "type": "string" }, "files": { "type": "integer" }, "kind": { "type": "string", "enum": [ "auto", "manual", "pre-restore", "pre-update", "uploaded" ] }, "name": { "type": "string", "example": "orla-numis-2026-10-16-030000-auto.zip" }, "size": { "type": "integer" }, "users": { "description": "Users, Coins (coins and banknotes) and Files (photos and other uploads) it holds.", "type": "integer" } } }, "services.BreakdownEntry": { "type": "object", "properties": { "count": { "type": "integer" }, "label": { "type": "string" }, "value": { "type": "number" } } }, "services.CancelCollectionProposalResult": { "type": "object", "properties": { "message": { "type": "string" }, "proposalId": { "type": "string" }, "status": { "type": "string" } } }, "services.ChecklistDimension": { "type": "string", "enum": [ "metadata", "images", "valuation", "ai" ], "x-enum-varnames": [ "ChecklistDimensionMetadata", "ChecklistDimensionImages", "ChecklistDimensionValuation", "ChecklistDimensionAI" ] }, "services.ChecklistSeverity": { "type": "string", "enum": [ "high", "medium", "low" ], "x-enum-varnames": [ "ChecklistSeverityHigh", "ChecklistSeverityMedium", "ChecklistSeverityLow" ] }, "services.ChecklistSlot": { "type": "object", "properties": { "coinId": { "description": "The collection's coin filling the slot.", "type": "integer" }, "coinImage": { "type": "string" }, "coinName": { "type": "string" }, "coinValue": { "description": "CoinValue is the current value of the coin filling the slot.", "type": "number" }, "country": { "type": "string" }, "currency": { "type": "string" }, "faceValue": { "type": "number" }, "faceValues": { "type": "array", "items": { "type": "number" } }, "imageUrl": { "description": "ImageURL and PageURL show the coin as its source does.", "type": "string" }, "key": { "type": "string" }, "owned": { "type": "boolean" }, "pageUrl": { "type": "string" }, "title": { "description": "Title names the coin; a challenge's country slots leave it empty (the\ncountry says it).", "type": "string" }, "toYear": { "description": "ToYear is a type's last year, when it was struck over several.", "type": "integer" }, "year": { "type": "integer" } } }, "services.CoinCopilotCapability": { "type": "object", "properties": { "enabled": { "type": "boolean" }, "mode": { "type": "string" }, "modelToolCallingSupported": { "type": "boolean" }, "reason": { "type": "string" } } }, "services.CoinHealthItem": { "type": "object", "properties": { "coinId": { "type": "integer" }, "dimensions": { "$ref": "#/definitions/services.HealthDimensionScores" }, "grade": { "$ref": "#/definitions/services.HealthGrade" }, "missingItems": { "type": "array", "items": { "$ref": "#/definitions/services.MissingChecklistItem" } }, "quickActions": { "type": "array", "items": { "$ref": "#/definitions/services.HealthActionHint" } }, "score": { "type": "integer" }, "title": { "type": "string" } } }, "services.CoinHealthListResponse": { "type": "object", "properties": { "coins": { "type": "array", "items": { "$ref": "#/definitions/services.CoinHealthItem" } }, "pagination": { "$ref": "#/definitions/services.HealthPagination" } } }, "services.CoinRecommendationItem": { "type": "object", "properties": { "confidence": { "type": "string" }, "id": { "type": "integer" }, "reasons": { "type": "array", "items": { "type": "string" } }, "score": { "type": "number" }, "status": { "$ref": "#/definitions/models.RecommendationStatus" }, "targetId": { "type": "integer" }, "targetName": { "type": "string" }, "targetType": { "$ref": "#/definitions/models.RecommendationTargetType" } } }, "services.CoinRelease": { "type": "object", "properties": { "country": { "description": "Country is the issuing country's ISO code (\"PT\"); \"\" for a joint issue\nof the euro area countries.", "type": "string" }, "currency": { "description": "Currency is the face value's, when not euros: \"PTE\" for escudos.", "type": "string" }, "faceValue": { "description": "FaceValue is the lowest of the design's face values; FaceValues all of\nthem, from the lowest, when there are several.", "type": "number" }, "faceValues": { "type": "array", "items": { "type": "number" } }, "from": { "description": "From and To are the first and last month of the issue period (\"2025-10\"\nand \"2025-12\" for \"Fourth quarter of 2025\"); the same month when it is\nknown; January to December when only the year is.", "type": "string", "example": "2025-10" }, "id": { "description": "ID identifies a coin of the catalogue (its first key), to hide it or\njoin it to another.", "type": "string" }, "imageUrl": { "type": "string" }, "issueText": { "description": "IssueText is the period as the ECB writes it, in English.", "type": "string" }, "kind": { "type": "string", "enum": [ "commemorative", "collector" ] }, "owned": { "description": "Owned is true when a coin of the collection is this one.", "type": "boolean" }, "pageUrl": { "type": "string" }, "sameAs": { "description": "SameAs is, for a coin the user joined to another, that one's id.", "type": "string" }, "source": { "type": "string", "enum": [ "ecb", "incm", "mdp", "moe", "numista" ] }, "title": { "description": "Title is what the coin shows: the ECB's feature (in English) or the\nmint's name for the design.", "type": "string" }, "to": { "type": "string", "example": "2025-12" }, "upcoming": { "description": "Upcoming is true before the issue period starts, and during a month or\na quarter of issue until it ends.", "type": "boolean" }, "variants": { "description": "Variants are the finishes and metals a mint sells the design in (\"BNC\", \"Prata Proof\").", "type": "array", "items": { "type": "string" } }, "volume": { "description": "Volume is how many are struck, when the source says.", "type": "integer" }, "wishlist": { "description": "Wishlist is true when a coin of the wishlist is this one;\nWishlistCoinID is that coin, to take it off the wishlist.", "type": "boolean" }, "wishlistCoinId": { "type": "integer" } } }, "services.CoinReleaseGroup": { "type": "object", "properties": { "releases": { "type": "array", "items": { "$ref": "#/definitions/services.CoinRelease" } }, "source": { "type": "string", "enum": [ "ecb", "mints" ] }, "year": { "description": "Year is the year of the ECB's list (the latest it has published). The\nmints' releases are those of the last twelve months (of this year for a\nmint that gives only the year) and to come.", "type": "integer" } } }, "services.CoinStatus": { "type": "object", "properties": { "commemoratedTopic": { "type": "string" }, "commemorative": { "description": "Commemorative is null when Numista says nothing of the coin.", "type": "boolean" }, "current": { "description": "Current is null when nobody knows.", "type": "boolean" }, "demonetizationDate": { "description": "DemonetizationDate is when it stopped being legal tender, if Numista says.", "type": "string" }, "manual": { "description": "Manual is true when the user set them on the coin (its circulation).", "type": "boolean" }, "nonCirculating": { "description": "NonCirculating is true for coins issued for collectors rather than for\ncirculation (Numista's \"non-circulating coins\"); null when nobody knows.", "type": "boolean" } } }, "services.CollectionChatContext": { "type": "object", "properties": { "activeCoinId": { "type": "integer" }, "activeDraftId": { "type": "integer" }, "route": { "type": "string" } } }, "services.CollectionCurrencyChange": { "type": "object", "properties": { "coinsConverted": { "description": "CoinsConverted counts coins with a price or value that was converted.", "type": "integer" }, "currency": { "type": "string" }, "factor": { "description": "Factor is what every amount was multiplied by (how much of the new\ncurrency one unit of the previous one buys), at the ECB rates of\nRateDate; both are empty when nothing was converted.", "type": "number" }, "previous": { "type": "string" }, "rateDate": { "type": "string" } } }, "services.CollectionHealthSummary": { "type": "object", "properties": { "dimensions": { "$ref": "#/definitions/services.HealthDimensionScores" }, "eligibleCoinCount": { "type": "integer" }, "grade": { "$ref": "#/definitions/services.HealthGrade" }, "score": { "type": "integer" }, "weights": { "$ref": "#/definitions/services.HealthWeights" } } }, "services.CommitCollectionProposalResult": { "type": "object", "properties": { "changedFields": { "type": "array", "items": { "type": "string" } }, "coinId": { "type": "integer" }, "journalSource": { "type": "string" }, "message": { "type": "string" }, "proposalId": { "type": "string" }, "status": { "type": "string" } } }, "services.ExchangeRate": { "type": "object", "properties": { "date": { "description": "Date is the ECB reference date, YYYY-MM-DD.", "type": "string" }, "usdPerEur": { "description": "USDPerEUR is how many US dollars one euro buys (e.g. 1.1206).", "type": "number" } } }, "services.GeocodeCandidate": { "type": "object", "properties": { "displayName": { "type": "string" }, "lat": { "type": "number" }, "lng": { "type": "number" } } }, "services.HealthActionHint": { "type": "string", "enum": [ "edit_metadata", "upload_images", "run_valuation", "run_ai_analysis" ], "x-enum-varnames": [ "HealthActionEditMetadata", "HealthActionUploadImages", "HealthActionRunValuation", "HealthActionRunAIAnalysis" ] }, "services.HealthDimensionScores": { "type": "object", "properties": { "aiCoverage": { "type": "integer" }, "imageCoverage": { "type": "integer" }, "metadata": { "type": "integer" }, "valuationFreshness": { "type": "integer" } } }, "services.HealthGrade": { "type": "string", "enum": [ "A", "B", "C", "D", "F" ], "x-enum-varnames": [ "HealthGradeA", "HealthGradeB", "HealthGradeC", "HealthGradeD", "HealthGradeF" ] }, "services.HealthPagination": { "type": "object", "properties": { "limit": { "type": "integer" }, "page": { "type": "integer" }, "total": { "type": "integer" } } }, "services.HealthWeights": { "type": "object", "properties": { "aiCoverage": { "type": "integer" }, "imageCoverage": { "type": "integer" }, "metadata": { "type": "integer" }, "valuationFreshness": { "type": "integer" } } }, "services.JointIssue": { "type": "object", "properties": { "coins": { "type": "array", "items": { "$ref": "#/definitions/services.JointIssueCoin" } }, "pageUrl": { "type": "string" }, "title": { "description": "Title is the ECB's feature, in English.", "type": "string", "example": "50th anniversary of the Treaty of Rome" }, "year": { "type": "integer", "example": 2007 } } }, "services.JointIssueCoin": { "type": "object", "properties": { "country": { "description": "Country is an ISO code.", "type": "string", "example": "PT" }, "imageUrl": { "type": "string" } } }, "services.MissingChecklistItem": { "type": "object", "properties": { "actionHint": { "$ref": "#/definitions/services.HealthActionHint" }, "dimension": { "$ref": "#/definitions/services.ChecklistDimension" }, "key": { "type": "string" }, "label": { "type": "string" }, "severity": { "$ref": "#/definitions/services.ChecklistSeverity" } } }, "services.MissingFieldStat": { "type": "object", "properties": { "count": { "type": "integer" }, "key": { "type": "string" }, "percentage": { "type": "number" } } }, "services.ReleaseNumistaType": { "type": "object", "properties": { "candidates": { "type": "array", "items": { "$ref": "#/definitions/models.NumistaCatalogSearchItem" } }, "status": { "type": "string", "enum": [ "ok", "unconfigured", "unauthorized", "quota_limited", "unavailable" ] }, "typeId": { "description": "TypeID is the coin's type; absent when none is sure.", "type": "integer" } } }, "services.RestoreResult": { "type": "object", "properties": { "at": { "type": "string" }, "error": { "type": "string" }, "name": { "type": "string" }, "ok": { "type": "boolean" } } }, "services.ShareListRequest": { "type": "object", "required": [ "format", "list" ], "properties": { "contact": { "description": "Contact, when given, is printed at the top (an e-mail, a phone); it is not stored.", "type": "string", "maxLength": 120 }, "format": { "description": "Format is \"pdf\" or \"csv\".", "type": "string", "enum": [ "pdf", "csv" ] }, "includeValues": { "description": "IncludeValues adds each coin's current value.", "type": "boolean" }, "lang": { "description": "Lang is the language of the list: \"pt\" (European Portuguese) or \"en\".", "type": "string", "enum": [ "pt", "en" ] }, "list": { "type": "string", "enum": [ "trades", "wishlist", "both" ] } } }, "services.TimeMachineBounds": { "type": "object", "properties": { "earliestDate": { "type": "string" }, "hasData": { "type": "boolean" }, "latestDate": { "type": "string" } } }, "services.TimeMachineSnapshot": { "type": "object", "properties": { "acquiredInYear": { "type": "integer" }, "asOfDate": { "type": "string" }, "byCategory": { "type": "array", "items": { "$ref": "#/definitions/services.BreakdownEntry" } }, "byEra": { "type": "array", "items": { "$ref": "#/definitions/services.BreakdownEntry" } }, "byMaterial": { "type": "array", "items": { "$ref": "#/definitions/services.BreakdownEntry" } }, "coinCount": { "type": "integer" }, "topCoins": { "type": "array", "items": { "$ref": "#/definitions/services.TopCoin" } }, "totalInvested": { "type": "number" }, "totalValue": { "type": "number" }, "undatedCoinCount": { "description": "UndatedCoinCount is how many owned coins have no purchase date and so\nappear at no point on the timeline. Surfaced rather than hidden: a\nnon-zero value means the numbers below understate the collection.", "type": "integer" }, "unrealizedGain": { "type": "number" }, "valueBasis": { "$ref": "#/definitions/services.ValueBasis" } } }, "services.TopCoin": { "type": "object", "properties": { "id": { "type": "integer" }, "name": { "type": "string" }, "value": { "type": "number" }, "valueFromHistory": { "type": "boolean" } } }, "services.UpdateStatus": { "type": "object", "properties": { "checkedAt": { "type": "string" }, "current": { "type": "string", "example": "1.0.0" }, "image": { "type": "string", "example": "anagermano/orlanumis" }, "latest": { "type": "string", "example": "1.1.0" }, "notes": { "description": "Notes are the release's notes, in Markdown.", "type": "string" }, "publishedAt": { "type": "string" }, "releaseUrl": { "type": "string" }, "status": { "type": "string", "enum": [ "ok", "disabled", "unavailable" ] }, "updateAvailable": { "description": "UpdateAvailable is true when the latest release is newer than the\nrunning version.", "type": "boolean" }, "watchtower": { "description": "Watchtower is true when Watchtower's address and token are set, so\nthe update can be started from the app.", "type": "boolean" } } }, "services.ValueBasis": { "type": "object", "properties": { "fromPurchasePrice": { "type": "integer" }, "fromValuationHistory": { "type": "integer" } } }, "services.WishlistURLAnalysis": { "type": "object", "properties": { "existingCoinId": { "type": "integer" }, "hypothesis": { "$ref": "#/definitions/services.WishlistURLHypothesis" }, "imageUrl": { "type": "string" }, "outcome": { "type": "string" }, "pageTitle": { "type": "string" }, "schemaVersion": { "type": "integer" }, "sourceUrl": { "type": "string" }, "warnings": { "type": "array", "items": { "type": "string" } } } }, "services.WishlistURLHypothesis": { "type": "object", "properties": { "category": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "coin_type": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "currency": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "dateRange": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "dealerName": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "denomination": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "diameterMm": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "era": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "grade": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "legible": { "type": "boolean" }, "listedPrice": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "listingStatus": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "material": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "mint": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "name": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "notes": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "observations": { "type": "string" }, "obverseDescription": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "obverseInscription": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "rarityRating": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "references": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "reverseDescription": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "reverseInscription": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "ruler": { "$ref": "#/definitions/services.WishlistURLHypothesisField" }, "weightGrams": { "$ref": "#/definitions/services.WishlistURLHypothesisField" } } }, "services.WishlistURLHypothesisField": { "type": "object", "properties": { "confidence": { "type": "number" }, "evidence": { "type": "array", "items": { "type": "string" } }, "value": { "type": "string" } } } }, "securityDefinitions": { "BearerAuth": { "description": "Enter your JWT token with the Bearer prefix, e.g. \"Bearer eyJhbGci...\"", "type": "apiKey", "name": "Authorization", "in": "header" } } }