{
  "openapi": "3.0.3",
  "info": {
    "title": "Ajans11 Haber Makinesi API",
    "version": "1.0.0",
    "description": "REST API for the Ajans11 Haber Makinesi platform. Tenant-scoped endpoints for pulling AI-generated Turkish news drafts, editing them, publishing to your CMS, and reading publication logs. All authenticated endpoints require a Sanctum bearer token that carries the caller's tenant scope.",
    "contact": {
      "name": "Ajans11 Haber Makinesi",
      "url": "https://newsmachine.umutozan.dev/gelistiriciler"
    },
    "license": {
      "name": "Proprietary"
    }
  },
  "servers": [
    {
      "url": "https://newsmachine.umutozan.dev/api/v1",
      "description": "Production"
    }
  ],
  "tags": [
    { "name": "Discovery", "description": "Unauthenticated discovery endpoints." },
    { "name": "Auth", "description": "Token creation and revocation." },
    { "name": "Me", "description": "Current user and tenant." },
    { "name": "Tenants", "description": "Tenant configuration." },
    { "name": "Articles", "description": "AI-generated news drafts and their lifecycle." },
    { "name": "Publication Logs", "description": "Downstream publication attempts." }
  ],
  "security": [
    { "bearerAuth": [] }
  ],
  "paths": {
    "/health": {
      "get": {
        "tags": ["Discovery"],
        "summary": "Health check",
        "security": [],
        "responses": {
          "200": {
            "description": "Service is healthy.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Health" },
                "example": { "status": "ok", "time": "2026-08-11T09:00:00Z" }
              }
            }
          }
        }
      }
    },
    "/openapi.json": {
      "get": {
        "tags": ["Discovery"],
        "summary": "OpenAPI 3.0 specification",
        "security": [],
        "responses": {
          "200": {
            "description": "The current OpenAPI spec as JSON.",
            "content": {
              "application/json": {
                "schema": { "type": "object" }
              }
            }
          }
        }
      }
    },
    "/categories": {
      "get": {
        "tags": ["Discovery"],
        "summary": "Supported category slugs",
        "description": "Static list of category slugs used across the platform. Publicly reachable.",
        "security": [],
        "responses": {
          "200": {
            "description": "Category list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Category" }
                    }
                  }
                }
              }
            }
          }
        }
      }
    },
    "/tokens": {
      "post": {
        "tags": ["Auth"],
        "summary": "Create an API token",
        "description": "Exchange email + password for a Sanctum personal-access token. Rate-limited to 5 requests / minute / IP.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["email", "password"],
                "properties": {
                  "email": { "type": "string", "format": "email" },
                  "password": { "type": "string", "format": "password" },
                  "name": { "type": "string", "description": "Human-readable token name.", "default": "api-token" }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Token issued.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "token": { "type": "string", "example": "1|abcDEF..." },
                    "user": { "$ref": "#/components/schemas/User" },
                    "tenant": { "$ref": "#/components/schemas/Tenant" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "422": { "$ref": "#/components/responses/ValidationError" },
          "429": { "$ref": "#/components/responses/RateLimited" }
        }
      }
    },
    "/tokens/current": {
      "delete": {
        "tags": ["Auth"],
        "summary": "Revoke the current token",
        "responses": {
          "200": {
            "description": "Token revoked.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "revoked": { "type": "boolean", "example": true } }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/me": {
      "get": {
        "tags": ["Me"],
        "summary": "Current user + tenant",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "user": { "$ref": "#/components/schemas/User" },
                    "tenant": { "$ref": "#/components/schemas/Tenant" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/tenants/me": {
      "get": {
        "tags": ["Tenants"],
        "summary": "Read caller's tenant",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "data": { "$ref": "#/components/schemas/Tenant" } }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      },
      "patch": {
        "tags": ["Tenants"],
        "summary": "Update caller's tenant",
        "description": "Owner / super-admin only. Whitelisted keys: name, description, config.prompt.*, config.categories, config.blocked_categories, config.gate.*.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "name": { "type": "string" },
                  "description": { "type": "string" },
                  "config": {
                    "type": "object",
                    "properties": {
                      "prompt": {
                        "type": "object",
                        "properties": {
                          "brand_voice": { "type": "string" },
                          "style_hints": { "type": "string" },
                          "min_words": { "type": "integer" }
                        }
                      },
                      "categories": { "type": "array", "items": { "type": "string" } },
                      "blocked_categories": { "type": "array", "items": { "type": "string" } },
                      "gate": {
                        "type": "object",
                        "properties": {
                          "min_quality": { "type": "number" },
                          "min_confidence": { "type": "number" },
                          "max_risk": { "type": "number" },
                          "min_sources": { "type": "integer" }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated tenant.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "data": { "$ref": "#/components/schemas/Tenant" } }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/ValidationError" }
        }
      }
    },
    "/articles": {
      "get": {
        "tags": ["Articles"],
        "summary": "List AI-generated drafts",
        "description": "Tenant-scoped list. Ordered by published_at DESC, generated_at DESC, id DESC.",
        "parameters": [
          {
            "name": "status",
            "in": "query",
            "schema": { "type": "string", "enum": ["draft", "needs_review", "approved", "published", "rejected"] }
          },
          { "name": "category", "in": "query", "schema": { "type": "string" } },
          { "name": "from", "in": "query", "schema": { "type": "string", "format": "date" }, "description": "generated_at >= from" },
          { "name": "to", "in": "query", "schema": { "type": "string", "format": "date" }, "description": "generated_at < to" },
          { "name": "q", "in": "query", "schema": { "type": "string" }, "description": "Search headline LIKE %q%" },
          { "name": "page", "in": "query", "schema": { "type": "integer", "minimum": 1 } },
          { "name": "per_page", "in": "query", "schema": { "type": "integer", "minimum": 1, "maximum": 100 } }
        ],
        "responses": {
          "200": {
            "description": "Paginated list.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/GeneratedArticle" }
                    },
                    "meta": { "$ref": "#/components/schemas/PaginationMeta" },
                    "links": { "$ref": "#/components/schemas/PaginationLinks" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/articles/{id}": {
      "get": {
        "tags": ["Articles"],
        "summary": "Get a single article",
        "parameters": [{ "$ref": "#/components/parameters/ArticleId" }],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "data": { "$ref": "#/components/schemas/GeneratedArticle" } }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      },
      "patch": {
        "tags": ["Articles"],
        "summary": "Update an article",
        "description": "Editor+ role required. Editable fields: headline, spot, body, category, tags, seo_title, seo_description, status. Only users with publish permission may set status=published.",
        "parameters": [{ "$ref": "#/components/parameters/ArticleId" }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "headline": { "type": "string" },
                  "spot": { "type": "string" },
                  "body": { "type": "string" },
                  "category": { "type": "string" },
                  "tags": { "type": "array", "items": { "type": "string" } },
                  "seo_title": { "type": "string" },
                  "seo_description": { "type": "string" },
                  "status": { "type": "string", "enum": ["draft", "needs_review", "approved", "published", "rejected"] }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Updated article.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "data": { "$ref": "#/components/schemas/GeneratedArticle" } }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" },
          "422": { "$ref": "#/components/responses/ValidationError" }
        }
      }
    },
    "/articles/{id}/publish": {
      "post": {
        "tags": ["Articles"],
        "summary": "Publish an article",
        "description": "Bypasses the auto-publish gate and publishes to configured downstream targets. Sets status=published and returns the publication log id + external URL.",
        "parameters": [{ "$ref": "#/components/parameters/ArticleId" }],
        "responses": {
          "200": {
            "description": "Published.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "status": { "type": "string", "example": "ok" },
                    "log_id": { "type": "integer" },
                    "target_id": { "type": "integer" },
                    "external_url": { "type": "string", "format": "uri" },
                    "article": { "$ref": "#/components/schemas/GeneratedArticle" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/articles/{id}/reject": {
      "post": {
        "tags": ["Articles"],
        "summary": "Reject an article",
        "parameters": [{ "$ref": "#/components/parameters/ArticleId" }],
        "responses": {
          "200": {
            "description": "Rejected.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "data": { "$ref": "#/components/schemas/GeneratedArticle" } }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/publication-logs": {
      "get": {
        "tags": ["Publication Logs"],
        "summary": "List publication attempts",
        "description": "Tenant-scoped via linked article. Newest first.",
        "parameters": [
          { "name": "page", "in": "query", "schema": { "type": "integer" } },
          { "name": "per_page", "in": "query", "schema": { "type": "integer", "maximum": 100 } }
        ],
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/PublicationLog" }
                    },
                    "meta": { "$ref": "#/components/schemas/PaginationMeta" },
                    "links": { "$ref": "#/components/schemas/PaginationLinks" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/webhooks": {
      "get": {
        "tags": ["Webhooks"],
        "summary": "List webhook subscriptions",
        "responses": {
          "200": {
            "description": "OK",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "type": "array",
                      "items": { "$ref": "#/components/schemas/Webhook" }
                    }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      },
      "post": {
        "tags": ["Webhooks"],
        "summary": "Register a webhook",
        "description": "Owner role required. Returns the plaintext HMAC-SHA256 signing secret exactly once. Store it immediately — it cannot be recovered.",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": ["name", "url", "events"],
                "properties": {
                  "name": { "type": "string" },
                  "url": { "type": "string", "format": "uri" },
                  "events": {
                    "type": "array",
                    "items": {
                      "type": "string",
                      "enum": [
                        "article.published",
                        "article.needs_review",
                        "article.rejected",
                        "gallery.published",
                        "quality.dropped"
                      ]
                    }
                  },
                  "active": { "type": "boolean", "default": true }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Webhook created — plaintext secret returned once.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "data": {
                      "allOf": [
                        { "$ref": "#/components/schemas/Webhook" },
                        {
                          "type": "object",
                          "properties": { "secret": { "type": "string" } }
                        }
                      ]
                    },
                    "notice": { "type": "string" }
                  }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "422": { "$ref": "#/components/responses/ValidationError" }
        }
      }
    },
    "/webhooks/{id}": {
      "delete": {
        "tags": ["Webhooks"],
        "summary": "Delete a webhook",
        "parameters": [{
          "name": "id",
          "in": "path",
          "required": true,
          "schema": { "type": "integer" }
        }],
        "responses": {
          "200": {
            "description": "Deleted.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": { "status": { "type": "string", "example": "ok" } }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "Sanctum personal-access token"
      }
    },
    "parameters": {
      "ArticleId": {
        "name": "id",
        "in": "path",
        "required": true,
        "schema": { "type": "integer", "minimum": 1 }
      }
    },
    "schemas": {
      "Health": {
        "type": "object",
        "properties": {
          "status": { "type": "string", "example": "ok" },
          "time": { "type": "string", "format": "date-time" }
        }
      },
      "Category": {
        "type": "object",
        "properties": {
          "slug": { "type": "string", "example": "gundem" },
          "name": { "type": "string", "example": "Gündem" }
        }
      },
      "User": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "email": { "type": "string", "format": "email" },
          "name": { "type": "string" },
          "role": { "type": "string", "example": "owner" },
          "tenant_id": { "type": "integer" }
        }
      },
      "Tenant": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "slug": { "type": "string" },
          "name": { "type": "string" },
          "description": { "type": "string", "nullable": true },
          "config": {
            "type": "object",
            "description": "Tenant configuration. Secret env-var pointers are redacted."
          }
        }
      },
      "GeneratedArticle": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "tenant_id": { "type": "integer" },
          "headline": { "type": "string" },
          "spot": { "type": "string", "nullable": true, "description": "Short lede / excerpt." },
          "body": { "type": "string" },
          "category": { "type": "string" },
          "tags": { "type": "array", "items": { "type": "string" } },
          "status": {
            "type": "string",
            "enum": ["draft", "needs_review", "approved", "published", "rejected"]
          },
          "hero_image_url": { "type": "string", "format": "uri", "nullable": true },
          "hero_image_credit": { "type": "string", "nullable": true },
          "seo_title": { "type": "string", "nullable": true },
          "seo_description": { "type": "string", "nullable": true },
          "quality_score": { "type": "number", "format": "float" },
          "source_urls": {
            "type": "array",
            "items": { "type": "string", "format": "uri" },
            "description": "Original source URLs the draft was verified against."
          },
          "generated_at": { "type": "string", "format": "date-time" },
          "published_at": { "type": "string", "format": "date-time", "nullable": true }
        }
      },
      "PublicationLog": {
        "type": "object",
        "properties": {
          "id": { "type": "integer" },
          "article_id": { "type": "integer" },
          "target_id": { "type": "integer" },
          "target_type": { "type": "string", "example": "wordpress" },
          "external_url": { "type": "string", "format": "uri", "nullable": true },
          "external_id": { "type": "string", "nullable": true },
          "status": { "type": "string", "enum": ["queued", "sent", "failed"] },
          "attempted_at": { "type": "string", "format": "date-time" },
          "response_snippet": { "type": "string", "nullable": true }
        }
      },
      "Webhook": {
        "type": "object",
        "description": "Webhook subscription. Outgoing payloads are signed with HMAC-SHA256 in the X-Signature header.",
        "properties": {
          "id": { "type": "integer" },
          "name": { "type": "string" },
          "url": { "type": "string", "format": "uri" },
          "events": {
            "type": "array",
            "items": {
              "type": "string",
              "enum": [
                "article.published",
                "article.needs_review",
                "article.rejected",
                "gallery.published",
                "quality.dropped"
              ]
            }
          },
          "active": { "type": "boolean" },
          "last_success_at": { "type": "string", "format": "date-time", "nullable": true },
          "last_failure_at": { "type": "string", "format": "date-time", "nullable": true },
          "consecutive_failures": { "type": "integer" },
          "created_at": { "type": "string", "format": "date-time" }
        }
      },
      "PaginationMeta": {
        "type": "object",
        "properties": {
          "current_page": { "type": "integer" },
          "per_page": { "type": "integer" },
          "total": { "type": "integer" },
          "last_page": { "type": "integer" }
        }
      },
      "PaginationLinks": {
        "type": "object",
        "properties": {
          "first": { "type": "string", "nullable": true },
          "last": { "type": "string", "nullable": true },
          "prev": { "type": "string", "nullable": true },
          "next": { "type": "string", "nullable": true }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "message": { "type": "string" },
          "errors": {
            "type": "object",
            "additionalProperties": { "type": "array", "items": { "type": "string" } }
          }
        }
      }
    },
    "responses": {
      "Unauthorized": {
        "description": "Missing or invalid credentials.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "Forbidden": {
        "description": "Authenticated but role is insufficient.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "NotFound": {
        "description": "Resource not found in tenant scope.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "ValidationError": {
        "description": "Validation failed.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      },
      "RateLimited": {
        "description": "Too many requests.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" } } }
      }
    }
  }
}
