{
 "openapi": "3.1.0",
 "info": {
  "title": "LegalForge API",
  "version": "1.0.0",
  "description": "Generate website and app legal documents (privacy policy, terms of service, cookie policy, disclaimer, refund policy, DMCA policy) from a business's answers. Deterministic templates; not legal advice. Operated by Heron Networks LLC. The same jobs are available as an MCP server at https://privacypolicygenerator.tools/mcp and as the CLI `legalforge`; setup: https://privacypolicygenerator.tools/docs/agents.",
  "contact": {
   "name": "Heron Networks LLC",
   "url": "https://privacypolicygenerator.tools/support"
  }
 },
 "servers": [
  {
   "url": "https://privacypolicygenerator.tools"
  }
 ],
 "externalDocs": {
  "url": "https://privacypolicygenerator.tools/docs/agents",
  "description": "Use LegalForge with your AI agent"
 },
 "components": {
  "securitySchemes": {
   "apiKey": {
    "type": "http",
    "scheme": "bearer",
    "description": "lf_... key shown once at subscription and rotatable on /account. The API is part of the Business plan."
   }
  },
  "schemas": {
   "DocumentType": {
    "type": "string",
    "enum": [
     "privacy-policy",
     "terms-of-service",
     "cookie-policy",
     "disclaimer",
     "refund-policy",
     "dmca-policy"
    ]
   },
   "BusinessInfo": {
    "type": "object",
    "required": [
     "businessName",
     "websiteUrl",
     "email"
    ],
    "properties": {
     "businessName": {
      "type": "string",
      "maxLength": 200
     },
     "websiteUrl": {
      "type": "string",
      "maxLength": 500,
      "description": "Site or app URL; one site slot per distinct host."
     },
     "email": {
      "type": "string",
      "maxLength": 200
     },
     "country": {
      "type": "string",
      "maxLength": 100,
      "default": "United States"
     },
     "businessType": {
      "type": "string",
      "enum": [
       "website",
       "ecommerce",
       "saas",
       "mobile-app",
       "blog"
      ],
      "default": "website"
     },
     "collectsPersonalData": {
      "type": "boolean"
     },
     "collectsPaymentData": {
      "type": "boolean"
     },
     "usesCookies": {
      "type": "boolean"
     },
     "usesAnalytics": {
      "type": "boolean"
     },
     "usesThirdPartyServices": {
      "type": "boolean"
     },
     "thirdPartyServices": {
      "type": "array",
      "items": {
       "type": "string",
       "maxLength": 100
      },
      "maxItems": 20
     },
     "hasUserAccounts": {
      "type": "boolean"
     },
     "allowsChildren": {
      "type": "boolean"
     },
     "sellsProducts": {
      "type": "boolean"
     },
     "refundPeriodDays": {
      "type": "integer",
      "minimum": 0,
      "maximum": 365
     },
     "customClauses": {
      "type": "string",
      "maxLength": 4000,
      "description": "Business plan only."
     },
     "whiteLabelName": {
      "type": "string",
      "maxLength": 120,
      "description": "Business plan only."
     },
     "whiteLabelLogoUrl": {
      "type": "string",
      "maxLength": 500,
      "description": "Business plan only; https URL."
     }
    }
   },
   "Error": {
    "type": "object",
    "required": [
     "error"
    ],
    "properties": {
     "error": {
      "type": "string",
      "description": "e.g. api_key_required, plan_feature, plan_excludes_type, invalid_document_type, business_name_website_email_required, invalid_website_url, invalid_email, site_limit"
     },
     "feature": {
      "type": "string"
     },
     "needs": {
      "type": "string"
     },
     "limit": {
      "type": "integer"
     }
    }
   },
   "GenerateRequest": {
    "type": "object",
    "required": [
     "type",
     "info"
    ],
    "properties": {
     "type": {
      "$ref": "#/components/schemas/DocumentType"
     },
     "info": {
      "$ref": "#/components/schemas/BusinessInfo"
     }
    }
   }
  }
 },
 "paths": {
  "/api/status": {
   "get": {
    "operationId": "getStatus",
    "summary": "Which services are switched on (names only)",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "checkout": {
           "type": "boolean"
          },
          "webhook": {
           "type": "boolean"
          },
          "email": {
           "type": "boolean"
          },
          "degraded": {
           "type": "object"
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/law-monitor": {
   "get": {
    "operationId": "getLawMonitor",
    "summary": "The official law sources the monitor watches, the version last read, and real changes",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "sources": {
           "type": "array",
           "items": {
            "type": "object"
           }
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/services": {
   "get": {
    "operationId": "listServices",
    "summary": "Named third-party services: who runs each, what it receives, why, its privacy policy and the vendor page that says so",
    "description": "Static, no key. Put any name, id or alias in info.thirdPartyServices and the privacy and cookie policies print that entry; a name not listed is printed by name only, with no claim about what it receives.",
    "parameters": [
     {
      "name": "platform",
      "in": "query",
      "required": false,
      "schema": {
       "type": "string",
       "enum": [
        "web",
        "app"
       ]
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "services": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "id": {
              "type": "string"
             },
             "name": {
              "type": "string"
             },
             "company": {
              "type": "string"
             },
             "category": {
              "type": "string"
             },
             "receives": {
              "type": "string"
             },
             "purpose": {
              "type": "string"
             },
             "policyUrl": {
              "type": "string"
             },
             "source": {
              "type": "string"
             },
             "aliases": {
              "type": "array",
              "items": {
               "type": "string"
              }
             },
             "platforms": {
              "type": "array",
              "items": {
               "type": "string",
               "enum": [
                "web",
                "app"
               ]
              }
             }
            }
           }
          },
          "note": {
           "type": "string"
          }
         }
        }
       }
      }
     }
    }
   }
  },
  "/api/generate": {
   "post": {
    "operationId": "generateFree",
    "summary": "The website generator: no key needed",
    "description": "Anonymous callers are on the Free plan: one complete privacy policy per email for one site (with a LegalForge credit line); other types return a preview (isPreview true). The MCP tool generate_document without a key runs this same function.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "allOf": [
         {
          "$ref": "#/components/schemas/GenerateRequest"
         },
         {
          "type": "object",
          "properties": {
           "email": {
            "type": "string",
            "description": "Owner email for the site slot; defaults to info.email."
           }
          }
         }
        ]
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Document or preview",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "document": {
           "type": "string",
           "description": "Markdown"
          },
          "type": {
           "$ref": "#/components/schemas/DocumentType"
          },
          "isPreview": {
           "type": "boolean"
          },
          "reason": {
           "type": [
            "string",
            "null"
           ]
          },
          "plan": {
           "type": "string"
          },
          "generatedAt": {
           "type": "string",
           "format": "date-time"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/generate": {
   "post": {
    "operationId": "generateDocument",
    "summary": "Generate one complete document (Business plan)",
    "security": [
     {
      "apiKey": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "$ref": "#/components/schemas/GenerateRequest"
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "Complete document",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "document": {
           "type": "string"
          },
          "type": {
           "$ref": "#/components/schemas/DocumentType"
          },
          "format": {
           "const": "markdown"
          },
          "templateRevision": {
           "type": "string"
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/api/v1/bulk": {
   "post": {
    "operationId": "generateBulk",
    "summary": "Generate up to 50 documents in one call (Business plan)",
    "security": [
     {
      "apiKey": []
     }
    ],
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "items"
        ],
        "properties": {
         "items": {
          "type": "array",
          "minItems": 1,
          "maxItems": 50,
          "items": {
           "$ref": "#/components/schemas/GenerateRequest"
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "One entry per item: a document or an error",
      "content": {
       "application/json": {
        "schema": {
         "type": "object",
         "properties": {
          "results": {
           "type": "array",
           "items": {
            "type": "object",
            "properties": {
             "index": {
              "type": "integer"
             },
             "type": {
              "type": "string"
             },
             "host": {
              "type": "string"
             },
             "document": {
              "type": "string"
             },
             "error": {
              "type": "string"
             }
            }
           }
          }
         }
        }
       }
      }
     },
     "400": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "401": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     },
     "403": {
      "description": "Error",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/mcp": {
   "post": {
    "operationId": "mcp",
    "summary": "MCP server (Streamable HTTP, JSON-RPC 2.0, plain JSON answers)",
    "description": "Tools: list_document_types, generate_document. initialize, tools/list and list_document_types need no key; generate_document runs /api/generate without a key and /api/v1/generate with one. An invalid key is a 401 with WWW-Authenticate: Bearer realm=\"legalforge\".",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "jsonrpc",
         "method"
        ],
        "properties": {
         "jsonrpc": {
          "const": "2.0"
         },
         "id": {
          "type": [
           "string",
           "integer"
          ]
         },
         "method": {
          "type": "string"
         },
         "params": {
          "type": "object"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "JSON-RPC response"
     },
     "202": {
      "description": "Notification accepted"
     },
     "401": {
      "description": "Invalid API key"
     },
     "403": {
      "description": "Origin not allowed"
     }
    }
   }
  },
  "/api/store-forms": {
   "get": {
    "operationId": "storeFormsCatalog",
    "summary": "Which services carry Google Play / Apple App Privacy rows, and the sources of the store rules",
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     }
    }
   },
   "post": {
    "operationId": "storeForms",
    "summary": "Google Play Data safety CSV and Apple App Privacy checklist for the picked services",
    "description": "Static, no key, nothing recorded. Covers only the services passed, using each vendor's own disclosure (every row quotes it). play.csv is the Play Console's Import-from-CSV shape, or null with play.blocked saying what to answer. Not legal advice.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "services"
        ],
        "properties": {
         "services": {
          "type": "array",
          "maxItems": 20,
          "items": {
           "type": "string",
           "maxLength": 100
          }
         },
         "encryptedInTransit": {
          "type": "boolean"
         },
         "accountCreation": {
          "type": "string",
          "enum": [
           "none",
           "password",
           "other_auth",
           "password_and_other",
           "oauth"
          ]
         },
         "deletionRequestUrl": {
          "type": "string",
          "maxLength": 500
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "400": {
      "description": "invalid_json or invalid_services",
      "content": {
       "application/json": {
        "schema": {
         "$ref": "#/components/schemas/Error"
        }
       }
      }
     }
    }
   }
  },
  "/api/consent-banner": {
   "post": {
    "operationId": "consentBanner",
    "summary": "Cookie banner snippet generated from the same services the policy names",
    "description": "Static, no key, nothing recorded. Returns one inline HTML snippet (no request to any server, choice kept in the visitor's browser), the mode (opt-in or opt-out by country), the categories and services, and inStep (the cookie policy generated from the same answers carries the same banner reference). Send {check:{policy,snippet}} to compare a policy and an installed snippet instead. Not legal advice.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "properties": {
         "info": {
          "type": "object"
         },
         "cookiePolicyUrl": {
          "type": "string",
          "maxLength": 500
         },
         "mode": {
          "type": "string",
          "enum": [
           "opt-in",
           "opt-out"
          ]
         },
         "check": {
          "type": "object",
          "properties": {
           "policy": {
            "type": "string"
           },
           "snippet": {
            "type": "string"
           }
          }
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "400": {
      "description": "Invalid body"
     }
    }
   }
  },
  "/api/dpa": {
   "post": {
    "operationId": "dpa",
    "summary": "Data Processing Agreement draft and sub-processor table from the same services the policy names",
    "description": "Static, no key, nothing recorded. Returns a DPA draft (the eight points of GDPR Article 28(3), the owner as processor, a placeholder for the customer as controller), the sub-processor table (a picked service is one row restated from the vendor's own documentation; an unpicked service never appears; a name with no saved vendor page is listed by name only) and the cases that need a lawyer. A hosted copy serves the same table with a dated change log at /p/{docId}/subprocessors and the agreement at /p/{docId}/dpa. Not legal advice; it never claims the agreement makes anyone compliant.",
    "requestBody": {
     "required": true,
     "content": {
      "application/json": {
       "schema": {
        "type": "object",
        "required": [
         "info"
        ],
        "properties": {
         "info": {
          "type": "object"
         }
        }
       }
      }
     }
    },
    "responses": {
     "200": {
      "description": "OK",
      "content": {
       "application/json": {
        "schema": {
         "type": "object"
        }
       }
      }
     },
     "400": {
      "description": "Invalid body"
     }
    }
   }
  },
  "/e/{file}": {
   "get": {
    "operationId": "embed",
    "summary": "Paste-once embed of a hosted policy (docId.js or docId.html)",
    "description": "Returns the hosted policy as stored now: a script (docId.js) that inserts the text where the tag was pasted, or an HTML fragment (docId.html) for server-side includes. Cache-Control public, max-age 86400 (a visitor's browser keeps it 24 hours), s-maxage 300; ETag revalidation answers 304. A law-monitor update or an edit therefore reaches an embedding page without re-pasting. No cookie, no third-party script, nothing written per view.",
    "parameters": [
     {
      "name": "file",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string",
       "pattern": "^[A-Za-z0-9_-]{6,32}\\.(js|html)$"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "OK"
     },
     "304": {
      "description": "Not modified"
     },
     "404": {
      "description": "Unknown or removed document"
     }
    }
   }
  },
  "/p/{id}/subprocessors": {
   "get": {
    "operationId": "subprocessorsPage",
    "summary": "Public sub-processor page of a hosted document, with a dated change log",
    "parameters": [
     {
      "name": "id",
      "in": "path",
      "required": true,
      "schema": {
       "type": "string"
      }
     }
    ],
    "responses": {
     "200": {
      "description": "HTML table of the sub-processors and the change log"
     },
     "404": {
      "description": "Unknown document"
     }
    }
   }
  }
 }
}
