2300 lines
90 KiB
JavaScript
2300 lines
90 KiB
JavaScript
/**
|
|
* OpenAPI 3.0 Specification
|
|
*
|
|
* Full API documentation for Cashumints.space
|
|
*/
|
|
|
|
export const openApiSpec = {
|
|
openapi: '3.0.3',
|
|
info: {
|
|
title: 'Cashumints.space API',
|
|
description: `
|
|
# Cashumints.space API
|
|
|
|
A decentralized observability, discovery, and reputation API for the Cashu mint ecosystem.
|
|
|
|
## Overview
|
|
|
|
This API provides:
|
|
- **Mint Discovery** - Track Cashu mints across the ecosystem
|
|
- **Uptime Monitoring** - Continuous probing with historical data
|
|
- **Metadata Tracking** - NUT-06 compliant metadata with change history
|
|
- **Nostr Reviews** - NIP-87 based review ingestion from relays
|
|
- **Trust Scores** - Transparent, explainable reputation scoring
|
|
- **Analytics** - Pageviews, trending, and popularity metrics
|
|
|
|
## Key Concepts
|
|
|
|
### Mint Identity
|
|
Each mint is identified by a stable \`mint_id\` (UUID). A mint can have multiple URLs (clearnet, Tor, mirrors). All URLs resolve to the same mint identity.
|
|
|
|
### Mint Status
|
|
Mints cycle through states: \`unknown\` → \`online\` → \`degraded\` → \`offline\` → \`abandoned\`
|
|
|
|
### Trust Scores
|
|
Scores range from 0-100 and are computed from:
|
|
- Uptime reliability (40 points max)
|
|
- Response speed (25 points max)
|
|
- Nostr reviews (20 points max)
|
|
- Identity completeness (10 points max)
|
|
- Penalties for instability (up to -15 points)
|
|
|
|
## Authentication
|
|
This is a public, read-only API. No authentication required for read access.
|
|
|
|
## Rate Limiting
|
|
100 requests per minute per IP. Rate limit headers are included in responses.
|
|
|
|
## Admin API
|
|
Admin endpoints require the \`X-Admin-Api-Key\` header. All admin actions are audited. Admin operations never delete raw data - they annotate, correct routing, or trigger recomputation.
|
|
`,
|
|
version: '1.0.0',
|
|
contact: {
|
|
name: 'Cashumints.space',
|
|
url: 'https://cashumints.space'
|
|
},
|
|
license: {
|
|
name: 'MIT',
|
|
url: 'https://opensource.org/licenses/MIT'
|
|
}
|
|
},
|
|
externalDocs: {
|
|
description: 'Cashu Protocol Documentation',
|
|
url: 'https://docs.cashu.space'
|
|
},
|
|
servers: [{
|
|
url: '/v1',
|
|
description: 'Current server (relative)'
|
|
},
|
|
{
|
|
url: 'https://api.cashumints.space/v1',
|
|
description: 'Production API'
|
|
},
|
|
{
|
|
url: 'http://localhost:3000/v1',
|
|
description: 'Local development'
|
|
}
|
|
],
|
|
tags: [{
|
|
name: 'System',
|
|
description: 'Health checks and system statistics'
|
|
},
|
|
{
|
|
name: 'Mints',
|
|
description: 'Mint discovery and listing'
|
|
},
|
|
{
|
|
name: 'Mint Details',
|
|
description: 'Detailed mint information'
|
|
},
|
|
{
|
|
name: 'Metadata',
|
|
description: 'NUT-06 metadata endpoints'
|
|
},
|
|
{
|
|
name: 'Uptime',
|
|
description: 'Uptime and reliability metrics'
|
|
},
|
|
{
|
|
name: 'Trust',
|
|
description: 'Trust scores and reputation'
|
|
},
|
|
{
|
|
name: 'Reviews',
|
|
description: 'Nostr-based reviews (NIP-87)'
|
|
},
|
|
{
|
|
name: 'Analytics',
|
|
description: 'Pageviews and popularity'
|
|
},
|
|
{
|
|
name: 'Admin',
|
|
description: 'Admin-only endpoints (requires X-Admin-Api-Key header)'
|
|
}
|
|
],
|
|
paths: {
|
|
'/health': {
|
|
get: {
|
|
tags: ['System'],
|
|
summary: 'Health check',
|
|
description: 'Returns the health status of the API',
|
|
operationId: 'getHealth',
|
|
responses: {
|
|
200: {
|
|
description: 'API is healthy',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/HealthResponse' }
|
|
}
|
|
}
|
|
},
|
|
503: {
|
|
description: 'API is unhealthy',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/HealthResponse' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'/stats': {
|
|
get: {
|
|
tags: ['System'],
|
|
summary: 'System statistics',
|
|
description: 'Returns overall system statistics including mint counts, activity, and trending data',
|
|
operationId: 'getStats',
|
|
responses: {
|
|
200: {
|
|
description: 'System statistics',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/StatsResponse' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'/reviews': {
|
|
get: {
|
|
tags: ['Reviews'],
|
|
summary: 'Global reviews feed',
|
|
description: 'Returns all reviews across the ecosystem with optional filtering',
|
|
operationId: 'getGlobalReviews',
|
|
parameters: [{
|
|
name: 'mint_id',
|
|
in: 'query',
|
|
description: 'Filter by mint ID',
|
|
schema: { type: 'string', format: 'uuid' }
|
|
},
|
|
{
|
|
name: 'mint_url',
|
|
in: 'query',
|
|
description: 'Filter by mint URL',
|
|
schema: { type: 'string' }
|
|
},
|
|
{
|
|
name: 'since',
|
|
in: 'query',
|
|
description: 'Unix timestamp - only reviews after this time',
|
|
schema: { type: 'integer' }
|
|
},
|
|
{
|
|
name: 'until',
|
|
in: 'query',
|
|
description: 'Unix timestamp - only reviews before this time',
|
|
schema: { type: 'integer' }
|
|
},
|
|
{
|
|
name: 'limit',
|
|
in: 'query',
|
|
description: 'Maximum number of results',
|
|
schema: { type: 'integer', default: 50, maximum: 200 }
|
|
},
|
|
{
|
|
name: 'offset',
|
|
in: 'query',
|
|
description: 'Number of results to skip',
|
|
schema: { type: 'integer', default: 0 }
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Review list',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/GlobalReviewsResponse' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'/analytics/uptime': {
|
|
get: {
|
|
tags: ['Analytics'],
|
|
summary: 'Ecosystem uptime analytics',
|
|
description: 'Returns ecosystem-wide uptime and reliability metrics',
|
|
operationId: 'getUptimeAnalytics',
|
|
parameters: [{
|
|
name: 'window',
|
|
in: 'query',
|
|
description: 'Time window for analytics',
|
|
schema: {
|
|
type: 'string',
|
|
enum: ['24h', '7d', '30d'],
|
|
default: '24h'
|
|
}
|
|
}],
|
|
responses: {
|
|
200: {
|
|
description: 'Uptime analytics',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/UptimeAnalytics' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'/analytics/versions': {
|
|
get: {
|
|
tags: ['Analytics'],
|
|
summary: 'Version distribution analytics',
|
|
description: 'Returns mint version distribution across the ecosystem',
|
|
operationId: 'getVersionAnalytics',
|
|
responses: {
|
|
200: {
|
|
description: 'Version analytics',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/VersionAnalytics' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'/analytics/nuts': {
|
|
get: {
|
|
tags: ['Analytics'],
|
|
summary: 'NUT support analytics',
|
|
description: 'Returns NUT support distribution across all mints',
|
|
operationId: 'getNutsAnalytics',
|
|
responses: {
|
|
200: {
|
|
description: 'NUT analytics',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/NutsAnalytics' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'/mints/trending': {
|
|
get: {
|
|
tags: ['Mints'],
|
|
summary: 'Get trending mints',
|
|
description: 'Returns mints ranked by view velocity (popularity)',
|
|
operationId: 'getTrendingMints',
|
|
parameters: [{
|
|
name: 'window',
|
|
in: 'query',
|
|
description: 'Time window for trending calculation',
|
|
schema: {
|
|
type: 'string',
|
|
enum: ['24h', '7d'],
|
|
default: '7d'
|
|
}
|
|
},
|
|
{
|
|
name: 'limit',
|
|
in: 'query',
|
|
description: 'Maximum number of results',
|
|
schema: { type: 'integer', default: 10, maximum: 50 }
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Trending mints',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/TrendingMintsResponse' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'/mints': {
|
|
get: {
|
|
tags: ['Mints'],
|
|
summary: 'List all mints',
|
|
description: 'Returns a paginated list of all tracked mints with optional filtering',
|
|
operationId: 'listMints',
|
|
parameters: [{
|
|
name: 'status',
|
|
in: 'query',
|
|
description: 'Filter by mint status',
|
|
schema: {
|
|
type: 'string',
|
|
enum: ['unknown', 'online', 'degraded', 'offline', 'abandoned']
|
|
}
|
|
},
|
|
{
|
|
name: 'limit',
|
|
in: 'query',
|
|
description: 'Maximum number of results',
|
|
schema: { type: 'integer', default: 100, maximum: 500 }
|
|
},
|
|
{
|
|
name: 'offset',
|
|
in: 'query',
|
|
description: 'Number of results to skip',
|
|
schema: { type: 'integer', default: 0 }
|
|
},
|
|
{
|
|
name: 'sort_by',
|
|
in: 'query',
|
|
description: 'Sort field',
|
|
schema: {
|
|
type: 'string',
|
|
enum: ['created_at', 'updated_at', 'name', 'trust_score'],
|
|
default: 'created_at'
|
|
}
|
|
},
|
|
{
|
|
name: 'sort_order',
|
|
in: 'query',
|
|
description: 'Sort order',
|
|
schema: { type: 'string', enum: ['asc', 'desc'], default: 'desc' }
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'List of mints',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintListResponse' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'/mints/submit': {
|
|
post: {
|
|
tags: ['Mints'],
|
|
summary: 'Submit a new mint',
|
|
description: 'Submit a mint URL for tracking. The mint will be probed and added to the index.',
|
|
operationId: 'submitMint',
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintSubmission' }
|
|
}
|
|
}
|
|
},
|
|
responses: {
|
|
201: {
|
|
description: 'Mint submitted successfully',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintSubmissionResponse' }
|
|
}
|
|
}
|
|
},
|
|
400: {
|
|
description: 'Invalid URL or submission error',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/Error' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'/mints/{mint_id}': {
|
|
get: {
|
|
tags: ['Mint Details'],
|
|
summary: 'Get mint by ID',
|
|
description: 'Returns detailed information about a specific mint',
|
|
operationId: 'getMintById',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintId' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Mint details',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/Mint' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/by-url': {
|
|
get: {
|
|
tags: ['Mint Details'],
|
|
summary: 'Get mint by URL',
|
|
description: 'Returns detailed information about a mint by its URL',
|
|
operationId: 'getMintByUrl',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintUrl' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Mint details',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/Mint' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/{mint_id}/urls': {
|
|
get: {
|
|
tags: ['Mint Details'],
|
|
summary: 'Get mint URLs',
|
|
description: 'Returns all known URLs for a mint (clearnet, Tor, mirrors)',
|
|
operationId: 'getMintUrls',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintId' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Mint URLs',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintUrlsResponse' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/{mint_id}/status': {
|
|
get: {
|
|
tags: ['Mint Details'],
|
|
summary: 'Get mint status',
|
|
description: 'Returns lightweight status information for quick checks',
|
|
operationId: 'getMintStatus',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintId' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Mint status',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintStatus' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/{mint_id}/metadata': {
|
|
get: {
|
|
tags: ['Metadata'],
|
|
summary: 'Get mint metadata',
|
|
description: 'Returns NUT-06 compliant metadata for a mint',
|
|
operationId: 'getMintMetadata',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintId' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Mint metadata',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintMetadata' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/{mint_id}/metadata/history': {
|
|
get: {
|
|
tags: ['Metadata'],
|
|
summary: 'Get metadata history',
|
|
description: 'Returns history of metadata changes for a mint',
|
|
operationId: 'getMintMetadataHistory',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintId' },
|
|
{
|
|
name: 'limit',
|
|
in: 'query',
|
|
description: 'Maximum number of history entries',
|
|
schema: { type: 'integer', default: 50 }
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Metadata history',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MetadataHistoryResponse' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/{mint_id}/uptime': {
|
|
get: {
|
|
tags: ['Uptime'],
|
|
summary: 'Get uptime statistics',
|
|
description: 'Returns uptime and reliability metrics for a mint',
|
|
operationId: 'getMintUptime',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintId' },
|
|
{
|
|
name: 'window',
|
|
in: 'query',
|
|
description: 'Time window for statistics',
|
|
schema: {
|
|
type: 'string',
|
|
enum: ['24h', '7d', '30d'],
|
|
default: '24h'
|
|
}
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Uptime statistics',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/UptimeStats' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/{mint_id}/uptime/timeseries': {
|
|
get: {
|
|
tags: ['Uptime'],
|
|
summary: 'Get uptime timeseries',
|
|
description: 'Returns time-bucketed uptime data for charting',
|
|
operationId: 'getMintUptimeTimeseries',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintId' },
|
|
{
|
|
name: 'window',
|
|
in: 'query',
|
|
description: 'Time window',
|
|
schema: {
|
|
type: 'string',
|
|
enum: ['24h', '7d', '30d'],
|
|
default: '24h'
|
|
}
|
|
},
|
|
{
|
|
name: 'bucket',
|
|
in: 'query',
|
|
description: 'Time bucket size',
|
|
schema: {
|
|
type: 'string',
|
|
enum: ['5m', '15m', '1h'],
|
|
default: '1h'
|
|
}
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Uptime timeseries',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/UptimeTimeseriesResponse' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/{mint_id}/incidents': {
|
|
get: {
|
|
tags: ['Uptime'],
|
|
summary: 'Get incidents',
|
|
description: 'Returns downtime incidents for a mint',
|
|
operationId: 'getMintIncidents',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintId' },
|
|
{
|
|
name: 'limit',
|
|
in: 'query',
|
|
description: 'Maximum number of incidents',
|
|
schema: { type: 'integer', default: 50 }
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Incident list',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/IncidentsResponse' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/{mint_id}/trust': {
|
|
get: {
|
|
tags: ['Trust'],
|
|
summary: 'Get trust score',
|
|
description: 'Returns the computed trust score with full breakdown',
|
|
operationId: 'getMintTrust',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintId' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Trust score',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/TrustScore' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/{mint_id}/reviews': {
|
|
get: {
|
|
tags: ['Reviews'],
|
|
summary: 'Get Nostr reviews',
|
|
description: 'Returns NIP-87 reviews from Nostr relays',
|
|
operationId: 'getMintReviews',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintId' },
|
|
{
|
|
name: 'limit',
|
|
in: 'query',
|
|
description: 'Maximum number of reviews',
|
|
schema: { type: 'integer', default: 50 }
|
|
},
|
|
{
|
|
name: 'offset',
|
|
in: 'query',
|
|
description: 'Number of reviews to skip',
|
|
schema: { type: 'integer', default: 0 }
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Review list',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/ReviewsResponse' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/{mint_id}/views': {
|
|
get: {
|
|
tags: ['Analytics'],
|
|
summary: 'Get pageview statistics',
|
|
description: 'Returns popularity metrics based on pageviews',
|
|
operationId: 'getMintViews',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintId' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Pageview statistics',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/ViewStats' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/{mint_id}/features': {
|
|
get: {
|
|
tags: ['Mint Details'],
|
|
summary: 'Get derived features',
|
|
description: 'Returns features derived from metadata (supported NUTs, Bolt11 support, etc.)',
|
|
operationId: 'getMintFeatures',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintId' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Derived features',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintFeatures' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/by-url/urls': {
|
|
get: {
|
|
tags: ['Mint Details'],
|
|
summary: 'Get mint URLs by URL',
|
|
description: 'Returns all known URLs for a mint identified by URL',
|
|
operationId: 'getMintUrlsByUrl',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintUrl' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Mint URLs',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintUrlsResponse' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/by-url/status': {
|
|
get: {
|
|
tags: ['Mint Details'],
|
|
summary: 'Get mint status by URL',
|
|
description: 'Returns lightweight status information by URL',
|
|
operationId: 'getMintStatusByUrl',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintUrl' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Mint status',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintStatus' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/by-url/metadata': {
|
|
get: {
|
|
tags: ['Metadata'],
|
|
summary: 'Get mint metadata by URL',
|
|
description: 'Returns NUT-06 compliant metadata for a mint by URL',
|
|
operationId: 'getMintMetadataByUrl',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintUrl' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Mint metadata',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintMetadata' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/by-url/metadata/history': {
|
|
get: {
|
|
tags: ['Metadata'],
|
|
summary: 'Get metadata history by URL',
|
|
description: 'Returns history of metadata changes for a mint by URL',
|
|
operationId: 'getMintMetadataHistoryByUrl',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintUrl' },
|
|
{
|
|
name: 'limit',
|
|
in: 'query',
|
|
description: 'Maximum number of history entries',
|
|
schema: { type: 'integer', default: 50 }
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Metadata history',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MetadataHistoryResponse' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/by-url/uptime': {
|
|
get: {
|
|
tags: ['Uptime'],
|
|
summary: 'Get uptime statistics by URL',
|
|
description: 'Returns uptime and reliability metrics for a mint by URL',
|
|
operationId: 'getMintUptimeByUrl',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintUrl' },
|
|
{
|
|
name: 'window',
|
|
in: 'query',
|
|
description: 'Time window for statistics',
|
|
schema: {
|
|
type: 'string',
|
|
enum: ['24h', '7d', '30d'],
|
|
default: '24h'
|
|
}
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Uptime statistics',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/UptimeStats' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/by-url/uptime/timeseries': {
|
|
get: {
|
|
tags: ['Uptime'],
|
|
summary: 'Get uptime timeseries by URL',
|
|
description: 'Returns time-bucketed uptime data for charting by URL',
|
|
operationId: 'getMintUptimeTimeseriesByUrl',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintUrl' },
|
|
{
|
|
name: 'window',
|
|
in: 'query',
|
|
description: 'Time window',
|
|
schema: {
|
|
type: 'string',
|
|
enum: ['24h', '7d', '30d'],
|
|
default: '24h'
|
|
}
|
|
},
|
|
{
|
|
name: 'bucket',
|
|
in: 'query',
|
|
description: 'Time bucket size',
|
|
schema: {
|
|
type: 'string',
|
|
enum: ['5m', '15m', '1h'],
|
|
default: '1h'
|
|
}
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Uptime timeseries',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/UptimeTimeseriesResponse' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/by-url/incidents': {
|
|
get: {
|
|
tags: ['Uptime'],
|
|
summary: 'Get incidents by URL',
|
|
description: 'Returns downtime incidents for a mint by URL',
|
|
operationId: 'getMintIncidentsByUrl',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintUrl' },
|
|
{
|
|
name: 'limit',
|
|
in: 'query',
|
|
description: 'Maximum number of incidents',
|
|
schema: { type: 'integer', default: 50 }
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Incident list',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/IncidentsResponse' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/by-url/trust': {
|
|
get: {
|
|
tags: ['Trust'],
|
|
summary: 'Get trust score by URL',
|
|
description: 'Returns the computed trust score with full breakdown by URL',
|
|
operationId: 'getMintTrustByUrl',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintUrl' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Trust score',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/TrustScore' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/by-url/reviews': {
|
|
get: {
|
|
tags: ['Reviews'],
|
|
summary: 'Get Nostr reviews by URL',
|
|
description: 'Returns NIP-87 reviews from Nostr relays by URL',
|
|
operationId: 'getMintReviewsByUrl',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintUrl' },
|
|
{
|
|
name: 'limit',
|
|
in: 'query',
|
|
description: 'Maximum number of reviews',
|
|
schema: { type: 'integer', default: 50 }
|
|
},
|
|
{
|
|
name: 'offset',
|
|
in: 'query',
|
|
description: 'Number of reviews to skip',
|
|
schema: { type: 'integer', default: 0 }
|
|
}
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Review list',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/ReviewsResponse' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/by-url/views': {
|
|
get: {
|
|
tags: ['Analytics'],
|
|
summary: 'Get pageview statistics by URL',
|
|
description: 'Returns popularity metrics based on pageviews by URL',
|
|
operationId: 'getMintViewsByUrl',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintUrl' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Pageview statistics',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/ViewStats' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/mints/by-url/features': {
|
|
get: {
|
|
tags: ['Mint Details'],
|
|
summary: 'Get derived features by URL',
|
|
description: 'Returns features derived from metadata by URL',
|
|
operationId: 'getMintFeaturesByUrl',
|
|
parameters: [
|
|
{ $ref: '#/components/parameters/mintUrl' }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Derived features',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintFeatures' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
// ==========================================
|
|
// ADMIN ENDPOINTS
|
|
// ==========================================
|
|
'/admin/mints': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Manually add a mint',
|
|
description: 'Add a mint to the system manually. Used for trusted bootstrap or recovery.',
|
|
operationId: 'adminCreateMint',
|
|
security: [{ AdminApiKey: [] }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
required: ['mint_url'],
|
|
properties: {
|
|
mint_url: { type: 'string', format: 'uri' },
|
|
notes: { type: 'string', description: 'Internal notes' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
responses: {
|
|
201: {
|
|
description: 'Mint created',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/AdminMintResponse' }
|
|
}
|
|
}
|
|
},
|
|
200: {
|
|
description: 'Mint already exists',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/AdminMintResponse' }
|
|
}
|
|
}
|
|
},
|
|
401: { $ref: '#/components/responses/Unauthorized' },
|
|
403: { $ref: '#/components/responses/Forbidden' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/{mint_id}/urls': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Add URL to mint',
|
|
description: 'Attach an additional URL (clearnet, Tor, mirror) to an existing mint.',
|
|
operationId: 'adminAddMintUrl',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintId' }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
required: ['url'],
|
|
properties: {
|
|
url: { type: 'string', format: 'uri' },
|
|
type: { type: 'string', enum: ['clearnet', 'tor', 'mirror'] },
|
|
active: { type: 'boolean', default: true }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
responses: {
|
|
201: { description: 'URL added' },
|
|
404: { $ref: '#/components/responses/NotFound' },
|
|
409: { description: 'URL already attached to another mint' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/merge': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Merge two mints',
|
|
description: 'Merge two mints that represent the same operator. All data is reassigned to target.',
|
|
operationId: 'adminMergeMints',
|
|
security: [{ AdminApiKey: [] }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
required: ['source_mint_id', 'target_mint_id'],
|
|
properties: {
|
|
source_mint_id: { type: 'string', format: 'uuid' },
|
|
target_mint_id: { type: 'string', format: 'uuid' },
|
|
reason: { type: 'string' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
responses: {
|
|
200: {
|
|
description: 'Mints merged',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MergeResponse' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/split': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Undo a merge',
|
|
description: 'Revert a previous mint merge operation.',
|
|
operationId: 'adminSplitMints',
|
|
security: [{ AdminApiKey: [] }],
|
|
requestBody: {
|
|
required: true,
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
required: ['merge_id'],
|
|
properties: {
|
|
merge_id: { type: 'string', format: 'uuid' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
responses: {
|
|
200: { description: 'Merge reverted' },
|
|
404: { description: 'Merge not found' },
|
|
409: { description: 'Merge already reverted' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/{mint_id}/disable': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Disable mint',
|
|
description: 'Hide a mint from public listings. Mint continues to be probed.',
|
|
operationId: 'adminDisableMint',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintId' }],
|
|
requestBody: {
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
properties: {
|
|
reason: { type: 'string' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
responses: {
|
|
200: { description: 'Mint disabled' },
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/{mint_id}/enable': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Enable mint',
|
|
description: 'Re-enable a previously hidden mint.',
|
|
operationId: 'adminEnableMint',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintId' }],
|
|
responses: {
|
|
200: { description: 'Mint enabled' },
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/{mint_id}/metadata/refresh': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Force metadata refresh',
|
|
description: 'Force metadata fetch, bypassing the hourly limit.',
|
|
operationId: 'adminForceMetadata',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintId' }],
|
|
responses: {
|
|
200: { description: 'Metadata refresh scheduled' },
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/{mint_id}/trust/recompute': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Force trust recompute',
|
|
description: 'Force trust score recomputation using current data.',
|
|
operationId: 'adminForceTrust',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintId' }],
|
|
responses: {
|
|
200: { description: 'Trust recomputation scheduled' },
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/{mint_id}/status/reset': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Reset mint status',
|
|
description: 'Clear stuck mint state. Resets consecutive failures and schedules probe.',
|
|
operationId: 'adminResetStatus',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintId' }],
|
|
responses: {
|
|
200: { description: 'Status reset, probe scheduled' },
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/jobs': {
|
|
get: {
|
|
tags: ['Admin'],
|
|
summary: 'Inspect job queue',
|
|
description: 'View background job queue status.',
|
|
operationId: 'adminGetJobs',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [
|
|
{ name: 'status', in: 'query', schema: { type: 'string', enum: ['pending', 'running', 'completed', 'failed'] } },
|
|
{ name: 'type', in: 'query', schema: { type: 'string' } },
|
|
{ name: 'limit', in: 'query', schema: { type: 'integer', default: 100 } },
|
|
{ name: 'offset', in: 'query', schema: { type: 'integer', default: 0 } }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Job list',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/JobListResponse' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'/admin/system/metrics': {
|
|
get: {
|
|
tags: ['Admin'],
|
|
summary: 'System metrics',
|
|
description: 'High-level system health and metrics.',
|
|
operationId: 'adminGetMetrics',
|
|
security: [{ AdminApiKey: [] }],
|
|
responses: {
|
|
200: {
|
|
description: 'System metrics',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/SystemMetrics' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'/admin/audit': {
|
|
get: {
|
|
tags: ['Admin'],
|
|
summary: 'Audit log',
|
|
description: 'View admin action audit log.',
|
|
operationId: 'adminGetAudit',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [
|
|
{ name: 'action', in: 'query', schema: { type: 'string' } },
|
|
{ name: 'target_type', in: 'query', schema: { type: 'string' } },
|
|
{ name: 'admin_id', in: 'query', schema: { type: 'string' } },
|
|
{ name: 'mint_id', in: 'query', description: 'Filter by mint ID', schema: { type: 'string', format: 'uuid' } },
|
|
{ name: 'limit', in: 'query', schema: { type: 'integer', default: 100 } },
|
|
{ name: 'offset', in: 'query', schema: { type: 'integer', default: 0 } }
|
|
],
|
|
responses: {
|
|
200: {
|
|
description: 'Audit log entries',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/AuditLogResponse' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/{mint_id}/probe': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Force probe',
|
|
description: 'Force an immediate probe of a mint.',
|
|
operationId: 'adminForceProbeMint',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintId' }],
|
|
responses: {
|
|
200: { description: 'Probe scheduled' },
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/{mint_id}/visibility': {
|
|
get: {
|
|
tags: ['Admin'],
|
|
summary: 'Get mint visibility',
|
|
description: 'Get the current visibility status of a mint.',
|
|
operationId: 'adminGetMintVisibility',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintId' }],
|
|
responses: {
|
|
200: {
|
|
description: 'Visibility status',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintVisibility' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/by-url/disable': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Disable mint by URL',
|
|
description: 'Hide a mint from public listings by URL.',
|
|
operationId: 'adminDisableMintByUrl',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintUrl' }],
|
|
requestBody: {
|
|
content: {
|
|
'application/json': {
|
|
schema: {
|
|
type: 'object',
|
|
properties: { reason: { type: 'string' } }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
responses: {
|
|
200: { description: 'Mint disabled' },
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/by-url/enable': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Enable mint by URL',
|
|
description: 'Re-enable a hidden mint by URL.',
|
|
operationId: 'adminEnableMintByUrl',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintUrl' }],
|
|
responses: {
|
|
200: { description: 'Mint enabled' },
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/by-url/metadata/refresh': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Force metadata refresh by URL',
|
|
description: 'Force metadata fetch by URL, bypassing the hourly limit.',
|
|
operationId: 'adminForceMetadataByUrl',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintUrl' }],
|
|
responses: {
|
|
200: { description: 'Metadata refresh scheduled' },
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/by-url/trust/recompute': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Force trust recompute by URL',
|
|
description: 'Force trust score recomputation by URL.',
|
|
operationId: 'adminForceTrustByUrl',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintUrl' }],
|
|
responses: {
|
|
200: { description: 'Trust recomputation scheduled' },
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/by-url/status/reset': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Reset mint status by URL',
|
|
description: 'Clear stuck mint state by URL.',
|
|
operationId: 'adminResetStatusByUrl',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintUrl' }],
|
|
responses: {
|
|
200: { description: 'Status reset, probe scheduled' },
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/by-url/probe': {
|
|
post: {
|
|
tags: ['Admin'],
|
|
summary: 'Force probe by URL',
|
|
description: 'Force an immediate probe by URL.',
|
|
operationId: 'adminForceProbeByUrl',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintUrl' }],
|
|
responses: {
|
|
200: { description: 'Probe scheduled' },
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
},
|
|
'/admin/mints/by-url/visibility': {
|
|
get: {
|
|
tags: ['Admin'],
|
|
summary: 'Get mint visibility by URL',
|
|
description: 'Get visibility status by URL.',
|
|
operationId: 'adminGetMintVisibilityByUrl',
|
|
security: [{ AdminApiKey: [] }],
|
|
parameters: [{ $ref: '#/components/parameters/mintUrl' }],
|
|
responses: {
|
|
200: {
|
|
description: 'Visibility status',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/MintVisibility' }
|
|
}
|
|
}
|
|
},
|
|
404: { $ref: '#/components/responses/NotFound' }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
components: {
|
|
securitySchemes: {
|
|
AdminApiKey: {
|
|
type: 'apiKey',
|
|
in: 'header',
|
|
name: 'X-Admin-Api-Key',
|
|
description: 'Admin API key for protected endpoints'
|
|
}
|
|
},
|
|
parameters: {
|
|
mintId: {
|
|
name: 'mint_id',
|
|
in: 'path',
|
|
required: true,
|
|
description: 'Unique mint identifier (UUID)',
|
|
schema: {
|
|
type: 'string',
|
|
format: 'uuid'
|
|
}
|
|
},
|
|
mintUrl: {
|
|
name: 'url',
|
|
in: 'query',
|
|
required: true,
|
|
description: 'Mint URL (will be normalized)',
|
|
schema: {
|
|
type: 'string',
|
|
format: 'uri'
|
|
}
|
|
}
|
|
},
|
|
responses: {
|
|
NotFound: {
|
|
description: 'Mint not found',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/Error' }
|
|
}
|
|
}
|
|
},
|
|
RateLimited: {
|
|
description: 'Rate limit exceeded',
|
|
headers: {
|
|
'Retry-After': {
|
|
description: 'Seconds until rate limit resets',
|
|
schema: { type: 'integer' }
|
|
},
|
|
'X-RateLimit-Limit': {
|
|
description: 'Maximum requests per window',
|
|
schema: { type: 'integer' }
|
|
},
|
|
'X-RateLimit-Remaining': {
|
|
description: 'Remaining requests in window',
|
|
schema: { type: 'integer' }
|
|
}
|
|
},
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/Error' }
|
|
}
|
|
}
|
|
},
|
|
Unauthorized: {
|
|
description: 'Missing authentication',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/Error' }
|
|
}
|
|
}
|
|
},
|
|
Forbidden: {
|
|
description: 'Invalid authentication',
|
|
content: {
|
|
'application/json': {
|
|
schema: { $ref: '#/components/schemas/Error' }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
schemas: {
|
|
Error: {
|
|
type: 'object',
|
|
properties: {
|
|
error: { type: 'string', description: 'Error message' }
|
|
},
|
|
required: ['error']
|
|
},
|
|
HealthResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
status: {
|
|
type: 'string',
|
|
enum: ['healthy', 'unhealthy']
|
|
},
|
|
database: {
|
|
type: 'string',
|
|
enum: ['connected', 'disconnected']
|
|
},
|
|
timestamp: {
|
|
type: 'string',
|
|
format: 'date-time'
|
|
},
|
|
version: { type: 'string' }
|
|
}
|
|
},
|
|
StatsResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
mints: {
|
|
type: 'object',
|
|
properties: {
|
|
total: { type: 'integer' },
|
|
by_status: {
|
|
type: 'object',
|
|
additionalProperties: { type: 'integer' }
|
|
}
|
|
}
|
|
},
|
|
activity: {
|
|
type: 'object',
|
|
properties: {
|
|
probes_24h: { type: 'integer' },
|
|
reviews_total: { type: 'integer' },
|
|
incidents_7d: { type: 'integer' }
|
|
}
|
|
},
|
|
computed_at: { type: 'string', format: 'date-time' }
|
|
}
|
|
},
|
|
Mint: {
|
|
type: 'object',
|
|
properties: {
|
|
mint_id: {
|
|
type: 'string',
|
|
format: 'uuid',
|
|
description: 'Unique mint identifier'
|
|
},
|
|
canonical_url: {
|
|
type: 'string',
|
|
format: 'uri',
|
|
description: 'Primary mint URL'
|
|
},
|
|
urls: {
|
|
type: 'array',
|
|
items: { type: 'string', format: 'uri' },
|
|
description: 'All known URLs for this mint'
|
|
},
|
|
name: {
|
|
type: 'string',
|
|
nullable: true,
|
|
description: 'Mint name from metadata'
|
|
},
|
|
icon_url: {
|
|
type: 'string',
|
|
format: 'uri',
|
|
nullable: true,
|
|
description: 'Mint icon URL'
|
|
},
|
|
status: {
|
|
type: 'string',
|
|
enum: ['unknown', 'online', 'degraded', 'offline', 'abandoned'],
|
|
description: 'Current mint status'
|
|
},
|
|
offline_since: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
nullable: true,
|
|
description: 'When the mint went offline'
|
|
},
|
|
last_success_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
nullable: true,
|
|
description: 'Last successful probe'
|
|
},
|
|
last_failure_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
nullable: true,
|
|
description: 'Last failed probe'
|
|
},
|
|
uptime_24h: {
|
|
type: 'number',
|
|
nullable: true,
|
|
description: '24-hour uptime percentage'
|
|
},
|
|
uptime_7d: {
|
|
type: 'number',
|
|
nullable: true,
|
|
description: '7-day uptime percentage'
|
|
},
|
|
uptime_30d: {
|
|
type: 'number',
|
|
nullable: true,
|
|
description: '30-day uptime percentage'
|
|
},
|
|
incidents_7d: {
|
|
type: 'integer',
|
|
description: 'Incident count in last 7 days'
|
|
},
|
|
incidents_30d: {
|
|
type: 'integer',
|
|
description: 'Incident count in last 30 days'
|
|
},
|
|
trust_score: {
|
|
type: 'integer',
|
|
nullable: true,
|
|
minimum: 0,
|
|
maximum: 100,
|
|
description: 'Trust score (0-100)'
|
|
},
|
|
trust_level: {
|
|
type: 'string',
|
|
enum: ['unknown', 'low', 'medium', 'high', 'excellent'],
|
|
nullable: true,
|
|
description: 'Trust level category'
|
|
}
|
|
}
|
|
},
|
|
MintListResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
mints: {
|
|
type: 'array',
|
|
items: { $ref: '#/components/schemas/Mint' }
|
|
},
|
|
total: { type: 'integer' },
|
|
limit: { type: 'integer' },
|
|
offset: { type: 'integer' }
|
|
}
|
|
},
|
|
MintSubmission: {
|
|
type: 'object',
|
|
required: ['mint_url'],
|
|
properties: {
|
|
mint_url: {
|
|
type: 'string',
|
|
format: 'uri',
|
|
description: 'URL of the Cashu mint to submit'
|
|
}
|
|
}
|
|
},
|
|
MintSubmissionResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
success: { type: 'boolean' },
|
|
mint_id: { type: 'string', format: 'uuid' },
|
|
status: { type: 'string' },
|
|
message: { type: 'string' }
|
|
}
|
|
},
|
|
MintUrlsResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
canonical_url: { type: 'string', format: 'uri' },
|
|
urls: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
url: { type: 'string', format: 'uri' },
|
|
type: {
|
|
type: 'string',
|
|
enum: ['clearnet', 'tor', 'mirror']
|
|
},
|
|
active: { type: 'boolean' },
|
|
discovered_at: { type: 'string', format: 'date-time' },
|
|
last_seen_at: { type: 'string', format: 'date-time' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
MintStatus: {
|
|
type: 'object',
|
|
properties: {
|
|
status: {
|
|
type: 'string',
|
|
enum: ['unknown', 'online', 'degraded', 'offline', 'abandoned']
|
|
},
|
|
offline_since: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
nullable: true
|
|
},
|
|
last_checked_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
nullable: true
|
|
},
|
|
current_rtt_ms: {
|
|
type: 'integer',
|
|
nullable: true,
|
|
description: 'Current response time in milliseconds'
|
|
}
|
|
}
|
|
},
|
|
MintMetadata: {
|
|
type: 'object',
|
|
description: 'NUT-06 compliant mint metadata',
|
|
properties: {
|
|
name: { type: 'string', nullable: true },
|
|
pubkey: {
|
|
type: 'string',
|
|
nullable: true,
|
|
description: 'Mint public key (hex)'
|
|
},
|
|
version: { type: 'string', nullable: true },
|
|
description: { type: 'string', nullable: true },
|
|
description_long: { type: 'string', nullable: true },
|
|
contact: {
|
|
type: 'object',
|
|
nullable: true,
|
|
additionalProperties: { type: 'string' }
|
|
},
|
|
motd: {
|
|
type: 'string',
|
|
nullable: true,
|
|
description: 'Message of the day'
|
|
},
|
|
icon_url: { type: 'string', format: 'uri', nullable: true },
|
|
urls: {
|
|
type: 'array',
|
|
nullable: true,
|
|
items: { type: 'string', format: 'uri' }
|
|
},
|
|
tos_url: {
|
|
type: 'string',
|
|
format: 'uri',
|
|
nullable: true,
|
|
description: 'Terms of service URL'
|
|
},
|
|
nuts: {
|
|
type: 'object',
|
|
nullable: true,
|
|
description: 'Supported NUTs configuration',
|
|
additionalProperties: { type: 'object' }
|
|
},
|
|
server_time: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
nullable: true
|
|
},
|
|
last_fetched_at: { type: 'string', format: 'date-time' }
|
|
}
|
|
},
|
|
MetadataHistoryResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
history: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
fetched_at: { type: 'string', format: 'date-time' },
|
|
change_type: {
|
|
type: 'string',
|
|
enum: ['initial', 'update', 'error']
|
|
},
|
|
diff: {
|
|
type: 'object',
|
|
nullable: true,
|
|
description: 'JSON diff of changes'
|
|
},
|
|
version: { type: 'string', nullable: true }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
UptimeStats: {
|
|
type: 'object',
|
|
properties: {
|
|
uptime_pct: {
|
|
type: 'number',
|
|
description: 'Uptime percentage'
|
|
},
|
|
downtime_seconds: {
|
|
type: 'integer',
|
|
description: 'Total downtime in seconds'
|
|
},
|
|
avg_rtt_ms: {
|
|
type: 'number',
|
|
nullable: true,
|
|
description: 'Average response time'
|
|
},
|
|
p95_rtt_ms: {
|
|
type: 'number',
|
|
nullable: true,
|
|
description: '95th percentile response time'
|
|
},
|
|
total_checks: { type: 'integer' },
|
|
ok_checks: { type: 'integer' }
|
|
}
|
|
},
|
|
UptimeTimeseriesResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
data: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
timestamp: { type: 'string', format: 'date-time' },
|
|
state: {
|
|
type: 'string',
|
|
enum: ['up', 'down', 'degraded']
|
|
},
|
|
ok: { type: 'integer' },
|
|
total: { type: 'integer' },
|
|
rtt_ms: { type: 'integer', nullable: true }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
IncidentsResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
incidents: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
started_at: { type: 'string', format: 'date-time' },
|
|
resolved_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
nullable: true
|
|
},
|
|
duration_seconds: { type: 'integer', nullable: true },
|
|
severity: {
|
|
type: 'string',
|
|
enum: ['minor', 'major', 'critical']
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
TrustScore: {
|
|
type: 'object',
|
|
properties: {
|
|
score_total: {
|
|
type: 'integer',
|
|
minimum: 0,
|
|
maximum: 100,
|
|
nullable: true
|
|
},
|
|
score_level: {
|
|
type: 'string',
|
|
enum: ['unknown', 'low', 'medium', 'high', 'excellent']
|
|
},
|
|
breakdown: {
|
|
type: 'object',
|
|
nullable: true,
|
|
description: 'Detailed score breakdown by component',
|
|
properties: {
|
|
uptime: {
|
|
type: 'object',
|
|
properties: {
|
|
score: { type: 'number' },
|
|
max: { type: 'number' },
|
|
details: { type: 'object' }
|
|
}
|
|
},
|
|
speed: {
|
|
type: 'object',
|
|
properties: {
|
|
score: { type: 'number' },
|
|
max: { type: 'number' },
|
|
details: { type: 'object' }
|
|
}
|
|
},
|
|
reviews: {
|
|
type: 'object',
|
|
properties: {
|
|
score: { type: 'number' },
|
|
max: { type: 'number' },
|
|
details: { type: 'object' }
|
|
}
|
|
},
|
|
identity: {
|
|
type: 'object',
|
|
properties: {
|
|
score: { type: 'number' },
|
|
max: { type: 'number' },
|
|
details: { type: 'object' }
|
|
}
|
|
},
|
|
penalties: {
|
|
type: 'object',
|
|
properties: {
|
|
score: { type: 'number' },
|
|
max: { type: 'number' },
|
|
details: { type: 'object' }
|
|
}
|
|
}
|
|
}
|
|
},
|
|
computed_at: {
|
|
type: 'string',
|
|
format: 'date-time',
|
|
nullable: true
|
|
}
|
|
}
|
|
},
|
|
ReviewsResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
reviews: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
event_id: {
|
|
type: 'string',
|
|
description: 'Nostr event ID'
|
|
},
|
|
pubkey: {
|
|
type: 'string',
|
|
description: 'Reviewer Nostr pubkey'
|
|
},
|
|
created_at: { type: 'string', format: 'date-time' },
|
|
rating: {
|
|
type: 'integer',
|
|
minimum: 1,
|
|
maximum: 5,
|
|
nullable: true
|
|
},
|
|
content: {
|
|
type: 'string',
|
|
nullable: true,
|
|
description: 'Review text'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
ViewStats: {
|
|
type: 'object',
|
|
properties: {
|
|
views_24h: { type: 'integer' },
|
|
views_7d: { type: 'integer' },
|
|
views_30d: { type: 'integer' },
|
|
unique_sessions_30d: { type: 'integer' },
|
|
view_velocity: {
|
|
type: 'number',
|
|
description: 'Average views per day'
|
|
}
|
|
}
|
|
},
|
|
MintFeatures: {
|
|
type: 'object',
|
|
properties: {
|
|
supported_nuts: {
|
|
type: 'array',
|
|
items: { type: 'integer' },
|
|
description: 'List of supported NUT numbers'
|
|
},
|
|
supports_bolt11: {
|
|
type: 'boolean',
|
|
description: 'Whether Bolt11 Lightning is supported'
|
|
},
|
|
min_amount: {
|
|
type: 'integer',
|
|
nullable: true,
|
|
description: 'Minimum transaction amount in sats'
|
|
},
|
|
max_amount: {
|
|
type: 'integer',
|
|
nullable: true,
|
|
description: 'Maximum transaction amount in sats'
|
|
},
|
|
has_tor_endpoint: {
|
|
type: 'boolean',
|
|
description: 'Whether a .onion URL is available'
|
|
},
|
|
has_multiple_urls: {
|
|
type: 'boolean',
|
|
description: 'Whether multiple URLs are configured'
|
|
},
|
|
feature_completeness_score: {
|
|
type: 'integer',
|
|
minimum: 0,
|
|
maximum: 100,
|
|
description: 'Overall feature completeness (0-100)'
|
|
}
|
|
}
|
|
},
|
|
AdminMintResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
mint_id: { type: 'string', format: 'uuid' },
|
|
status: { type: 'string' },
|
|
message: { type: 'string' },
|
|
created: { type: 'boolean' }
|
|
}
|
|
},
|
|
MergeResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
merge_id: { type: 'string', format: 'uuid' },
|
|
source_mint_id: { type: 'string', format: 'uuid' },
|
|
target_mint_id: { type: 'string', format: 'uuid' },
|
|
message: { type: 'string' }
|
|
}
|
|
},
|
|
JobListResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
jobs: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
id: { type: 'integer' },
|
|
type: { type: 'string' },
|
|
status: { type: 'string', enum: ['pending', 'running', 'completed', 'failed'] },
|
|
payload: { type: 'object' },
|
|
run_at: { type: 'string', format: 'date-time' },
|
|
retries: { type: 'integer' },
|
|
error_message: { type: 'string', nullable: true }
|
|
}
|
|
}
|
|
},
|
|
total: { type: 'integer' },
|
|
limit: { type: 'integer' },
|
|
offset: { type: 'integer' }
|
|
}
|
|
},
|
|
SystemMetrics: {
|
|
type: 'object',
|
|
properties: {
|
|
total_mints: { type: 'integer' },
|
|
online_mints: { type: 'integer' },
|
|
degraded_mints: { type: 'integer' },
|
|
offline_mints: { type: 'integer' },
|
|
abandoned_mints: { type: 'integer' },
|
|
unknown_mints: { type: 'integer' },
|
|
probes_last_minute: { type: 'integer' },
|
|
failed_probes_last_minute: { type: 'integer' },
|
|
job_backlog: { type: 'integer' },
|
|
oldest_job_age_seconds: { type: 'integer' },
|
|
worker_heartbeat_seconds: { type: 'integer', nullable: true },
|
|
computed_at: { type: 'string', format: 'date-time' }
|
|
}
|
|
},
|
|
AuditLogResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
entries: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
id: { type: 'integer' },
|
|
admin_id: { type: 'string' },
|
|
action: { type: 'string' },
|
|
target_type: { type: 'string' },
|
|
target_id: { type: 'string', nullable: true },
|
|
before_state: { type: 'object', nullable: true },
|
|
after_state: { type: 'object', nullable: true },
|
|
notes: { type: 'string', nullable: true },
|
|
ip_address: { type: 'string', nullable: true },
|
|
created_at: { type: 'string', format: 'date-time' }
|
|
}
|
|
}
|
|
},
|
|
total: { type: 'integer' },
|
|
limit: { type: 'integer' },
|
|
offset: { type: 'integer' }
|
|
}
|
|
},
|
|
GlobalReviewsResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
reviews: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
event_id: { type: 'string' },
|
|
mint_id: { type: 'string', format: 'uuid', nullable: true },
|
|
mint_name: { type: 'string', nullable: true },
|
|
mint_url: { type: 'string', nullable: true },
|
|
pubkey: { type: 'string' },
|
|
created_at: { type: 'string', format: 'date-time' },
|
|
rating: { type: 'integer', minimum: 1, maximum: 5, nullable: true },
|
|
content: { type: 'string', nullable: true }
|
|
}
|
|
}
|
|
},
|
|
total: { type: 'integer' },
|
|
limit: { type: 'integer' },
|
|
offset: { type: 'integer' }
|
|
}
|
|
},
|
|
TrendingMintsResponse: {
|
|
type: 'object',
|
|
properties: {
|
|
window: { type: 'string', enum: ['24h', '7d'] },
|
|
mints: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
mint_id: { type: 'string', format: 'uuid' },
|
|
canonical_url: { type: 'string' },
|
|
name: { type: 'string', nullable: true },
|
|
icon_url: { type: 'string', nullable: true },
|
|
status: { type: 'string' },
|
|
trust_score: { type: 'integer', nullable: true },
|
|
trust_level: { type: 'string', nullable: true },
|
|
view_count: { type: 'integer' },
|
|
view_velocity: { type: 'number' },
|
|
unique_sessions: { type: 'integer' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
UptimeAnalytics: {
|
|
type: 'object',
|
|
properties: {
|
|
window: { type: 'string' },
|
|
overall: {
|
|
type: 'object',
|
|
properties: {
|
|
total_probes: { type: 'integer' },
|
|
successful_probes: { type: 'integer' },
|
|
uptime_percent: { type: 'number', nullable: true },
|
|
avg_rtt_ms: { type: 'number', nullable: true },
|
|
min_rtt_ms: { type: 'number', nullable: true },
|
|
max_rtt_ms: { type: 'number', nullable: true }
|
|
}
|
|
},
|
|
by_status: {
|
|
type: 'object',
|
|
additionalProperties: {
|
|
type: 'object',
|
|
properties: {
|
|
mint_count: { type: 'integer' },
|
|
probe_count: { type: 'integer' },
|
|
successful_probes: { type: 'integer' }
|
|
}
|
|
}
|
|
},
|
|
rtt_distribution: {
|
|
type: 'object',
|
|
properties: {
|
|
fast: { type: 'integer', description: '< 500ms' },
|
|
normal: { type: 'integer', description: '500-1000ms' },
|
|
slow: { type: 'integer', description: '1000-2000ms' },
|
|
degraded: { type: 'integer', description: '> 2000ms' }
|
|
}
|
|
},
|
|
computed_at: { type: 'string', format: 'date-time' }
|
|
}
|
|
},
|
|
VersionAnalytics: {
|
|
type: 'object',
|
|
properties: {
|
|
versions: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
version: { type: 'string' },
|
|
count: { type: 'integer' },
|
|
mints: { type: 'array', items: { type: 'string' } }
|
|
}
|
|
}
|
|
},
|
|
total_with_version: { type: 'integer' },
|
|
total_mints: { type: 'integer' },
|
|
coverage_percent: { type: 'integer' },
|
|
computed_at: { type: 'string', format: 'date-time' }
|
|
}
|
|
},
|
|
NutsAnalytics: {
|
|
type: 'object',
|
|
properties: {
|
|
nuts: {
|
|
type: 'array',
|
|
items: {
|
|
type: 'object',
|
|
properties: {
|
|
nut: { type: 'integer' },
|
|
count: { type: 'integer' },
|
|
percent: { type: 'integer' }
|
|
}
|
|
}
|
|
},
|
|
key_nuts: {
|
|
type: 'object',
|
|
additionalProperties: { type: 'integer' }
|
|
},
|
|
total_mints_analyzed: { type: 'integer' },
|
|
computed_at: { type: 'string', format: 'date-time' }
|
|
}
|
|
},
|
|
MintVisibility: {
|
|
type: 'object',
|
|
properties: {
|
|
mint_id: { type: 'string', format: 'uuid' },
|
|
canonical_url: { type: 'string' },
|
|
visibility: { type: 'string', enum: ['public', 'hidden'] },
|
|
status: { type: 'string' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}; |