{
  "openapi": "3.0.0",
  "info": {
    "title": "bs-rhb",
    "version": "0.2.1",
    "description": "BS-RHB is a centralized notification hub managing cross-application delivery via multiple channels (email, push, chat, etc.)",
    "contact": {
      "name": "Jiri Beck",
      "email": "jb@jiribeck.com"
    }
  },
  "paths": {
    "/admin/apps/{id}/regenerate-key": {
      "post": {
        "x-controller-name": "AdminAppsController",
        "x-operation-name": "regenerateKey",
        "tags": [
          "AdminAppsController"
        ],
        "summary": "Regenerate an application secret",
        "description": "Issues a new appKey, invalidating the previous one. The new key is returned only once, in this response.",
        "responses": {
          "200": {
            "description": "The id and the new appKey",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RegeneratedKey"
                }
              }
            }
          },
          "404": {
            "description": "Application not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "operationId": "AdminAppsController.regenerateKey"
      }
    },
    "/admin/apps/{id}": {
      "patch": {
        "x-controller-name": "AdminAppsController",
        "x-operation-name": "update",
        "tags": [
          "AdminAppsController"
        ],
        "summary": "Update an application name",
        "responses": {
          "200": {
            "description": "The updated application",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppSummary"
                }
              }
            }
          },
          "404": {
            "description": "Application not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              }
            }
          },
          "description": "Fields to update (appId and appKey cannot be changed here)",
          "required": true,
          "x-parameter-index": 1
        },
        "operationId": "AdminAppsController.update"
      },
      "get": {
        "x-controller-name": "AdminAppsController",
        "x-operation-name": "findById",
        "tags": [
          "AdminAppsController"
        ],
        "summary": "Get a single application (without its secret)",
        "responses": {
          "200": {
            "description": "The application",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppSummary"
                }
              }
            }
          },
          "404": {
            "description": "Application not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "operationId": "AdminAppsController.findById"
      },
      "delete": {
        "x-controller-name": "AdminAppsController",
        "x-operation-name": "delete",
        "tags": [
          "AdminAppsController"
        ],
        "summary": "Delete an application",
        "responses": {
          "204": {
            "description": "Application deleted"
          },
          "404": {
            "description": "Application not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "operationId": "AdminAppsController.delete"
      }
    },
    "/admin/apps": {
      "post": {
        "x-controller-name": "AdminAppsController",
        "x-operation-name": "create",
        "tags": [
          "AdminAppsController"
        ],
        "summary": "Register a new application",
        "description": "Creates an application and generates its secret. The `appKey` is returned only once, in this response.",
        "responses": {
          "200": {
            "description": "The created application including its appKey",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AppWithKey"
                }
              }
            }
          },
          "409": {
            "description": "An application with this appId already exists"
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "appId"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "appId": {
                    "type": "string",
                    "minLength": 1,
                    "pattern": "^[A-Za-z0-9_-]+$"
                  }
                }
              },
              "example": {
                "name": "Canvas",
                "appId": "canvas"
              }
            }
          },
          "description": "New application to register",
          "required": true
        },
        "operationId": "AdminAppsController.create"
      },
      "get": {
        "x-controller-name": "AdminAppsController",
        "x-operation-name": "list",
        "tags": [
          "AdminAppsController"
        ],
        "summary": "List all registered applications (without secrets)",
        "responses": {
          "200": {
            "description": "Array of applications",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AppSummary"
                  }
                }
              }
            }
          }
        },
        "operationId": "AdminAppsController.list"
      }
    },
    "/admin/dashboard": {
      "get": {
        "x-controller-name": "AdminDashboardController",
        "x-operation-name": "stats",
        "tags": [
          "AdminDashboardController"
        ],
        "summary": "Aggregate counts for the admin dashboard",
        "responses": {
          "200": {
            "description": "Dashboard statistics",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DashboardStats"
                }
              }
            }
          }
        },
        "operationId": "AdminDashboardController.stats"
      }
    },
    "/admin/languages": {
      "get": {
        "x-controller-name": "AdminSettingsController",
        "x-operation-name": "languages",
        "tags": [
          "AdminSettingsController"
        ],
        "summary": "Supported languages for sender names (admin-authenticated copy)",
        "responses": {
          "200": {
            "description": "Supported languages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "code": {
                        "type": "string"
                      },
                      "label": {
                        "type": "string"
                      },
                      "countryCode": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "AdminSettingsController.languages"
      }
    },
    "/admin/login": {
      "post": {
        "x-controller-name": "AdminAuthController",
        "x-operation-name": "login",
        "tags": [
          "AdminAuthController"
        ],
        "summary": "Authenticate an administrator and obtain a JWT",
        "responses": {
          "200": {
            "description": "JWT and the authenticated admin profile",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": {
                      "type": "string"
                    },
                    "admin": {
                      "type": "object",
                      "properties": {
                        "id": {
                          "type": "string"
                        },
                        "email": {
                          "type": "string"
                        },
                        "name": {
                          "type": "string"
                        },
                        "role": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          },
          "401": {
            "description": "Invalid email or password"
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "minLength": 1
                  },
                  "password": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              },
              "example": {
                "email": "admin",
                "password": "admin"
              }
            }
          },
          "description": "Admin email/username and password",
          "required": true
        },
        "operationId": "AdminAuthController.login"
      }
    },
    "/admin/logs/{id}": {
      "get": {
        "x-controller-name": "AdminLogsController",
        "x-operation-name": "findById",
        "tags": [
          "AdminLogsController"
        ],
        "summary": "Get a single mail log entry including its body",
        "responses": {
          "200": {
            "description": "The log entry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LogMail"
                }
              }
            }
          },
          "404": {
            "description": "Log entry not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "number"
            },
            "required": true
          }
        ],
        "operationId": "AdminLogsController.findById"
      }
    },
    "/admin/logs": {
      "get": {
        "x-controller-name": "AdminLogsController",
        "x-operation-name": "list",
        "tags": [
          "AdminLogsController"
        ],
        "summary": "Cross-application mail log with filters and pagination",
        "description": "Returns sent/failed mail entries across all applications. The list omits the text/html bodies; use GET /admin/logs/{id} for a full record.",
        "responses": {
          "200": {
            "description": "Paginated log entries",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LogMailGetResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "appId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "provider",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "to",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateFrom",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "dateTo",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "number"
            }
          }
        ],
        "operationId": "AdminLogsController.list"
      }
    },
    "/admin/me": {
      "get": {
        "x-controller-name": "AdminAuthController",
        "x-operation-name": "me",
        "tags": [
          "AdminAuthController"
        ],
        "summary": "Return the currently authenticated administrator",
        "responses": {
          "200": {
            "description": "The authenticated admin profile",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminProfile"
                }
              }
            }
          },
          "401": {
            "description": "Missing or invalid token"
          }
        },
        "operationId": "AdminAuthController.me"
      }
    },
    "/admin/queue/{id}/retry": {
      "post": {
        "x-controller-name": "AdminQueueController",
        "x-operation-name": "retry",
        "tags": [
          "AdminQueueController"
        ],
        "summary": "Requeue a failed job for delivery",
        "description": "Resets a failed job to pending with a fresh attempt budget so the worker picks it up again.",
        "responses": {
          "200": {
            "description": "The requeued job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailJob"
                }
              }
            }
          },
          "404": {
            "description": "Job not found"
          },
          "409": {
            "description": "Only failed jobs can be retried"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "operationId": "AdminQueueController.retry"
      }
    },
    "/admin/queue/{id}": {
      "get": {
        "x-controller-name": "AdminQueueController",
        "x-operation-name": "findById",
        "tags": [
          "AdminQueueController"
        ],
        "summary": "Get a single queue job",
        "responses": {
          "200": {
            "description": "The job",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailJob"
                }
              }
            }
          },
          "404": {
            "description": "Job not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "operationId": "AdminQueueController.findById"
      },
      "delete": {
        "x-controller-name": "AdminQueueController",
        "x-operation-name": "delete",
        "tags": [
          "AdminQueueController"
        ],
        "summary": "Delete a single queue job",
        "responses": {
          "204": {
            "description": "Job deleted"
          },
          "404": {
            "description": "Job not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "operationId": "AdminQueueController.delete"
      }
    },
    "/admin/queue": {
      "get": {
        "x-controller-name": "AdminQueueController",
        "x-operation-name": "list",
        "tags": [
          "AdminQueueController"
        ],
        "summary": "List mail-queue jobs (filterable, paginated)",
        "description": "Returns queued jobs (one per recipient). The shared message content lives in the referenced MailBatch, not on the job.",
        "responses": {
          "200": {
            "description": "Paginated jobs and total count",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/MailJobList"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "appId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "number"
            }
          }
        ],
        "operationId": "AdminQueueController.list"
      },
      "delete": {
        "x-controller-name": "AdminQueueController",
        "x-operation-name": "purge",
        "tags": [
          "AdminQueueController"
        ],
        "summary": "Bulk-purge jobs by terminal status",
        "description": "Deletes all jobs in the given terminal status. Only \"completed\" and \"failed\" may be purged, never active pending/processing jobs.",
        "responses": {
          "200": {
            "description": "Number of purged jobs",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PurgeResult"
                }
              }
            }
          },
          "400": {
            "description": "Missing or non-purgeable status"
          }
        },
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "operationId": "AdminQueueController.purge"
      }
    },
    "/admin/settings/{id}": {
      "patch": {
        "x-controller-name": "AdminSettingsController",
        "x-operation-name": "update",
        "tags": [
          "AdminSettingsController"
        ],
        "summary": "Update a delivery profile (sender, provider, credentials)",
        "responses": {
          "200": {
            "description": "The updated settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Settings"
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "number"
            },
            "required": true
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "provider": {
                    "type": "string",
                    "enum": [
                      "sendgrid",
                      "gmail-relay"
                    ]
                  },
                  "fromEmail": {
                    "type": "string",
                    "format": "email"
                  },
                  "fromNames": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string",
                      "minLength": 1
                    }
                  },
                  "defaultLang": {
                    "type": "string",
                    "minLength": 1
                  },
                  "template": {
                    "type": "string"
                  },
                  "apiKey": {
                    "type": "string",
                    "minLength": 1
                  },
                  "smtpUser": {
                    "type": "string",
                    "minLength": 1
                  },
                  "smtpPass": {
                    "type": "string",
                    "minLength": 1
                  },
                  "sgWebhookKey": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              }
            }
          },
          "description": "Fields to update",
          "required": true,
          "x-parameter-index": 1
        },
        "operationId": "AdminSettingsController.update"
      },
      "get": {
        "x-controller-name": "AdminSettingsController",
        "x-operation-name": "findById",
        "tags": [
          "AdminSettingsController"
        ],
        "summary": "Get a single delivery profile",
        "responses": {
          "200": {
            "description": "The settings record",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Settings"
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "number"
            },
            "required": true
          }
        ],
        "operationId": "AdminSettingsController.findById"
      },
      "delete": {
        "x-controller-name": "AdminSettingsController",
        "x-operation-name": "delete",
        "tags": [
          "AdminSettingsController"
        ],
        "summary": "Delete a delivery profile",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "404": {
            "description": "Not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "number"
            },
            "required": true
          }
        ],
        "operationId": "AdminSettingsController.delete"
      }
    },
    "/admin/settings": {
      "post": {
        "x-controller-name": "AdminSettingsController",
        "x-operation-name": "create",
        "tags": [
          "AdminSettingsController"
        ],
        "summary": "Create a delivery profile for an application/code",
        "responses": {
          "200": {
            "description": "The created settings",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Settings"
                }
              }
            }
          },
          "409": {
            "description": "Settings for this appId/code already exist"
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "appId",
                  "code",
                  "provider",
                  "fromEmail",
                  "fromNames",
                  "defaultLang"
                ],
                "properties": {
                  "appId": {
                    "type": "string",
                    "minLength": 1
                  },
                  "code": {
                    "type": "string",
                    "minLength": 1
                  },
                  "provider": {
                    "type": "string",
                    "enum": [
                      "sendgrid",
                      "gmail-relay"
                    ]
                  },
                  "fromEmail": {
                    "type": "string",
                    "format": "email"
                  },
                  "fromNames": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string",
                      "minLength": 1
                    }
                  },
                  "defaultLang": {
                    "type": "string",
                    "minLength": 1
                  },
                  "template": {
                    "type": "string"
                  },
                  "apiKey": {
                    "type": "string",
                    "minLength": 1
                  },
                  "smtpUser": {
                    "type": "string",
                    "minLength": 1
                  },
                  "smtpPass": {
                    "type": "string",
                    "minLength": 1
                  },
                  "sgWebhookKey": {
                    "type": "string",
                    "minLength": 1
                  }
                }
              },
              "example": {
                "appId": "canvas",
                "code": "default",
                "provider": "sendgrid",
                "fromEmail": "info@example.com",
                "fromNames": {
                  "cs": "Příklad",
                  "en": "Example"
                },
                "defaultLang": "cs",
                "template": "artslimit",
                "apiKey": "SG.xxxxx"
              }
            }
          },
          "description": "Provider delivery profile for an application/code",
          "required": true
        },
        "operationId": "AdminSettingsController.create"
      },
      "get": {
        "x-controller-name": "AdminSettingsController",
        "x-operation-name": "list",
        "tags": [
          "AdminSettingsController"
        ],
        "summary": "List delivery profiles, optionally filtered by appId",
        "responses": {
          "200": {
            "description": "Array of settings",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/Settings"
                  }
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "appId",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "operationId": "AdminSettingsController.list"
      }
    },
    "/admin/templates": {
      "get": {
        "x-controller-name": "AdminSettingsController",
        "x-operation-name": "templates",
        "tags": [
          "AdminSettingsController"
        ],
        "summary": "Brand templates a delivery profile can be wrapped in",
        "description": "Asks the template renderer what it can produce. Returns an empty list when no renderer is configured or it cannot be reached, so a profile stays editable (the template id can then be typed by hand).",
        "responses": {
          "200": {
            "description": "Available templates",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "id": {
                        "type": "string"
                      },
                      "langs": {
                        "type": "array",
                        "items": {
                          "type": "string"
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "AdminSettingsController.templates"
      }
    },
    "/admin/test-send": {
      "post": {
        "x-controller-name": "AdminTestController",
        "x-operation-name": "testSend",
        "tags": [
          "AdminTestController"
        ],
        "summary": "Send a test email immediately (bypassing the queue)",
        "description": "Sends a single email through the target application's provider and returns the delivery outcome synchronously. The send is still recorded in the mail log.",
        "responses": {
          "200": {
            "description": "Delivery outcome",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/TestSendResult"
                }
              }
            }
          },
          "404": {
            "description": "Application not found"
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "appId",
                  "code",
                  "to"
                ],
                "properties": {
                  "appId": {
                    "type": "string",
                    "minLength": 1
                  },
                  "code": {
                    "type": "string",
                    "minLength": 1
                  },
                  "to": {
                    "type": "string",
                    "format": "email"
                  },
                  "subject": {
                    "type": "string"
                  },
                  "html": {
                    "type": "string"
                  },
                  "lang": {
                    "type": "string"
                  },
                  "template": {
                    "type": "string"
                  },
                  "attachments": {
                    "type": "array",
                    "items": {
                      "type": "object"
                    }
                  }
                }
              },
              "example": {
                "appId": "canvas",
                "code": "default",
                "to": "me@example.com",
                "lang": "cs"
              }
            }
          },
          "description": "Send a one-off test email through an application provider",
          "required": true
        },
        "operationId": "AdminTestController.testSend"
      }
    },
    "/admin/users/{id}": {
      "patch": {
        "x-controller-name": "AdminUsersController",
        "x-operation-name": "update",
        "tags": [
          "AdminUsersController"
        ],
        "summary": "Update an admin user (name/role/enabled/password)",
        "responses": {
          "200": {
            "description": "The updated admin user",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminUser"
                }
              }
            }
          },
          "400": {
            "description": "Cannot disable your own account"
          },
          "404": {
            "description": "Not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "role": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean"
                  },
                  "password": {
                    "type": "string",
                    "minLength": 4
                  }
                }
              }
            }
          },
          "description": "Fields to update (omit password to keep it unchanged)",
          "required": true,
          "x-parameter-index": 1
        },
        "operationId": "AdminUsersController.update"
      },
      "get": {
        "x-controller-name": "AdminUsersController",
        "x-operation-name": "findById",
        "tags": [
          "AdminUsersController"
        ],
        "summary": "Get an admin user",
        "responses": {
          "200": {
            "description": "The admin user",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminUser"
                }
              }
            }
          },
          "404": {
            "description": "Not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "operationId": "AdminUsersController.findById"
      },
      "delete": {
        "x-controller-name": "AdminUsersController",
        "x-operation-name": "delete",
        "tags": [
          "AdminUsersController"
        ],
        "summary": "Delete an admin user",
        "responses": {
          "204": {
            "description": "Deleted"
          },
          "400": {
            "description": "Cannot delete your own account"
          },
          "404": {
            "description": "Not found"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "operationId": "AdminUsersController.delete"
      }
    },
    "/admin/users": {
      "post": {
        "x-controller-name": "AdminUsersController",
        "x-operation-name": "create",
        "tags": [
          "AdminUsersController"
        ],
        "summary": "Create an admin user",
        "responses": {
          "200": {
            "description": "The created admin user",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/AdminUser"
                }
              }
            }
          },
          "409": {
            "description": "Email already taken"
          }
        },
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "name",
                  "password"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "minLength": 1
                  },
                  "name": {
                    "type": "string",
                    "minLength": 1
                  },
                  "password": {
                    "type": "string",
                    "minLength": 4
                  },
                  "role": {
                    "type": "string"
                  }
                }
              }
            }
          },
          "description": "New admin user",
          "required": true
        },
        "operationId": "AdminUsersController.create"
      },
      "get": {
        "x-controller-name": "AdminUsersController",
        "x-operation-name": "list",
        "tags": [
          "AdminUsersController"
        ],
        "summary": "List admin users",
        "responses": {
          "200": {
            "description": "Array of admin users",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/AdminUser"
                  }
                }
              }
            }
          }
        },
        "operationId": "AdminUsersController.list"
      }
    },
    "/languages": {
      "get": {
        "x-controller-name": "LanguagesController",
        "x-operation-name": "languages",
        "tags": [
          "Languages"
        ],
        "responses": {
          "200": {
            "description": "Supported languages",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "code": {
                        "type": "string"
                      },
                      "label": {
                        "type": "string"
                      },
                      "countryCode": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "LanguagesController.languages"
      }
    },
    "/relay/email/log/{id}": {
      "get": {
        "x-controller-name": "RelayController",
        "x-operation-name": "getLogEntry",
        "tags": [
          "RelayController"
        ],
        "summary": "Get a single email log entry for the current application",
        "description": "Returns the full log entry (including the `text`/`html` bodies and delivery status) for the authenticated application. Entries of other applications are not accessible.",
        "responses": {
          "200": {
            "description": "The full log entry",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LogMail"
                }
              }
            }
          },
          "404": {
            "description": "No such log entry for this application"
          }
        },
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "schema": {
              "type": "number"
            },
            "required": true
          }
        ],
        "operationId": "RelayController.getLogEntry"
      }
    },
    "/relay/email/log": {
      "get": {
        "x-controller-name": "RelayController",
        "x-operation-name": "getLog",
        "tags": [
          "RelayController"
        ],
        "summary": "Get email logs for the current application",
        "description": "Returns a paginated list of email logs (sent messages) for the authenticated application. Ordered by send date (newest first); controlled via the `page` and `limit` query parameters. The list omits the message bodies (`text`/`html`) — fetch a single entry via `GET /relay/email/log/{id}` for the full record.",
        "responses": {
          "200": {
            "description": "Paginated list of email logs (without message bodies)",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LogMailGetResponse"
                }
              }
            }
          }
        },
        "parameters": [
          {
            "name": "page",
            "in": "query",
            "schema": {
              "type": "number"
            }
          },
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "number"
            }
          }
        ],
        "operationId": "RelayController.getLog"
      }
    },
    "/relay/email/send": {
      "post": {
        "x-controller-name": "RelayController",
        "x-operation-name": "sendEmail",
        "tags": [
          "RelayController"
        ],
        "description": "Queues one or more emails for asynchronous delivery through the application's configured provider (SendGrid or Gmail). The batch is expanded into one durable job per recipient and processed by a background worker. Each email object must include a `to` field (array of recipients), subject, and body (`html` required, `text` optional). Duplicate submissions are suppressed: send an `X-Idempotency-Key` header to dedupe explicitly, or rely on the automatic content-based window (see `MAIL_DEDUPE_WINDOW_MS`). `accepted` counts the jobs actually queued and `duplicates` those dropped as repeats.",
        "responses": {
          "202": {
            "description": "Emails accepted and enqueued for delivery",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "accepted": {
                      "type": "number"
                    },
                    "duplicates": {
                      "type": "number"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "Invalid input data or malformed attachment"
          },
          "413": {
            "description": "Attachments exceed the configured size limit"
          },
          "500": {
            "description": "Internal server error"
          }
        },
        "parameters": [
          {
            "name": "code",
            "in": "query",
            "schema": {
              "type": "string"
            },
            "required": true
          },
          {
            "name": "X-Idempotency-Key",
            "in": "header",
            "schema": {
              "type": "string"
            },
            "required": false
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "type": "array",
                "items": {
                  "$ref": "#/components/schemas/MailData"
                }
              },
              "example": [
                {
                  "to": [
                    {
                      "email": "user1@example.com",
                      "recipientId": "100"
                    },
                    {
                      "email": "user2@example.com",
                      "recipientId": "200"
                    }
                  ],
                  "subject": "Test email",
                  "text": "Hello from Relay Hub!",
                  "html": "<p>Hello from Relay Hub!</p><img src=\"cid:logo123\">",
                  "attachments": [
                    {
                      "filename": "logo.png",
                      "content": "iVBORw0KGgo...base64...",
                      "type": "image/png",
                      "disposition": "inline",
                      "contentId": "logo123"
                    },
                    {
                      "filename": "invoice.pdf",
                      "content": "JVBERi0xLjcK...base64...",
                      "type": "application/pdf",
                      "disposition": "attachment"
                    }
                  ]
                }
              ]
            }
          },
          "required": true,
          "description": "An array of email objects to be sent. Each object must include recipients (`to`: array of `{email, recipientId?}`), subject, and body (`html` required, `text` optional). Optional `attachments` carry base64 `content` with a MIME `type`; set `disposition: \"inline\"` with a `contentId` to embed an image referenced in the HTML as `<img src=\"cid:<contentId>\">`.",
          "x-parameter-index": 1
        },
        "operationId": "RelayController.sendEmail"
      }
    },
    "/relay/health": {
      "get": {
        "x-controller-name": "RelayController",
        "x-operation-name": "healthCheck",
        "tags": [
          "RelayController"
        ],
        "responses": {
          "200": {
            "description": "Return value of RelayController.healthCheck"
          }
        },
        "operationId": "RelayController.healthCheck"
      }
    },
    "/version": {
      "get": {
        "x-controller-name": "VersionController",
        "x-operation-name": "version",
        "tags": [
          "Version"
        ],
        "responses": {
          "200": {
            "description": "Version",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "app": {
                      "type": "string"
                    },
                    "version": {
                      "type": "string"
                    },
                    "apiVersion": {
                      "type": "string"
                    },
                    "minClientVersion": {
                      "type": "string"
                    }
                  }
                }
              }
            }
          }
        },
        "operationId": "VersionController.version"
      }
    },
    "/webhooks/sendgrid/{appId}/{code}": {
      "post": {
        "x-controller-name": "SendgridWebhookController",
        "x-operation-name": "handle",
        "tags": [
          "SendgridWebhookController"
        ],
        "responses": {
          "204": {
            "description": "Events accepted"
          },
          "401": {
            "description": "Missing/invalid signature or webhook not configured"
          }
        },
        "parameters": [
          {
            "name": "appId",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "required": true
          },
          {
            "name": "code",
            "in": "path",
            "schema": {
              "type": "string"
            },
            "required": true
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "x-parser": "raw",
              "schema": {
                "type": "array",
                "items": {
                  "type": "object"
                }
              }
            }
          },
          "required": true,
          "x-parameter-index": 2
        },
        "operationId": "SendgridWebhookController.handle"
      }
    }
  },
  "servers": [
    {
      "url": "https://relayhub.uat.bckpp.com"
    }
  ],
  "components": {
    "schemas": {
      "Buffer": {},
      "MailData": {
        "title": "MailData",
        "type": "object",
        "properties": {
          "to": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "subject": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "html": {
            "type": "string"
          },
          "attachments": {
            "type": "array",
            "items": {
              "type": "object"
            }
          },
          "lang": {
            "type": "string"
          }
        },
        "required": [
          "to",
          "subject",
          "html"
        ],
        "additionalProperties": false
      },
      "LogMail": {
        "title": "LogMail",
        "type": "object",
        "properties": {
          "id": {
            "type": "number"
          },
          "provider": {
            "type": "string"
          },
          "appId": {
            "type": "string"
          },
          "sendDate": {
            "type": "string",
            "format": "date-time"
          },
          "from": {
            "type": "string"
          },
          "to": {
            "type": "string"
          },
          "recipientId": {
            "type": "string"
          },
          "subject": {
            "type": "string"
          },
          "text": {
            "type": "string"
          },
          "html": {
            "type": "string"
          },
          "xMessageId": {
            "type": "string"
          },
          "messageId": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "errorCode": {
            "type": "string"
          },
          "errorMessage": {
            "type": "string"
          },
          "attachments": {
            "type": "string"
          },
          "deliveryStatus": {
            "type": "string"
          },
          "deliveryUpdatedAt": {
            "type": "string",
            "format": "date-time"
          },
          "deliveryReason": {
            "type": "string"
          }
        },
        "required": [
          "provider",
          "appId",
          "from"
        ],
        "additionalProperties": false
      },
      "LogMailGetResponse": {
        "title": "LogMailGetResponse",
        "type": "object",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/LogMail"
            }
          },
          "total": {
            "type": "number"
          }
        },
        "required": [
          "data",
          "total"
        ],
        "additionalProperties": false
      },
      "AdminUser": {
        "title": "AdminUser",
        "type": "object",
        "description": "(tsType: Omit<Admin, 'passwordHash'>, schemaOptions: { exclude: [ 'passwordHash' ], title: 'AdminUser' }), {\"indexInfo\":{\"email\":{\"unique\":true}}}",
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "role": {
            "type": "string"
          },
          "enabled": {
            "type": "boolean"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "email",
          "name",
          "role"
        ],
        "additionalProperties": false,
        "x-typescript-type": "Omit<Admin, 'passwordHash'>"
      },
      "Settings": {
        "title": "Settings",
        "type": "object",
        "properties": {
          "id": {
            "type": "number"
          },
          "appId": {
            "type": "string"
          },
          "code": {
            "type": "string"
          },
          "provider": {
            "type": "string"
          },
          "fromEmail": {
            "type": "string"
          },
          "fromNames": {
            "type": "object"
          },
          "defaultLang": {
            "type": "string"
          },
          "template": {
            "type": "string"
          },
          "apiKey": {
            "type": "string"
          },
          "smtpUser": {
            "type": "string"
          },
          "smtpPass": {
            "type": "string"
          },
          "sgWebhookKey": {
            "type": "string"
          }
        },
        "required": [
          "appId",
          "code",
          "provider",
          "fromEmail",
          "fromNames",
          "defaultLang"
        ],
        "additionalProperties": false
      },
      "MailJob": {
        "title": "MailJob",
        "type": "object",
        "properties": {
          "id": {
            "type": "string"
          },
          "appId": {
            "type": "string"
          },
          "batchId": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "recipientId": {
            "type": "string"
          },
          "status": {
            "type": "string"
          },
          "dedupeKey": {
            "type": "string"
          },
          "attempts": {
            "type": "number"
          },
          "maxAttempts": {
            "type": "number"
          },
          "availableAt": {
            "type": "string",
            "format": "date-time"
          },
          "lastError": {
            "type": "string"
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          },
          "updatedAt": {
            "type": "string",
            "format": "date-time"
          }
        },
        "required": [
          "appId",
          "batchId",
          "email",
          "status",
          "attempts",
          "maxAttempts"
        ],
        "description": "{\"indexInfo\":{\"dedupeKey\":{\"unique\":true}}}",
        "additionalProperties": false
      },
      "RegeneratedKey": {
        "type": "object",
        "title": "RegeneratedKey",
        "properties": {
          "id": {
            "type": "string"
          },
          "appKey": {
            "type": "string"
          }
        }
      },
      "AppSummary": {
        "type": "object",
        "title": "AppSummary",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "appId": {
            "type": "string"
          }
        }
      },
      "AppWithKey": {
        "type": "object",
        "title": "AppWithKey",
        "properties": {
          "id": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "appId": {
            "type": "string"
          },
          "appKey": {
            "type": "string"
          }
        }
      },
      "DashboardStats": {
        "type": "object",
        "title": "DashboardStats",
        "properties": {
          "apps": {
            "type": "number"
          },
          "logs": {
            "type": "object",
            "properties": {
              "total": {
                "type": "number"
              },
              "sent": {
                "type": "number"
              },
              "failed": {
                "type": "number"
              }
            }
          },
          "logsByProvider": {
            "type": "object",
            "properties": {
              "sendgrid": {
                "type": "number"
              },
              "gmail-relay": {
                "type": "number"
              }
            }
          },
          "queue": {
            "type": "object",
            "properties": {
              "pending": {
                "type": "number"
              },
              "processing": {
                "type": "number"
              },
              "completed": {
                "type": "number"
              },
              "failed": {
                "type": "number"
              }
            }
          }
        }
      },
      "AdminProfile": {
        "type": "object",
        "title": "AdminProfile",
        "properties": {
          "id": {
            "type": "string"
          },
          "email": {
            "type": "string"
          },
          "name": {
            "type": "string"
          },
          "role": {
            "type": "string"
          }
        }
      },
      "MailJobList": {
        "type": "object",
        "title": "MailJobList",
        "properties": {
          "data": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/MailJob"
            }
          },
          "total": {
            "type": "number"
          }
        }
      },
      "PurgeResult": {
        "type": "object",
        "title": "PurgeResult",
        "properties": {
          "deleted": {
            "type": "number"
          }
        }
      },
      "TestSendResult": {
        "type": "object",
        "title": "TestSendResult",
        "properties": {
          "ok": {
            "type": "boolean"
          },
          "provider": {
            "type": "string"
          },
          "template": {
            "type": "string"
          },
          "templateFailed": {
            "type": "boolean"
          },
          "error": {
            "type": "string"
          }
        }
      }
    },
    "securitySchemes": {
      "appKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-KEY"
      },
      "appId": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-ID"
      },
      "adminJwt": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "JWT"
      }
    }
  },
  "security": [
    {
      "appKey": [],
      "appId": [],
      "adminJwt": []
    }
  ]
}