{
  "openapi": "3.1.0",
  "info": {
    "title": "modwall \u2014 Image Moderation API",
    "version": "1.0.0",
    "description": "Moderacja obraz\u00f3w (NSFW) jako API. Obraz przetwarzany w pami\u0119ci, nigdy nie zapisywany; logowane tylko metadane."
  },
  "servers": [
    {
      "url": "https://modwall.dev"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Klucz serwerowy sk_live_... w nag\u0142\u00f3wku Authorization."
      }
    },
    "schemas": {
      "ModerationResult": {
        "type": "object",
        "properties": {
          "scores": {
            "type": "object",
            "properties": {
              "nsfw": {
                "type": "number",
                "format": "float"
              },
              "safe": {
                "type": "number",
                "format": "float"
              }
            }
          },
          "decision": {
            "type": "string",
            "enum": [
              "allow",
              "review",
              "block"
            ],
            "description": "Akcja dla klienta WG TRYBU konta. W trybie monitor zawsze 'allow' (nic nie blokujemy)."
          },
          "recommended_action": {
            "type": "string",
            "enum": [
              "allow",
              "review",
              "block"
            ],
            "description": "Surowa rekomendacja modelu, niezale\u017cna od trybu. W monitor pokazuje, co modwall BY zrobi\u0142."
          },
          "mode": {
            "type": "string",
            "enum": [
              "monitor",
              "review",
              "enforce"
            ],
            "description": "Aktywny tryb konta w chwili decyzji."
          },
          "threshold": {
            "type": "number",
            "format": "float"
          },
          "categories": {
            "type": "object",
            "additionalProperties": {
              "type": "number",
              "format": "float"
            },
            "description": "Score per kategoria (nsfw zawsze; weapon/violence \u2014 BETA, tylko gdy wlaczone w profilu klucza)."
          },
          "blocked_by": {
            "type": "array",
            "items": {
              "type": "string"
            },
            "description": "Kategorie, ktore przekroczyly prog blokady."
          },
          "profile": {
            "type": "string",
            "description": "Nazwa profilu moderacji, ktory podjal decyzje."
          },
          "model_version": {
            "type": "string"
          },
          "review_id": {
            "type": "integer",
            "description": "Obecne tylko gdy decision=review."
          }
        }
      }
    }
  },
  "paths": {
    "/v1/moderate": {
      "post": {
        "summary": "Oce\u0144 obraz",
        "parameters": [
          {
            "name": "threshold",
            "in": "query",
            "required": false,
            "schema": {
              "type": "number"
            },
            "description": "Nadpisuje pr\u00f3g blokady na to \u017c\u0105danie (wy\u0142\u0105cza pasmo review)."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "image": {
                    "type": "string",
                    "format": "binary"
                  },
                  "reference": {
                    "type": "string"
                  },
                  "image_url": {
                    "type": "string"
                  }
                },
                "required": [
                  "image"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ModerationResult"
                }
              }
            }
          },
          "401": {
            "description": "Brak/nieprawid\u0142owy klucz"
          },
          "402": {
            "description": "Przekroczony limit planu"
          },
          "413": {
            "description": "Obraz za du\u017cy"
          },
          "503": {
            "description": "Silnik inferencji niedost\u0119pny"
          }
        }
      }
    },
    "/v1/moderate-text": {
      "post": {
        "summary": "Oce\u0144 tekst (wymaga zakresu tre\u015b\u0107/obie)",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "text": {
                    "type": "string"
                  },
                  "reference": {
                    "type": "string"
                  },
                  "image_url": {
                    "type": "string"
                  }
                },
                "required": [
                  "text"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK"
          },
          "401": {
            "description": "Brak/nieprawid\u0142owy klucz"
          },
          "402": {
            "description": "Przekroczony limit planu"
          },
          "403": {
            "description": "Plan nie obejmuje moderacji tekstu"
          },
          "413": {
            "description": "Tekst za d\u0142ugi"
          },
          "503": {
            "description": "Model tekstu niedost\u0119pny"
          }
        }
      }
    },
    "/v1/report": {
      "post": {
        "summary": "Zg\u0142o\u015b tre\u015b\u0107 (warstwa reaktywna)",
        "requestBody": {
          "required": true,
          "content": {
            "multipart/form-data": {
              "schema": {
                "type": "object",
                "properties": {
                  "reference": {
                    "type": "string"
                  },
                  "image_url": {
                    "type": "string"
                  }
                },
                "required": [
                  "reference"
                ]
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "reference": {
                      "type": "string"
                    },
                    "reports": {
                      "type": "integer"
                    },
                    "escalated": {
                      "type": "boolean"
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/v1/review/{id}": {
      "get": {
        "summary": "Status pozycji kolejki HITL",
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "schema": {
              "type": "integer"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "OK"
          },
          "404": {
            "description": "Nie znaleziono"
          }
        }
      }
    }
  }
}