{"openapi":"3.0.3","info":{"title":"ProtoPM Public API","description":"Machine-readable description of Proto Project Management's public HTTP endpoints. Covers health, free construction PDF widgets (invoice and status report), widget quota, and feature-request intake. Authenticated app APIs (projects, workspaces, billing, Bid Board, etc.) require a signed-in session and are not published in this catalog. Human docs: https://www.protopm.app/developers","version":"1.0.0","contact":{"name":"ProtoPM Support","url":"https://www.protopm.app/support"}},"servers":[{"url":"https://www.protopm.app","description":"ProtoPM production"}],"tags":[{"name":"Health","description":"Liveness and database probe"},{"name":"Tools","description":"Free invoice and status-report PDF widgets"},{"name":"Support","description":"Public feature-request intake"}],"paths":{"/api/health":{"get":{"tags":["Health"],"summary":"Liveness and database probe","description":"Public health check used by uptime monitors and Render. Returns 200 when the database is reachable; 503 otherwise.","operationId":"getHealth","responses":{"200":{"description":"Service healthy","content":{"application/json":{"schema":{"type":"object","properties":{"ok":{"type":"boolean","example":true},"status":{"type":"string","example":"healthy"},"database":{"type":"string","example":"connected"},"timestamp":{"type":"string","format":"date-time"}}}}}},"503":{"description":"Database unavailable"}}}},"/api/tools/quota":{"get":{"tags":["Tools"],"summary":"Remaining free-widget PDF quota","description":"Returns remaining PDF generations for invoice and status-report widgets for the current visitor (anonymous cookie and/or session).","operationId":"getToolsQuota","responses":{"200":{"description":"Quota snapshots per widget","content":{"application/json":{"schema":{"type":"object","properties":{"widgets":{"type":"object","additionalProperties":true}}}}}}}}},"/api/tools/invoice-pdf":{"post":{"tags":["Tools"],"summary":"Generate a construction invoice PDF","description":"Builds a ProtoPM-branded invoice PDF. Rate-limited and quota-gated (anonymous / Free / Pro). Form values are not stored server-side. Prefer the HTML widget at /tools/invoice for interactive use.","operationId":"postToolsInvoicePdf","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["invoiceNumber","dateIssued","fromName","projectTitle","lineItems"],"properties":{"invoiceNumber":{"type":"string","maxLength":64},"dateIssued":{"type":"string","maxLength":32},"dueDate":{"type":"string","nullable":true},"status":{"type":"string","enum":["PAID","NOT_PAID"],"default":"NOT_PAID"},"fromName":{"type":"string"},"fromEmail":{"type":"string"},"fromAddress":{"type":"string","nullable":true},"projectTitle":{"type":"string"},"projectID":{"type":"string"},"billToEmail":{"type":"string"},"billToAddress":{"type":"string","nullable":true},"currency":{"type":"string","minLength":3,"maxLength":3,"default":"CAD"},"paymentTerms":{"type":"string","nullable":true},"referenceNumber":{"type":"string","nullable":true},"taxAmount":{"type":"number","nullable":true},"retainageAmount":{"type":"number","nullable":true},"notes":{"type":"string","nullable":true},"lineItems":{"type":"array","minItems":1,"maxItems":20,"items":{"type":"object","required":["description","amount"],"properties":{"description":{"type":"string"},"quantity":{"type":"number"},"unitPrice":{"type":"number"},"amount":{"type":"number"}}}}}}}}},"responses":{"200":{"description":"PDF bytes","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid body"},"401":{"description":"Quota exceeded — signup required"},"402":{"description":"Quota exceeded — Pro required"},"429":{"description":"Rate limited"}}}},"/api/tools/status-report-pdf":{"post":{"tags":["Tools"],"summary":"Generate a construction status-report PDF","description":"Builds a one-page status report PDF. Same quota and rate-limit rules as invoice-pdf. Prefer /tools/status-report for interactive use.","operationId":"postToolsStatusReportPdf","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["projectTitle","status"],"properties":{"projectTitle":{"type":"string"},"projectID":{"type":"string","nullable":true},"phase":{"type":"string","nullable":true},"status":{"type":"string"},"location":{"type":"string","nullable":true},"reportDate":{"type":"string","nullable":true},"preparedBy":{"type":"string","nullable":true},"budget":{"type":"number","nullable":true},"actuals":{"type":"number","nullable":true},"forecast":{"type":"number","nullable":true},"currency":{"type":"string","minLength":3,"maxLength":3,"default":"CAD"},"summary":{"type":"string","nullable":true},"milestones":{"type":"array","maxItems":10,"items":{"type":"object","properties":{"title":{"type":"string"},"date":{"type":"string","nullable":true},"status":{"type":"string","nullable":true}}}}}}}}},"responses":{"200":{"description":"PDF bytes","content":{"application/pdf":{"schema":{"type":"string","format":"binary"}}}},"400":{"description":"Invalid body"},"401":{"description":"Quota exceeded — signup required"},"402":{"description":"Quota exceeded — Pro required"},"429":{"description":"Rate limited"}}}},"/api/support/feature-request":{"post":{"tags":["Support"],"summary":"Submit a product feature request","description":"Emails the support inbox. Honeypot field (website) must be empty. Rate-limited. Field values are not stored in the database.","operationId":"postFeatureRequest","requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","required":["email","title","details"],"properties":{"name":{"type":"string","maxLength":100},"email":{"type":"string","format":"email","maxLength":254},"title":{"type":"string","maxLength":120},"details":{"type":"string","maxLength":2000},"website":{"type":"string","description":"Honeypot — leave empty"}}}}}},"responses":{"200":{"description":"Request accepted (or honeypot silent success)"},"400":{"description":"Invalid body"},"429":{"description":"Rate limited"},"503":{"description":"Mail not configured or send failed"}}}}}}