{
  "openapi": "3.0.3",
  "info": {
    "title": "ArticleGrade API",
    "description": "Run the ArticleGrade dual-LLM content-quality audit from your code: start audits, poll or stream results, bulk-submit URLs, schedule recurring audits, and read account usage. Audits are metered against your plan's monthly quota; deep audits past the included amount are billed as overage. API access is a Growth / Enterprise feature.",
    "version": "1.0.0",
    "contact": {
      "name": "ArticleGrade",
      "url": "https://articlegrade.com",
      "email": "hello@articlegrade.com"
    },
    "termsOfService": "https://articlegrade.com/terms"
  },
  "servers": [
    {
      "url": "https://app.articlegrade.com",
      "description": "Production"
    }
  ],
  "security": [
    {
      "bearerAuth": []
    }
  ],
  "tags": [
    { "name": "Audits", "description": "Start, fetch, stream and list content-quality audits." },
    { "name": "Account", "description": "Plan, features and usage." },
    { "name": "Schedules", "description": "Recurring automated audits." }
  ],
  "paths": {
    "/v1/audits": {
      "post": {
        "tags": ["Audits"],
        "summary": "Start an audit",
        "description": "Queues an audit for a single URL and returns immediately. Defaults to the deep dual-LLM audit (provider \"claude\"). Use provider \"gemini\" for the fast SEO/structure screen only, or runBoth=true to force the full deep audit.",
        "operationId": "createAudit",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateAuditRequest" },
              "examples": {
                "deep": {
                  "summary": "Deep audit (default)",
                  "value": { "url": "https://example.com/blog/best-running-shoes" }
                },
                "screen": {
                  "summary": "Fast SEO/structure screen only",
                  "value": { "url": "https://example.com/blog/best-running-shoes", "provider": "gemini" }
                },
                "both": {
                  "summary": "Force full deep dual-LLM audit",
                  "value": { "url": "https://example.com/blog/best-running-shoes", "runBoth": true }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Audit queued.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/AuditAccepted" },
                "example": {
                  "id": "aud_8Fq2kR1xR7",
                  "status": "queued",
                  "url": "https://example.com/blog/best-running-shoes"
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/QuotaExceeded" },
          "403": { "$ref": "#/components/responses/Forbidden" },
          "502": { "$ref": "#/components/responses/BadGateway" }
        }
      },
      "get": {
        "tags": ["Audits"],
        "summary": "List recent audits",
        "description": "Returns your most recent audits, newest first.",
        "operationId": "listAudits",
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "How many audits to return. Defaults to 50.",
            "schema": { "type": "integer", "default": 50, "minimum": 1, "maximum": 200 }
          }
        ],
        "responses": {
          "200": {
            "description": "A list of audits.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": { "$ref": "#/components/schemas/Audit" }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/v1/audits/{id}": {
      "get": {
        "tags": ["Audits"],
        "summary": "Get an audit",
        "description": "Fetches the full audit record. Poll until status is \"done\" or \"error\". While running, score and most of record are null.",
        "operationId": "getAudit",
        "parameters": [
          { "$ref": "#/components/parameters/AuditId" }
        ],
        "responses": {
          "200": {
            "description": "The audit record.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Audit" },
                "example": {
                  "id": "aud_8Fq2kR1xR7",
                  "status": "done",
                  "score": 82,
                  "provider": "claude",
                  "title": "The Best Running Shoes of 2026",
                  "live_url": "https://example.com/blog/best-running-shoes",
                  "record": {
                    "dims": [
                      { "name": "Experience", "val": 78 },
                      { "name": "Expertise", "val": 85 },
                      { "name": "Factual depth", "val": 80 }
                    ],
                    "errors": [
                      {
                        "phrase": "experts agree these are the best",
                        "sev": "high",
                        "fix": "Attribute to a named source or cite testing data.",
                        "source": "claude"
                      }
                    ],
                    "scores": { "claude": 82, "gemini": 76 },
                    "text": "Choosing a running shoe comes down to...",
                    "headings": ["How we tested", "Top picks"]
                  },
                  "pass": true,
                  "threshold": 70
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/audits/{id}/stream": {
      "get": {
        "tags": ["Audits"],
        "summary": "Stream audit progress (SSE)",
        "description": "Opens a Server-Sent Events (text/event-stream) connection that pushes live progress while the audit runs. Emits 'status' events as the audit moves through its phases, and a single 'complete' event carrying the final score when it reaches done or error, after which the stream closes.",
        "operationId": "streamAudit",
        "parameters": [
          { "$ref": "#/components/parameters/AuditId" }
        ],
        "responses": {
          "200": {
            "description": "An event stream of status and complete events.",
            "content": {
              "text/event-stream": {
                "schema": { "type": "string" },
                "example": "event: status\ndata: {\"status\":\"running\"}\n\nevent: complete\ndata: {\"status\":\"done\",\"score\":82}\n\n"
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "404": { "$ref": "#/components/responses/NotFound" }
        }
      }
    },
    "/v1/audits/bulk": {
      "post": {
        "tags": ["Audits"],
        "summary": "Bulk submit audits",
        "description": "Submits many URLs in one call (Growth+). Up to 200 URLs per request. Each accepted URL becomes its own audit you then poll or stream individually.",
        "operationId": "bulkCreateAudits",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/BulkAuditRequest" },
              "example": { "urls": ["https://example.com/a", "https://example.com/b"] }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Batch accepted; per-URL acceptance and rejection lists.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/BulkAuditResponse" },
                "example": {
                  "accepted": [{ "id": "aud_aa11", "url": "https://example.com/a" }],
                  "rejected": [{ "url": "not-a-url", "reason": "invalid url" }]
                }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "402": { "$ref": "#/components/responses/QuotaExceeded" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    },
    "/v1/account": {
      "get": {
        "tags": ["Account"],
        "summary": "Account, features and usage",
        "description": "Returns your organization, current usage against quota, and the feature flags your plan enables.",
        "operationId": "getAccount",
        "responses": {
          "200": {
            "description": "Account, usage and features.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Account" },
                "example": {
                  "org": "Acme Media",
                  "usage": { "included": 200, "used": 147, "overage": 0 },
                  "features": { "api": true, "bulk": true, "schedules": true, "webhooks": true }
                }
              }
            }
          },
          "401": { "$ref": "#/components/responses/Unauthorized" }
        }
      }
    },
    "/v1/schedules": {
      "post": {
        "tags": ["Schedules"],
        "summary": "Create a recurring audit",
        "description": "Creates a schedule so a URL is re-graded automatically on a daily, weekly or monthly cadence (Growth+).",
        "operationId": "createSchedule",
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": { "$ref": "#/components/schemas/CreateScheduleRequest" },
              "example": { "url": "https://example.com/pillar", "cadence": "weekly" }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Schedule created.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Schedule" },
                "example": { "id": "sch_3kQ9", "url": "https://example.com/pillar", "cadence": "weekly" }
              }
            }
          },
          "400": { "$ref": "#/components/responses/BadRequest" },
          "401": { "$ref": "#/components/responses/Unauthorized" },
          "403": { "$ref": "#/components/responses/Forbidden" }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "bearerFormat": "ag_live_...",
        "description": "API key as a bearer token: `Authorization: Bearer ag_live_...`. The `x-api-key: ag_live_...` header is accepted as an alternative. Keys are created on the Developers page of the dashboard (Growth / Enterprise feature)."
      }
    },
    "parameters": {
      "AuditId": {
        "name": "id",
        "in": "path",
        "required": true,
        "description": "The audit id, e.g. aud_8Fq2kR1xR7.",
        "schema": { "type": "string", "example": "aud_8Fq2kR1xR7" }
      }
    },
    "responses": {
      "BadRequest": {
        "description": "Bad input — missing/malformed url, invalid provider, or non-JSON body.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "url is required" } } }
      },
      "Unauthorized": {
        "description": "Missing or invalid API key.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "invalid api key" } } }
      },
      "QuotaExceeded": {
        "description": "Monthly quota or a configured hard limit was hit.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "monthly audit limit reached" } } }
      },
      "Forbidden": {
        "description": "Your plan doesn't include this feature.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "feature not available on your plan" } } }
      },
      "NotFound": {
        "description": "No resource with that id belongs to your account.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "audit not found" } } }
      },
      "BadGateway": {
        "description": "An upstream LLM or fetch failed transiently. Safe to retry with backoff.",
        "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "error": "upstream provider error" } } }
      }
    },
    "schemas": {
      "CreateAuditRequest": {
        "type": "object",
        "required": ["url"],
        "properties": {
          "url": {
            "type": "string",
            "format": "uri",
            "description": "Fully-qualified https URL of the live page to audit.",
            "example": "https://example.com/blog/best-running-shoes"
          },
          "provider": {
            "type": "string",
            "enum": ["claude", "gemini"],
            "default": "claude",
            "description": "claude = deep audit (default); gemini = fast SEO/structure screen only (not a deep audit)."
          },
          "runBoth": {
            "type": "boolean",
            "description": "When true, runs the full deep dual-LLM audit (Claude + Gemini)."
          }
        }
      },
      "AuditAccepted": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "example": "aud_8Fq2kR1xR7" },
          "status": { "type": "string", "enum": ["queued"], "example": "queued" },
          "url": { "type": "string", "format": "uri", "example": "https://example.com/blog/best-running-shoes" }
        }
      },
      "Audit": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "example": "aud_8Fq2kR1xR7" },
          "status": {
            "type": "string",
            "enum": ["queued", "running", "done", "error"],
            "description": "Poll until done or error."
          },
          "score": {
            "type": "integer",
            "nullable": true,
            "minimum": 0,
            "maximum": 100,
            "description": "Overall quality score 0-100. null until the audit finishes.",
            "example": 82
          },
          "provider": { "type": "string", "enum": ["claude", "gemini"], "example": "claude" },
          "title": { "type": "string", "example": "The Best Running Shoes of 2026" },
          "live_url": { "type": "string", "format": "uri", "example": "https://example.com/blog/best-running-shoes" },
          "record": { "$ref": "#/components/schemas/AuditRecord" },
          "pass": {
            "type": "boolean",
            "nullable": true,
            "description": "Whether the score met threshold, if one applies; else null."
          },
          "threshold": {
            "type": "number",
            "nullable": true,
            "description": "The pass/fail threshold applied, if any.",
            "example": 70
          }
        }
      },
      "AuditRecord": {
        "type": "object",
        "nullable": true,
        "description": "The full audit result. Largely null until the audit completes.",
        "properties": {
          "dims": {
            "type": "array",
            "description": "Scored dimensions.",
            "items": {
              "type": "object",
              "properties": {
                "name": { "type": "string", "example": "Expertise" },
                "val": { "type": "number", "example": 85 }
              }
            }
          },
          "errors": {
            "type": "array",
            "description": "Specific issues found in the content.",
            "items": {
              "type": "object",
              "properties": {
                "phrase": { "type": "string", "example": "experts agree these are the best" },
                "sev": { "type": "string", "example": "high" },
                "fix": { "type": "string", "example": "Attribute to a named source or cite testing data." },
                "source": { "type": "string", "example": "claude" }
              }
            }
          },
          "scores": {
            "type": "object",
            "description": "Per-model scores.",
            "properties": {
              "claude": { "type": "number", "nullable": true, "example": 82 },
              "gemini": { "type": "number", "nullable": true, "example": 76 }
            }
          },
          "text": { "type": "string", "description": "The extracted body text that was graded." },
          "headings": {
            "type": "array",
            "description": "The page's heading outline.",
            "items": { "type": "string" },
            "example": ["How we tested", "Top picks"]
          }
        }
      },
      "BulkAuditRequest": {
        "type": "object",
        "required": ["urls"],
        "properties": {
          "urls": {
            "type": "array",
            "description": "URLs to audit. Maximum 200.",
            "maxItems": 200,
            "items": { "type": "string", "format": "uri" },
            "example": ["https://example.com/a", "https://example.com/b"]
          }
        }
      },
      "BulkAuditResponse": {
        "type": "object",
        "properties": {
          "accepted": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "id": { "type": "string", "example": "aud_aa11" },
                "url": { "type": "string", "format": "uri", "example": "https://example.com/a" }
              }
            }
          },
          "rejected": {
            "type": "array",
            "items": {
              "type": "object",
              "properties": {
                "url": { "type": "string", "example": "not-a-url" },
                "reason": { "type": "string", "example": "invalid url" }
              }
            }
          }
        }
      },
      "Account": {
        "type": "object",
        "properties": {
          "org": { "type": "string", "example": "Acme Media" },
          "usage": {
            "type": "object",
            "properties": {
              "included": { "type": "integer", "description": "Deep audits included in the plan this period.", "example": 200 },
              "used": { "type": "integer", "description": "Deep audits used this period.", "example": 147 },
              "overage": { "type": "integer", "description": "Deep audits beyond the included amount, billed as overage.", "example": 0 }
            }
          },
          "features": {
            "type": "object",
            "description": "Feature flags enabled by the current plan.",
            "additionalProperties": { "type": "boolean" },
            "example": { "api": true, "bulk": true, "schedules": true, "webhooks": true }
          }
        }
      },
      "CreateScheduleRequest": {
        "type": "object",
        "required": ["url", "cadence"],
        "properties": {
          "url": { "type": "string", "format": "uri", "example": "https://example.com/pillar" },
          "cadence": { "type": "string", "enum": ["daily", "weekly", "monthly"], "example": "weekly" }
        }
      },
      "Schedule": {
        "type": "object",
        "properties": {
          "id": { "type": "string", "example": "sch_3kQ9" },
          "url": { "type": "string", "format": "uri", "example": "https://example.com/pillar" },
          "cadence": { "type": "string", "enum": ["daily", "weekly", "monthly"], "example": "weekly" }
        }
      },
      "WebhookEvent": {
        "type": "object",
        "description": "Payload POSTed to your registered webhook endpoint on audit completion (Growth+). Signed with header X-AG-Signature: sha256=<hex>, an HMAC-SHA256 of the raw request body using your endpoint signing secret; X-AG-Event carries the event name.",
        "properties": {
          "event": { "type": "string", "enum": ["audit.completed"], "example": "audit.completed" },
          "data": {
            "type": "object",
            "properties": {
              "id": { "type": "string", "example": "aud_8Fq2kR1xR7" },
              "url": { "type": "string", "format": "uri", "example": "https://example.com/blog/best-running-shoes" },
              "score": { "type": "integer", "nullable": true, "example": 82 },
              "status": { "type": "string", "enum": ["done", "error"], "example": "done" }
            }
          },
          "ts": { "type": "integer", "description": "Unix timestamp (seconds).", "example": 1782000000 }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string", "description": "A short, human-readable message.", "example": "url is required" }
        }
      }
    }
  }
}
