{"openapi":"3.1.0","info":{"title":"Hamelyn API","version":"6.0.0","description":"Canonical Hamelyn REST API (v6).\n\nAuthentication: `Authorization: Bearer <token>` where the token is\neither an Auth0 access token or a Hamelyn personal access token\n(prefix `hmk_`). Every response carries an `x-request-id` header and\nuses the envelope `{ ok: true, data, meta? }` on success or\n`{ ok: false, error: { code, message, details? } }` on failure.\n\nRate limiting: 300 requests per 60s\nper credential (per client IP on public endpoints), signalled with\n`RateLimit`/`RateLimit-Policy` structured fields plus the legacy\n`RateLimit-Limit`/`RateLimit-Remaining`/`RateLimit-Reset` headers."},"servers":[{"url":"https://api.hamelyn.com","description":"Production"},{"url":"http://localhost:3011","description":"Local development"}],"paths":{"/api/v6/health":{"get":{"operationId":"getHealth","summary":"Health check","description":"Returns a static payload proving the API process is up. Public, no authentication, does not touch the database.","tags":["system"],"security":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"status":{"type":"string","const":"ok","description":"Static readiness indicator."},"version":{"type":"string","description":"Hamelyn API major version, e.g. '6.0.0'."},"time":{"type":"string","description":"Current server time in ISO 8601 format."}},"required":["status","version","time"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/whoami":{"get":{"operationId":"getWhoami","summary":"Identify the current credentials","description":"Returns the user, roles and scopes behind the Authorization header. Works with any valid credentials regardless of scopes.","tags":["system"],"security":[{"bearerAuth":[]},{"oauth2":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"userId":{"type":"string","description":"Hamelyn user id (Mongo ObjectId as string)."},"email":{"type":"string","description":"Email address of the authenticated user."},"name":{"type":"string","description":"Display name of the authenticated user."},"roles":{"type":"array","items":{"type":"string"},"description":"Hamelyn role names assigned to the user (e.g. 'admin-user')."},"scopes":{"type":"array","items":{"type":"string","enum":["read:stocks","read:orders","read:ordersells","read:marketplaces","read:meta","read:bq","write:ordersells","write:orders","write:stocks","write:products","write:coupons","write:costs","write:issues","manage:tokens"]},"description":"API scopes granted to the current credentials."},"authMethod":{"type":"string","enum":["pat","auth0"],"description":"How the request was authenticated: personal access token or Auth0 session."},"token":{"anyOf":[{"type":"object","properties":{"id":{"type":"string","description":"Id of the personal access token used."},"name":{"type":"string","description":"Human-assigned name of the token."}},"required":["id","name"],"additionalProperties":false},{"type":"null"}],"description":"Details of the PAT used, or null for Auth0 sessions."}},"required":["userId","email","name","roles","scopes","authMethod","token"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/stocks":{"get":{"operationId":"listStocks","summary":"List stocks","description":"Lists stock items visible to API clients, with optional exact filters by stock status, EAN and drawer.","tags":["stocks"],"security":[{"bearerAuth":[]},{"oauth2":["read:stocks"]}],"parameters":[{"in":"query","name":"page","schema":{"default":1,"description":"1-based page number.","type":"integer","minimum":1,"maximum":9007199254740991},"description":"1-based page number."},{"in":"query","name":"per_page","schema":{"default":20,"description":"Number of items per page (max 100).","type":"integer","minimum":1,"maximum":100},"description":"Number of items per page (max 100)."},{"in":"query","name":"stock_status","schema":{"description":"Filter by operational stock status.","type":"string","enum":["ACTIVE","WITHDRAWN","PENDING_SKYPOD"]},"description":"Filter by operational stock status."},{"in":"query","name":"ean","schema":{"description":"Filter by product EAN stored in the stock product field.","type":"string","minLength":1},"description":"Filter by product EAN stored in the stock product field."},{"in":"query","name":"drawer","schema":{"description":"Filter by exact physical drawer code.","type":"string","minLength":1},"description":"Filter by exact physical drawer code."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Stock id, also known as the SKU string."},"ean":{"type":"string","description":"Product EAN stored in the stock product field."},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Product title, or null if missing."},"price":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Current stock sale price, or null."},"stockStatus":{"type":"string","enum":["ACTIVE","WITHDRAWN","PENDING_SKYPOD"],"description":"Operational stock status."},"status":{"type":"number","description":"Numeric conservation status of the stock item."},"productType":{"type":"string","enum":["book","movie","music","vinyl","game"],"description":"Product media type."},"creationDate":{"type":"string","description":"Stock creation date in ISO 8601 format."},"drawer":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Physical drawer code where the stock is stored, or null."}},"required":["id","ean","title","price","stockStatus","status","productType","creationDate","drawer"],"additionalProperties":false}},"meta":{"type":"object","properties":{"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Total number of items matching the query."},"page":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Current 1-based page number."},"per_page":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Number of items per page."},"has_more":{"type":"boolean","description":"Whether more pages exist after the current one."}},"required":["total","page","per_page","has_more"],"additionalProperties":false,"description":"Pagination metadata for list responses."}},"required":["ok","data","meta"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/stocks/{stockId}":{"get":{"operationId":"getStock","summary":"Get a stock item","description":"Returns one stock item by SKU string. The stock id is not a Mongo ObjectId.","tags":["stocks"],"security":[{"bearerAuth":[]},{"oauth2":["read:stocks"]}],"parameters":[{"in":"path","name":"stockId","schema":{"type":"string","minLength":1,"description":"Stock SKU string."},"required":true,"description":"Stock SKU string."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"id":{"type":"string","description":"Stock id, also known as the SKU string."},"ean":{"type":"string","description":"Product EAN stored in the stock product field."},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Product title, or null if missing."},"price":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Current stock sale price, or null."},"stockStatus":{"type":"string","enum":["ACTIVE","WITHDRAWN","PENDING_SKYPOD"],"description":"Operational stock status."},"status":{"type":"number","description":"Numeric conservation status of the stock item."},"productType":{"type":"string","enum":["book","movie","music","vinyl","game"],"description":"Product media type."},"creationDate":{"type":"string","description":"Stock creation date in ISO 8601 format."},"drawer":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Physical drawer code where the stock is stored, or null."},"asin":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Amazon ASIN linked to the stock, or null."},"orderId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Mongo ObjectId of the source order as a string, or null."},"image":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Primary image URL, or null."}},"required":["id","ean","title","price","stockStatus","status","productType","creationDate","drawer","asin","orderId","image"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/orders":{"get":{"operationId":"listOrders","summary":"List buy orders","description":"Lists buy orders with optional exact status and created-date range filters.","tags":["orders"],"security":[{"bearerAuth":[]},{"oauth2":["read:orders"]}],"parameters":[{"in":"query","name":"page","schema":{"default":1,"description":"1-based page number.","type":"integer","minimum":1,"maximum":9007199254740991},"description":"1-based page number."},{"in":"query","name":"per_page","schema":{"default":20,"description":"Number of items per page (max 100).","type":"integer","minimum":1,"maximum":100},"description":"Number of items per page (max 100)."},{"in":"query","name":"status","schema":{"description":"Filter by buy-order status.","type":"string","enum":["CHECKOUT","PENDING","PENDING_ERROR","IN_TRANSIT","SUSPICIOUS","PAID","PAYMENT_ERROR","PAYMENT_PENDING","PICKED_UP","CANCELLED","REVIEWED","REVIEW_IN_PROGRESS","MERGED"]},"description":"Filter by buy-order status."},{"in":"query","name":"created_from","schema":{"description":"Filter orders created at or after this ISO 8601 timestamp.","type":"string"},"description":"Filter orders created at or after this ISO 8601 timestamp."},{"in":"query","name":"created_to","schema":{"description":"Filter orders created at or before this ISO 8601 timestamp.","type":"string"},"description":"Filter orders created at or before this ISO 8601 timestamp."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Order id (Mongo ObjectId as string)."},"status":{"type":"string","enum":["CHECKOUT","PENDING","PENDING_ERROR","IN_TRANSIT","SUSPICIOUS","PAID","PAYMENT_ERROR","PAYMENT_PENDING","PICKED_UP","CANCELLED","REVIEWED","REVIEW_IN_PROGRESS","MERGED"],"description":"Current buy-order status."},"created":{"type":"string","description":"Order creation timestamp in ISO 8601 format."},"userId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Owner user id (Mongo ObjectId as string), or null when missing."},"productsCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Number of products in the order."},"creationType":{"anyOf":[{"type":"string","enum":["B2C","B2B","DONATION","INVENTORY"]},{"type":"null"}],"description":"Order creation channel, or null when legacy data is missing it."}},"required":["id","status","created","userId","productsCount","creationType"],"additionalProperties":false}},"meta":{"type":"object","properties":{"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Total number of items matching the query."},"page":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Current 1-based page number."},"per_page":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Number of items per page."},"has_more":{"type":"boolean","description":"Whether more pages exist after the current one."}},"required":["total","page","per_page","has_more"],"additionalProperties":false,"description":"Pagination metadata for list responses."}},"required":["ok","data","meta"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/orders/{orderId}":{"get":{"operationId":"getOrder","summary":"Get a buy order","description":"Returns one buy order by Mongo ObjectId, including product acceptance details.","tags":["orders"],"security":[{"bearerAuth":[]},{"oauth2":["read:orders"]}],"parameters":[{"in":"path","name":"orderId","schema":{"type":"string","pattern":"^[0-9a-f]{24}$","description":"Order id as a 24-character hex ObjectId."},"required":true,"description":"Order id as a 24-character hex ObjectId."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"id":{"type":"string","description":"Order id (Mongo ObjectId as string)."},"status":{"type":"string","enum":["CHECKOUT","PENDING","PENDING_ERROR","IN_TRANSIT","SUSPICIOUS","PAID","PAYMENT_ERROR","PAYMENT_PENDING","PICKED_UP","CANCELLED","REVIEWED","REVIEW_IN_PROGRESS","MERGED"],"description":"Current buy-order status."},"created":{"type":"string","description":"Order creation timestamp in ISO 8601 format."},"userId":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Owner user id (Mongo ObjectId as string), or null when missing."},"productsCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Number of products in the order."},"creationType":{"anyOf":[{"type":"string","enum":["B2C","B2B","DONATION","INVENTORY"]},{"type":"null"}],"description":"Order creation channel, or null when legacy data is missing it."},"products":{"type":"array","items":{"type":"object","properties":{"ean":{"type":"string","description":"Product EAN stored in the order product field."},"purchasePrice":{"type":"number","description":"Price Hamelyn pays for this product."},"accepted":{"type":"boolean","description":"Whether Hamelyn accepted this product."},"status":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Numeric conservation status, or null if missing."}},"required":["ean","purchasePrice","accepted","status"],"additionalProperties":false},"description":"Products in this order."},"reviewedDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Review completion timestamp in ISO 8601 format, or null."},"validationDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Validation timestamp in ISO 8601 format, or null."},"paymentDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Payment timestamp in ISO 8601 format, or null."}},"required":["id","status","created","userId","productsCount","creationType","products","reviewedDate","validationDate","paymentDate"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/ordersells":{"get":{"operationId":"listOrderSells","summary":"List marketplace sales","description":"Lists marketplace sell orders with optional exact marketplace, status, creation-date range and tracking-number filters.","tags":["ordersells"],"security":[{"bearerAuth":[]},{"oauth2":["read:ordersells"]}],"parameters":[{"in":"query","name":"page","schema":{"default":1,"description":"1-based page number.","type":"integer","minimum":1,"maximum":9007199254740991},"description":"1-based page number."},{"in":"query","name":"per_page","schema":{"default":20,"description":"Number of items per page (max 100).","type":"integer","minimum":1,"maximum":100},"description":"Number of items per page (max 100)."},{"in":"query","name":"marketplace","schema":{"description":"Filter by marketplace code.","type":"string","enum":["CDL","EBAY_ES","ABEBOOKS","TODO_COLECCION","FNAC","AMAZON_ES","AMAZON_COM","AMAZON_SE","AMAZON_MX","AMAZON_IT","AMAZON_FR","BIBLIO","BUSCALIBRE","WALLAPOP","MERCADOLIBRE","MERCADOLIBRE_GLOBAL","HAMELYN_ECOMMERCE","ZOOMBOOKS","GEORGETOWN","RUNNERS","BOOKXCHANGE","FELTRINELLI","KAUFLAND","DECATHLON","DISCOGS","ALIBRIS"]},"description":"Filter by marketplace code."},{"in":"query","name":"status","schema":{"description":"Filter by sell-order status.","type":"string","enum":["PENDING_LABEL","LABEL_ISSUE","PENDING_PICKING","IN_PICKING","PICKED_UP","TO_CANCEL","CANCELLED","REFUNDED","PENDING_PROVIDER_CONFIRMATION"]},"description":"Filter by sell-order status."},{"in":"query","name":"from","schema":{"description":"Filter sales created at or after this ISO 8601 timestamp.","type":"string"},"description":"Filter sales created at or after this ISO 8601 timestamp."},{"in":"query","name":"to","schema":{"description":"Filter sales created at or before this ISO 8601 timestamp.","type":"string"},"description":"Filter sales created at or before this ISO 8601 timestamp."},{"in":"query","name":"tracking","schema":{"description":"Find sales carrying this carrier or Hamelyn tracking number (exact match on either field).","type":"string","minLength":3},"description":"Find sales carrying this carrier or Hamelyn tracking number (exact match on either field)."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Order-sell id (Mongo ObjectId as string)."},"marketplace":{"type":"string","enum":["CDL","EBAY_ES","ABEBOOKS","TODO_COLECCION","FNAC","AMAZON_ES","AMAZON_COM","AMAZON_SE","AMAZON_MX","AMAZON_IT","AMAZON_FR","BIBLIO","BUSCALIBRE","WALLAPOP","MERCADOLIBRE","MERCADOLIBRE_GLOBAL","HAMELYN_ECOMMERCE","ZOOMBOOKS","GEORGETOWN","RUNNERS","BOOKXCHANGE","FELTRINELLI","KAUFLAND","DECATHLON","DISCOGS","ALIBRIS"],"description":"Marketplace that owns the sale."},"status":{"type":"string","enum":["PENDING_LABEL","LABEL_ISSUE","PENDING_PICKING","IN_PICKING","PICKED_UP","TO_CANCEL","CANCELLED","REFUNDED","PENDING_PROVIDER_CONFIRMATION"],"description":"Current sell-order status."},"carrier":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Carrier assigned to the sale, or null."},"shipTrackingNumber":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Carrier tracking number, or null."},"productsCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Number of products in the sale."},"creationDate":{"type":"string","description":"Sale creation date in ISO 8601 format."},"totalPrice":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Total sale price, or null."}},"required":["id","marketplace","status","carrier","shipTrackingNumber","productsCount","creationDate","totalPrice"],"additionalProperties":false}},"meta":{"type":"object","properties":{"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Total number of items matching the query."},"page":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Current 1-based page number."},"per_page":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Number of items per page."},"has_more":{"type":"boolean","description":"Whether more pages exist after the current one."}},"required":["total","page","per_page","has_more"],"additionalProperties":false,"description":"Pagination metadata for list responses."}},"required":["ok","data","meta"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/ordersells/{orderSellId}":{"get":{"operationId":"getOrderSell","summary":"Get a marketplace sale","description":"Returns one marketplace sell order by Mongo ObjectId without address, phone or email data.","tags":["ordersells"],"security":[{"bearerAuth":[]},{"oauth2":["read:ordersells"]}],"parameters":[{"in":"path","name":"orderSellId","schema":{"type":"string","pattern":"^[0-9a-f]{24}$","description":"Order-sell id as a 24-character hex ObjectId."},"required":true,"description":"Order-sell id as a 24-character hex ObjectId."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"id":{"type":"string","description":"Order-sell id (Mongo ObjectId as string)."},"marketplace":{"type":"string","enum":["CDL","EBAY_ES","ABEBOOKS","TODO_COLECCION","FNAC","AMAZON_ES","AMAZON_COM","AMAZON_SE","AMAZON_MX","AMAZON_IT","AMAZON_FR","BIBLIO","BUSCALIBRE","WALLAPOP","MERCADOLIBRE","MERCADOLIBRE_GLOBAL","HAMELYN_ECOMMERCE","ZOOMBOOKS","GEORGETOWN","RUNNERS","BOOKXCHANGE","FELTRINELLI","KAUFLAND","DECATHLON","DISCOGS","ALIBRIS"],"description":"Marketplace that owns the sale."},"status":{"type":"string","enum":["PENDING_LABEL","LABEL_ISSUE","PENDING_PICKING","IN_PICKING","PICKED_UP","TO_CANCEL","CANCELLED","REFUNDED","PENDING_PROVIDER_CONFIRMATION"],"description":"Current sell-order status."},"carrier":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Carrier assigned to the sale, or null."},"shipTrackingNumber":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Carrier tracking number, or null."},"productsCount":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Number of products in the sale."},"creationDate":{"type":"string","description":"Sale creation date in ISO 8601 format."},"totalPrice":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Total sale price, or null."},"fullName":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Buyer full name. Address, phone and email are never returned."},"marketplaceData":{"type":"object","properties":{"status":{"anyOf":[{"type":"string","enum":["PENDING","SHIPPED","SYNC_ERROR"]},{"type":"null"}],"description":"Marketplace-side shipment status, or null."}},"required":["status"],"additionalProperties":false,"description":"Safe marketplace metadata for this sale."},"pickedUpDate":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Pickup timestamp in ISO 8601 format, or null."},"products":{"type":"array","items":{"type":"object","properties":{"stock":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Stock SKU string linked to this sale line, or null when missing."},"ean":{"type":"string","description":"Product EAN for this sale line."},"price":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Sale line price, or null."},"title":{"type":"string","description":"Product title for this sale line."}},"required":["stock","ean","price","title"],"additionalProperties":false},"description":"Products in this sale."}},"required":["id","marketplace","status","carrier","shipTrackingNumber","productsCount","creationDate","totalPrice","fullName","marketplaceData","pickedUpDate","products"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/products/{ean}":{"get":{"operationId":"getProduct","summary":"Get a product by EAN","description":"Returns catalog detail for one product looked up by its EAN/barcode (products are keyed by EAN).","tags":["products"],"security":[{"bearerAuth":[]},{"oauth2":["read:stocks"]}],"parameters":[{"in":"path","name":"ean","schema":{"type":"string","pattern":"^[0-9A-Za-z._-]{6,20}$","description":"Product EAN/barcode."},"required":true,"description":"Product EAN/barcode."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"ean":{"type":"string","description":"Product EAN/barcode (this is the product _id)."},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Product title, or null."},"productType":{"anyOf":[{"type":"string","enum":["book","movie","music","vinyl","game"]},{"type":"null"}],"description":"Product type (book, music, movie...), or null."},"categories":{"type":"array","items":{"type":"string"},"description":"Category ids the product belongs to."},"authors":{"type":"array","items":{"type":"string"},"description":"Author names, when applicable."},"publisher":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Publisher, or null."},"image":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Cover image URL, or null."},"priceSell":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Current sell price, or null."},"priceBuy":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Reference buy price, or null."},"priceNew":{"anyOf":[{"type":"number"},{"type":"null"}],"description":"Market price of a new copy, or null."}},"required":["ean","title","productType","categories","authors","publisher","image","priceSell","priceBuy","priceNew"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/catalog/{ean}":{"get":{"operationId":"getCatalogItem","summary":"Public catalog lookup by EAN/ISBN","description":"Returns the public catalog data of one product: title, catalog attributes, availability and current store price. Exposes only what is already publicly visible on the Hamelyn store. Public, no authentication.","tags":["catalog"],"security":[],"parameters":[{"in":"path","name":"ean","schema":{"type":"string","pattern":"^[0-9][0-9\\s-]{6,18}[0-9Xx]$","description":"EAN-13, ISBN-13, ISBN-10 (X check digit allowed) or UPC code; hyphens and spaces accepted; normalized to the canonical EAN-13."},"required":true,"description":"EAN-13, ISBN-13, ISBN-10 (X check digit allowed) or UPC code; hyphens and spaces accepted; normalized to the canonical EAN-13."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"ean":{"type":"string","description":"Product EAN/ISBN (the catalog id)."},"title":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Product title, or null."},"productType":{"anyOf":[{"type":"string","enum":["book","movie","music","vinyl","game"]},{"type":"null"}],"description":"Product type (book, music, movie...), or null."},"authors":{"description":"Author names, when applicable.","type":"array","items":{"type":"string"}},"brand":{"description":"Brand, when applicable.","type":"string"},"platform":{"description":"Platform (e.g. a video game console), when applicable.","type":"string"},"releaseYear":{"description":"Release year, when known.","type":"number"},"inStock":{"type":"boolean","description":"Whether at least one unit is on sale right now."},"price":{"description":"Current best price; absent when out of stock.","type":"object","properties":{"amount":{"type":"number","description":"Cheapest current offer on the store."},"currency":{"type":"string","const":"EUR","description":"Prices are always in euros."}},"required":["amount","currency"],"additionalProperties":false},"url":{"type":"string","description":"Product page on the Hamelyn store."}},"required":["ean","title","productType","inStock","url"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"404":{"description":"No product found with that EAN","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/marketplaces/{marketplace}/sync-status":{"get":{"operationId":"getMarketplaceSyncStatus","summary":"Get marketplace sync status","description":"Aggregates one marketplace collection to report document totals, status counts and latest activity.","tags":["marketplaces"],"security":[{"bearerAuth":[]},{"oauth2":["read:marketplaces"]}],"parameters":[{"in":"path","name":"marketplace","schema":{"type":"string","enum":["casaDelLibro","ebayEs","abebooks","todoColeccion","fnac","fnacPt","fnacFr","amazonEs","amazonCom","amazonSe","amazonMx","amazonIt","amazonFr","buscalibre","wallapop","mercadolibre","mercadolibreGlobal","hamelynEcommerce","zoombooks","georgetown","runners","bookxchange","feltrinelli","kaufland","biblio","decathlon","discogs","alibris"],"description":"Marketplace database suffix, e.g. amazonEs or casaDelLibro."},"required":true,"description":"Marketplace database suffix, e.g. amazonEs or casaDelLibro."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"marketplace":{"type":"string","enum":["casaDelLibro","ebayEs","abebooks","todoColeccion","fnac","fnacPt","fnacFr","amazonEs","amazonCom","amazonSe","amazonMx","amazonIt","amazonFr","buscalibre","wallapop","mercadolibre","mercadolibreGlobal","hamelynEcommerce","zoombooks","georgetown","runners","bookxchange","feltrinelli","kaufland","biblio","decathlon","discogs","alibris"],"description":"Marketplace database suffix used in the mk_<marketplace> collection."},"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Total documents in the marketplace collection."},"byStatus":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},"description":"Document counts grouped by the marketplace document status field. SYNC_ERROR here counts every status match regardless of stockStatus (includes already-withdrawn stock); see syncError for the honest split."},"syncError":{"anyOf":[{"type":"object","properties":{"total":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Documents with status SYNC_ERROR across all stock."},"onActiveStock":{"anyOf":[{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991},{"type":"null"}],"description":"SYNC_ERROR docs whose stock is still ACTIVE (errors on stock we still intend to sell); excludes already-withdrawn stock. null when the bounded count timed out."}},"required":["total","onActiveStock"],"additionalProperties":false},{"type":"null"}],"description":"Honest SYNC_ERROR split excluding withdrawn stock. Null when there are no sync errors. Corregible-vs-structural detail lives in the BigQuery sync dashboards."},"lastActivity":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Latest lastUpdatedTimestamp in ISO 8601 format, or null."}},"required":["marketplace","total","byStatus","syncError","lastActivity"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/meta/enums":{"get":{"operationId":"getEnums","summary":"List API enum values","description":"Returns static enum values and API constants useful for client validation and discovery.","tags":["meta"],"security":[{"bearerAuth":[]},{"oauth2":["read:meta"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"marketplaces":{"type":"array","items":{"type":"string","enum":["CDL","EBAY_ES","ABEBOOKS","TODO_COLECCION","FNAC","AMAZON_ES","AMAZON_COM","AMAZON_SE","AMAZON_MX","AMAZON_IT","AMAZON_FR","BIBLIO","BUSCALIBRE","WALLAPOP","MERCADOLIBRE","MERCADOLIBRE_GLOBAL","HAMELYN_ECOMMERCE","ZOOMBOOKS","GEORGETOWN","RUNNERS","BOOKXCHANGE","FELTRINELLI","KAUFLAND","DECATHLON","DISCOGS","ALIBRIS"]},"description":"Allowed Marketplaces enum values for order-sell filters."},"marketplaceDatabases":{"type":"array","items":{"type":"string","enum":["casaDelLibro","ebayEs","abebooks","todoColeccion","fnac","fnacPt","fnacFr","amazonEs","amazonCom","amazonSe","amazonMx","amazonIt","amazonFr","buscalibre","wallapop","mercadolibre","mercadolibreGlobal","hamelynEcommerce","zoombooks","georgetown","runners","bookxchange","feltrinelli","kaufland","biblio","decathlon","discogs","alibris"]},"description":"Allowed MarketplaceDatabase enum values for mk_* collections."},"marketplaceStatus":{"type":"array","items":{"type":"string"},"description":"MarketplaceStatus enum member names."},"orderStatus":{"type":"array","items":{"type":"string","enum":["CHECKOUT","PENDING","PENDING_ERROR","IN_TRANSIT","SUSPICIOUS","PAID","PAYMENT_ERROR","PAYMENT_PENDING","PICKED_UP","CANCELLED","REVIEWED","REVIEW_IN_PROGRESS","MERGED"]},"description":"Allowed OrderStatus enum values."},"orderSellStatus":{"type":"array","items":{"type":"string","enum":["PENDING_LABEL","LABEL_ISSUE","PENDING_PICKING","IN_PICKING","PICKED_UP","TO_CANCEL","CANCELLED","REFUNDED","PENDING_PROVIDER_CONFIRMATION"]},"description":"Allowed OrderSellStatus enum values."},"stockStatus":{"type":"array","items":{"type":"string","enum":["ACTIVE","WITHDRAWN","PENDING_SKYPOD"]},"description":"Allowed StockStatus enum values."},"productTypes":{"type":"array","items":{"type":"string","enum":["book","movie","music","vinyl","game"]},"description":"Allowed ProductType enum values."},"apiScopes":{"type":"array","items":{"type":"string","enum":["read:stocks","read:orders","read:ordersells","read:marketplaces","read:meta","read:bq","write:ordersells","write:orders","write:stocks","write:products","write:coupons","write:costs","write:issues","manage:tokens"]},"description":"API scopes accepted by v6."},"errorCodes":{"type":"array","items":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"]},"description":"Stable API error code names returned in error envelopes."}},"required":["marketplaces","marketplaceDatabases","marketplaceStatus","orderStatus","orderSellStatus","stockStatus","productTypes","apiScopes","errorCodes"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/meta/operations":{"get":{"operationId":"listWriteOperations","summary":"List the gates of every mutating operation","description":"Returns the write-operation policy: for each mutation, the scope, minimum role, idempotency and Auth0-only requirements. This is the single source of truth for what gates each operation carries.","tags":["meta"],"security":[{"bearerAuth":[]},{"oauth2":["read:meta"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"array","items":{"type":"object","properties":{"operationId":{"type":"string","description":"Stable id of the mutating operation."},"method":{"type":"string","enum":["post","put","delete"],"description":"HTTP method of the operation."},"path":{"type":"string","description":"API path of the operation."},"scope":{"type":"string","enum":["read:stocks","read:orders","read:ordersells","read:marketplaces","read:meta","read:bq","write:ordersells","write:orders","write:stocks","write:products","write:coupons","write:costs","write:issues","manage:tokens"],"description":"Write scope a token must carry to run it."},"minRole":{"type":"string","description":"Minimum Hamelyn role (by rank) allowed to run it."},"idempotent":{"type":"boolean","description":"Whether it honours an Idempotency-Key header."},"auth0Only":{"type":"boolean","description":"Whether it rejects PATs and requires an Auth0 session."},"decision":{"type":"string","enum":["expose","gate"],"description":"Exposure tier decided in the mutations review."},"summary":{"type":"string","description":"What the operation does."}},"required":["operationId","method","path","scope","minRole","idempotent","auth0Only","decision","summary"],"additionalProperties":false}}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/bq/query":{"post":{"operationId":"runBqQuery","summary":"Run a read-only BigQuery SQL query","description":"Executes standard SQL against the hamelyn-data BigQuery project with a read-only service account. Writes are impossible at the IAM level. Every query carries a maximum-bytes-billed guard (default 25 GiB, hard server cap 100 GiB) and is labeled for auditing. Use dry_run to estimate scanned bytes without executing. Data freshness lags Mongo by up to ~1 hour.","tags":["bq"],"security":[{"bearerAuth":[]},{"oauth2":["read:bq"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"sql":{"type":"string","minLength":8,"maxLength":20000,"description":"Standard SQL SELECT to execute. Only reads can succeed."},"dry_run":{"default":false,"description":"Estimate bytes without running the query.","type":"boolean"},"max_bytes_billed":{"description":"Per-query bytes-billed guard. Clamped to the 100 GiB server cap.","type":"integer","exclusiveMinimum":0,"maximum":9007199254740991},"max_rows":{"description":"Maximum rows returned (default 200, max 2000).","type":"integer","minimum":1,"maximum":2000}},"required":["sql"]}}}},"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"rows":{"type":"array","items":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{}},"description":"Result rows as JSON records (empty on dry runs)."},"row_count":{"type":"integer","minimum":-9007199254740991,"maximum":9007199254740991,"description":"Number of rows returned."},"truncated":{"type":"boolean","description":"True when the result exceeded max_rows and was cut."},"dry_run":{"type":"boolean","description":"Whether this was a dry run."},"stats":{"type":"object","properties":{"bytes_processed":{"type":"number","description":"Bytes BigQuery scanned (or would scan, on dry runs)."},"bytes_billed":{"type":"number","description":"Bytes actually billed for the query."},"cache_hit":{"type":"boolean","description":"Whether BigQuery served the result from cache (free)."},"job_id":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"BigQuery job id, traceable in INFORMATION_SCHEMA.JOBS."},"duration_ms":{"type":"number","description":"Wall-clock milliseconds spent server-side on the query."}},"required":["bytes_processed","bytes_billed","cache_hit","job_id","duration_ms"],"additionalProperties":false}},"required":["rows","row_count","truncated","dry_run","stats"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/bq/tables":{"get":{"operationId":"listBqTables","summary":"List BigQuery datasets or the tables of one dataset","description":"Without parameters returns every dataset in the hamelyn-data project. With ?dataset= returns the tables of that dataset. Use this to discover what can be queried before writing SQL.","tags":["bq"],"security":[{"bearerAuth":[]},{"oauth2":["read:bq"]}],"parameters":[{"in":"query","name":"dataset","schema":{"description":"Dataset id to list tables for. Omit to list datasets.","type":"string","pattern":"^[A-Za-z0-9_]+$"},"description":"Dataset id to list tables for. Omit to list datasets."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"datasets":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Dataset id."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Dataset description from BigQuery metadata."}},"required":["id","description"],"additionalProperties":false},"description":"Datasets (empty when a dataset was specified)."},"tables":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Table id within the dataset."},"type":{"type":"string","description":"BigQuery object type, e.g. TABLE or VIEW."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Table description from BigQuery metadata."}},"required":["id","type","description"],"additionalProperties":false},"description":"Tables of the requested dataset (empty when listing datasets)."}},"required":["datasets","tables"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/bq/tables/{tableRef}":{"get":{"operationId":"getBqTable","summary":"Describe a BigQuery table (schema, rows, size)","description":"Returns the column schema and basic stats of one table. tableRef is 'dataset.table', e.g. 'marketplace_snapshots.mercadolibre_listings'. Use it before writing SQL against an unfamiliar table.","tags":["bq"],"security":[{"bearerAuth":[]},{"oauth2":["read:bq"]}],"parameters":[{"in":"path","name":"tableRef","schema":{"type":"string","pattern":"^[A-Za-z0-9_]+\\.[A-Za-z0-9_$]+$","description":"Table reference as dataset.table."},"required":true,"description":"Table reference as dataset.table."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"dataset":{"type":"string","description":"Dataset id."},"table":{"type":"string","description":"Table id."},"num_rows":{"type":"number","description":"Row count reported by BigQuery metadata."},"num_bytes":{"type":"number","description":"Storage size in bytes."},"columns":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string","description":"Column name."},"type":{"type":"string","description":"BigQuery column type, e.g. STRING."},"mode":{"type":"string","description":"NULLABLE, REQUIRED or REPEATED."},"description":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Column description when present."}},"required":["name","type","mode","description"],"additionalProperties":false},"description":"Column definitions."}},"required":["dataset","table","num_rows","num_bytes","columns"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/tokens":{"post":{"operationId":"createToken","summary":"Create a personal access token","description":"Creates a PAT for the authenticated user. Requires an Auth0 session with the manage:tokens scope, granted by role: admin-user may mint any scopes, the cli role may mint read-only scopes for itself. PATs can never mint other tokens. The clear-text token is returned exactly once in this response.","tags":["tokens"],"security":[{"bearerAuth":[]},{"oauth2":["manage:tokens"]}],"requestBody":{"content":{"application/json":{"schema":{"type":"object","properties":{"name":{"type":"string","minLength":3,"maxLength":64,"description":"Display name for the token, e.g. 'javier-cli-readonly'."},"scopes":{"minItems":1,"type":"array","items":{"type":"string","enum":["read:stocks","read:orders","read:ordersells","read:marketplaces","read:meta","read:bq","write:ordersells","write:orders","write:stocks","write:products","write:coupons","write:costs","write:issues","manage:tokens"]},"description":"Scopes to grant. Use read:* scopes for read-only tokens."},"expires_in_days":{"description":"Days until expiry. Omit for a non-expiring token.","type":"integer","minimum":1,"maximum":365}},"required":["name","scopes"]}}}},"responses":{"201":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"id":{"type":"string","description":"Token id (Mongo ObjectId as string)."},"name":{"type":"string","description":"Human-assigned name of the token."},"tokenPrefix":{"type":"string","description":"First characters of the token, safe to display (hmk_xxxxxx)."},"scopes":{"type":"array","items":{"type":"string","enum":["read:stocks","read:orders","read:ordersells","read:marketplaces","read:meta","read:bq","write:ordersells","write:orders","write:stocks","write:products","write:coupons","write:costs","write:issues","manage:tokens"]},"description":"Scopes granted to this token."},"createdAt":{"type":"string","description":"Creation time, ISO 8601."},"expiresAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Expiry time in ISO 8601, or null if the token never expires."},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Last authentication time in ISO 8601, or null if never used."},"token":{"type":"string","description":"The clear-text token (hmk_...). Shown only once; store it securely."}},"required":["id","name","tokenPrefix","scopes","createdAt","expiresAt","lastUsedAt","token"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}},"get":{"operationId":"listTokens","summary":"List the personal access tokens of the current user","description":"Lists the caller's non-revoked PATs. Token secrets are never returned, only display prefixes.","tags":["tokens"],"security":[{"bearerAuth":[]},{"oauth2":["manage:tokens"]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"array","items":{"type":"object","properties":{"id":{"type":"string","description":"Token id (Mongo ObjectId as string)."},"name":{"type":"string","description":"Human-assigned name of the token."},"tokenPrefix":{"type":"string","description":"First characters of the token, safe to display (hmk_xxxxxx)."},"scopes":{"type":"array","items":{"type":"string","enum":["read:stocks","read:orders","read:ordersells","read:marketplaces","read:meta","read:bq","write:ordersells","write:orders","write:stocks","write:products","write:coupons","write:costs","write:issues","manage:tokens"]},"description":"Scopes granted to this token."},"createdAt":{"type":"string","description":"Creation time, ISO 8601."},"expiresAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Expiry time in ISO 8601, or null if the token never expires."},"lastUsedAt":{"anyOf":[{"type":"string"},{"type":"null"}],"description":"Last authentication time in ISO 8601, or null if never used."}},"required":["id","name","tokenPrefix","scopes","createdAt","expiresAt","lastUsedAt"],"additionalProperties":false}}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/tokens/{tokenId}":{"delete":{"operationId":"deleteToken","summary":"Revoke a personal access token","description":"Revokes one of the caller's PATs. Revocation is immediate and permanent; the token stops authenticating on the next request.","tags":["tokens"],"security":[{"bearerAuth":[]},{"oauth2":["manage:tokens"]}],"parameters":[{"in":"path","name":"tokenId","schema":{"type":"string","pattern":"^[0-9a-f]{24}$","description":"Id of the token to revoke."},"required":true,"description":"Id of the token to revoke."}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":true,"description":"Always true for success responses."},"data":{"type":"object","properties":{"revoked":{"type":"boolean","const":true,"description":"Confirmation flag."},"id":{"type":"string","description":"Id of the revoked token."}},"required":["revoked","id"],"additionalProperties":false}},"required":["ok","data"],"additionalProperties":false,"description":"Canonical v6 success envelope."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"401":{"description":"Missing, invalid or expired credentials","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"403":{"description":"Authenticated but missing scopes","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"429":{"description":"Rate limit exceeded (300 requests per 60s); retry after the seconds in Retry-After","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}},"/api/v6/openapi.json":{"get":{"operationId":"getOpenApiDocument","summary":"OpenAPI 3.1 document","description":"Returns the full OpenAPI 3.1 specification of this API as committed at build time. Public, served raw without the response envelope.","tags":["system"],"security":[],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","propertyNames":{"type":"string"},"additionalProperties":{},"description":"OpenAPI 3.1 document describing every v6 endpoint."}}}},"400":{"description":"Malformed request or failed validation","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}},"500":{"description":"Unexpected server error","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","const":false,"description":"Always false for error responses."},"error":{"type":"object","properties":{"code":{"type":"string","enum":["BAD_REQUEST","VALIDATION_ERROR","UNAUTHORIZED","INVALID_TOKEN","TOKEN_EXPIRED","TOKEN_REVOKED","FORBIDDEN","NOT_FOUND","CONFLICT","RATE_LIMITED","INTERNAL_ERROR"],"description":"Stable machine-readable error code."},"message":{"type":"string","description":"Human-readable error description."},"details":{"description":"Optional structured error context (e.g. validation issues)."}},"required":["code","message"],"additionalProperties":false}},"required":["ok","error"],"additionalProperties":false,"description":"Canonical v6 error envelope."}}}}}}}},"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","description":"Auth0 access token or Hamelyn personal access token (hmk_...)"},"oauth2":{"type":"oauth2","description":"Auth0 flow of the Hamelyn tenant. Each operation lists the scopes it requires, and the API enforces them per request against the scopes granted server-side: an Auth0 session gets the scopes derived from the user's Hamelyn roles (the token's own `scope` claim is not read) and a personal access token carries the scopes selected when it was minted. Do not request these scopes through the authorize `scope` parameter — it neither grants nor restricts them. The flows supported today are the device flow used by the Hamelyn CLI (`hamelyn auth login`) and personal access tokens (`hmk_...`) sent via bearerAuth; for authorization-code requests use PKCE (S256) and keep the `audience=https://hamelyn.eu.auth0.com/api/v2/` parameter (already in the authorizationUrl): tokens minted without that audience are rejected with 401.","flows":{"authorizationCode":{"authorizationUrl":"https://auth.hamelyn.com/authorize?audience=https%3A%2F%2Fhamelyn.eu.auth0.com%2Fapi%2Fv2%2F","tokenUrl":"https://auth.hamelyn.com/oauth/token","scopes":{"read:stocks":"Read inventory stock units and product catalog data","read:orders":"Read purchase orders","read:ordersells":"Read marketplace and store sales","read:marketplaces":"Read marketplace sync status","read:meta":"Read API metadata (enums, write-operation gates)","read:bq":"Run read-only BigQuery queries","write:ordersells":"Edit sales: shipping, labels, manifests, invoices","write:orders":"Edit purchase orders and their workflow status","write:stocks":"Edit or withdraw inventory stock units","write:products":"Edit product catalog data and flags","write:coupons":"Create and edit discount coupons","write:costs":"Edit cost data","write:issues":"Create internal issues","manage:tokens":"Create, list and revoke personal access tokens"}}}}}}}