{
  "service": "file-hub",
  "baseUrl": "https://api.filehub.foreachsoftware.it",
  "auth": "Bearer <api-key> in Authorization header",
  "endpoints": [
    {
      "method": "GET",
      "path": "/api/v1/buckets",
      "description": "List buckets",
      "params": "page, pageSize, search, sort, projectId, projectFilter"
    },
    {
      "method": "POST",
      "path": "/api/v1/buckets",
      "description": "Create bucket (admin)",
      "body": "slug, name, allowedMimeTypes, defaultVisibility, maxFileSizeMb, projectId?"
    },
    {
      "method": "GET",
      "path": "/api/v1/projects",
      "description": "List projects in tenant",
      "params": "page, pageSize, search, sort"
    },
    {
      "method": "POST",
      "path": "/api/v1/projects",
      "description": "Create project (admin)",
      "body": "name, slug?, description?, color?"
    },
    {
      "method": "GET",
      "path": "/api/v1/projects/:id",
      "description": "Get project by id or slug"
    },
    {
      "method": "PATCH",
      "path": "/api/v1/projects/:id",
      "description": "Update project (admin)"
    },
    {
      "method": "DELETE",
      "path": "/api/v1/projects/:id",
      "description": "Delete project (admin). Buckets are preserved with projectId=null."
    },
    {
      "method": "GET",
      "path": "/api/v1/buckets/:id",
      "description": "Get bucket by ID or slug"
    },
    {
      "method": "PUT",
      "path": "/api/v1/buckets/:id",
      "description": "Update bucket (admin)"
    },
    {
      "method": "DELETE",
      "path": "/api/v1/buckets/:id",
      "description": "Delete bucket (admin, must be empty)"
    },
    {
      "method": "GET",
      "path": "/api/v1/files",
      "description": "List files",
      "params": "page, pageSize, bucketSlug, visibility, mimeType, search, tags"
    },
    {
      "method": "GET",
      "path": "/api/v1/files/:id",
      "description": "Get file details + variants"
    },
    {
      "method": "PUT",
      "path": "/api/v1/files/:id",
      "description": "Update file metadata (write)"
    },
    {
      "method": "DELETE",
      "path": "/api/v1/files/:id",
      "description": "Soft delete (?hard=true for permanent)"
    },
    {
      "method": "GET",
      "path": "/api/v1/files/:id/download",
      "description": "Download file binary",
      "params": "variant (THUMBNAIL|SMALL|MEDIUM|LARGE)"
    },
    {
      "method": "GET",
      "path": "/api/v1/files/:id/serve",
      "description": "Serve file (public or signed URL)",
      "params": "expires, sig"
    },
    {
      "method": "GET",
      "path": "/api/v1/files/search",
      "description": "Full-text search",
      "params": "q, page, pageSize, bucketSlug"
    },
    {
      "method": "GET",
      "path": "/api/v1/files/by-context",
      "description": "Query by context",
      "params": "bucketSlug, contextKey, contextValue"
    },
    {
      "method": "POST",
      "path": "/api/v1/upload",
      "description": "Upload file (multipart)",
      "body": "file, bucketSlug, visibility?, context?, tags?, category?"
    },
    {
      "method": "POST",
      "path": "/api/v1/batch-upload",
      "description": "Batch upload (max 10)",
      "body": "files[], bucketSlug, visibility?"
    },
    {
      "method": "POST",
      "path": "/api/v1/signed-url",
      "description": "Generate signed URL",
      "body": "fileId, expiryMinutes?"
    },
    {
      "method": "GET",
      "path": "/api/v1/api-keys",
      "description": "List API keys (admin)"
    },
    {
      "method": "POST",
      "path": "/api/v1/api-keys",
      "description": "Create API key (admin). Returns key ONCE."
    },
    {
      "method": "GET",
      "path": "/api/v1/api-keys/:id",
      "description": "Get API key detail (admin)"
    },
    {
      "method": "PUT",
      "path": "/api/v1/api-keys/:id",
      "description": "Update API key (admin)"
    },
    {
      "method": "DELETE",
      "path": "/api/v1/api-keys/:id",
      "description": "Revoke API key (admin)"
    },
    {
      "method": "GET",
      "path": "/api/v1/audit-log",
      "description": "Query audit log (admin)",
      "params": "page, action, entity, from, to"
    },
    {
      "method": "GET",
      "path": "/api/v1/webhooks",
      "description": "List webhooks (admin)",
      "params": "page, pageSize"
    },
    {
      "method": "POST",
      "path": "/api/v1/webhooks",
      "description": "Register webhook (admin). Returns signing secret ONCE.",
      "body": "url, events[], description?"
    },
    {
      "method": "GET",
      "path": "/api/v1/webhooks/:id",
      "description": "Get webhook details (admin)"
    },
    {
      "method": "PUT",
      "path": "/api/v1/webhooks/:id",
      "description": "Update webhook (admin)",
      "body": "url?, events[]?, description?, isActive?"
    },
    {
      "method": "DELETE",
      "path": "/api/v1/webhooks/:id",
      "description": "Delete webhook (admin)"
    },
    {
      "method": "GET",
      "path": "/api/v1/webhooks/:id/deliveries",
      "description": "List webhook deliveries",
      "params": "page, pageSize, success, event"
    },
    {
      "method": "POST",
      "path": "/api/v1/webhooks/:id/test",
      "description": "Send test event to webhook (admin)"
    },
    {
      "method": "GET",
      "path": "/health/live",
      "description": "Liveness probe (no auth)"
    },
    {
      "method": "GET",
      "path": "/health/ready",
      "description": "Readiness probe (no auth)"
    }
  ]
}