From 7f0478999775dbef30cc69f48f53483e622a2d4b Mon Sep 17 00:00:00 2001 From: DocFast Bot Date: Sat, 14 Feb 2026 14:31:44 +0000 Subject: [PATCH] Add proper API docs page, fix Stripe lazy init, update docs links --- public/docs.html | 394 ++++++++++++++++++++++++++++++++++++++++++ public/index.html | 4 +- src/index.ts | 5 + src/routes/billing.ts | 28 +-- src/routes/signup.ts | 2 +- 5 files changed, 419 insertions(+), 14 deletions(-) create mode 100644 public/docs.html diff --git a/public/docs.html b/public/docs.html new file mode 100644 index 0000000..e46817e --- /dev/null +++ b/public/docs.html @@ -0,0 +1,394 @@ + + + + + + DocFast API Documentation + + + +
+
+

DocFast API Documentation

+

Convert HTML, Markdown, and URLs to PDF. Built-in invoice & receipt templates.

+
Base URL: https://docfast.dev
+
+ + + +
+

Authentication

+

All conversion and template endpoints require an API key. Pass it in the Authorization header:

+
Authorization: Bearer df_free_your_api_key_here
+

Get a free API key instantly — no credit card required:

+
curl -X POST https://docfast.dev/v1/signup/free \
+  -H "Content-Type: application/json" \
+  -d '{"email": "you@example.com"}'
+
Free tier: 100 PDFs/month. Pro ($9/mo): 10,000 PDFs/month. Upgrade anytime at docfast.dev.
+
+ +
+

Convert HTML to PDF

+
+
+ POST + /v1/convert/html +
+

Convert raw HTML (with optional CSS) to a PDF document.

+ +

Request Body

+ + + + + + + +
FieldTypeDescription
html requiredstringHTML content to convert
cssstringAdditional CSS to inject
formatstringPage size: A4 (default), Letter, Legal, A3
landscapebooleanLandscape orientation (default: false)
marginobject{top, right, bottom, left} in CSS units (default: 20mm each)
+ +

Example

+
curl -X POST https://docfast.dev/v1/convert/html \
+  -H "Authorization: Bearer YOUR_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "html": "<h1>Hello World</h1><p>Generated by DocFast.</p>",
+    "css": "h1 { color: navy; }",
+    "format": "A4"
+  }' \
+  -o output.pdf
+ +

Response

+

200 OK — Returns the PDF as application/pdf binary stream.

+
+ 200 PDF generated + 400 Missing html field + 401 Invalid/missing API key + 429 Rate limited +
+
+
+ +
+

Convert Markdown to PDF

+
+
+ POST + /v1/convert/markdown +
+

Convert Markdown to a styled PDF with syntax highlighting for code blocks.

+ +

Request Body

+ + + + + + + +
FieldTypeDescription
markdown requiredstringMarkdown content
cssstringAdditional CSS to inject
formatstringPage size (default: A4)
landscapebooleanLandscape orientation
marginobjectCustom margins
+ +

Example

+
curl -X POST https://docfast.dev/v1/convert/markdown \
+  -H "Authorization: Bearer YOUR_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "markdown": "# Monthly Report\n\n## Summary\n\nRevenue increased by **15%** this quarter.\n\n| Metric | Value |\n|--------|-------|\n| Users  | 1,234 |\n| MRR    | $5,670 |"
+  }' \
+  -o report.pdf
+ +

Response

+

200 OK — Returns application/pdf.

+
+ 200 PDF generated + 400 Missing markdown field + 401 Invalid/missing API key +
+
+
+ +
+

Convert URL to PDF

+
+
+ POST + /v1/convert/url +
+

Navigate to a URL and convert the rendered page to PDF. Supports JavaScript-rendered pages.

+ +

Request Body

+ + + + + + + +
FieldTypeDescription
url requiredstringURL to convert (must start with http:// or https://)
waitUntilstringload (default), domcontentloaded, networkidle0, networkidle2
formatstringPage size (default: A4)
landscapebooleanLandscape orientation
marginobjectCustom margins
+ +

Example

+
curl -X POST https://docfast.dev/v1/convert/url \
+  -H "Authorization: Bearer YOUR_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "url": "https://example.com",
+    "waitUntil": "networkidle0",
+    "format": "Letter"
+  }' \
+  -o page.pdf
+ +

Response

+

200 OK — Returns application/pdf.

+
+ 200 PDF generated + 400 Missing or invalid URL + 401 Invalid/missing API key +
+
+
+ +
+

List Templates

+
+
+ GET + /v1/templates +
+

List all available document templates with their field definitions.

+ +

Example

+
curl https://docfast.dev/v1/templates \
+  -H "Authorization: Bearer YOUR_KEY"
+ +

Response

+
{
+  "templates": [
+    {
+      "id": "invoice",
+      "name": "Invoice",
+      "description": "Professional invoice with line items, taxes, and payment details",
+      "fields": [
+        {"name": "invoiceNumber", "type": "string", "required": true},
+        {"name": "date", "type": "string", "required": true},
+        {"name": "from", "type": "object", "required": true, "description": "Sender: {name, address?, email?, phone?, vatId?}"},
+        {"name": "to", "type": "object", "required": true, "description": "Recipient: {name, address?, email?, vatId?}"},
+        {"name": "items", "type": "array", "required": true, "description": "Line items: [{description, quantity, unitPrice, taxRate?}]"},
+        {"name": "currency", "type": "string", "required": false},
+        {"name": "notes", "type": "string", "required": false},
+        {"name": "paymentDetails", "type": "string", "required": false}
+      ]
+    },
+    {
+      "id": "receipt",
+      "name": "Receipt",
+      "description": "Simple receipt for payments received",
+      "fields": [ ... ]
+    }
+  ]
+}
+
+
+ +
+

Render Template

+
+
+ POST + /v1/templates/:id/render +
+

Render a template with your data and get a PDF. No HTML needed — just pass structured data.

+ +

Path Parameters

+ + + +
ParamDescription
:idTemplate ID (invoice or receipt)
+ +

Request Body

+ + + +
FieldTypeDescription
data requiredobjectTemplate data (see field definitions from /v1/templates)
+ +

Invoice Example

+
curl -X POST https://docfast.dev/v1/templates/invoice/render \
+  -H "Authorization: Bearer YOUR_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{
+    "data": {
+      "invoiceNumber": "INV-2026-001",
+      "date": "2026-02-14",
+      "dueDate": "2026-03-14",
+      "from": {
+        "name": "Acme Corp",
+        "address": "123 Main St, Vienna",
+        "email": "billing@acme.com",
+        "vatId": "ATU12345678"
+      },
+      "to": {
+        "name": "Client Inc",
+        "address": "456 Oak Ave, Berlin",
+        "email": "accounts@client.com"
+      },
+      "items": [
+        {"description": "Web Development", "quantity": 40, "unitPrice": 95, "taxRate": 20},
+        {"description": "Hosting (monthly)", "quantity": 1, "unitPrice": 29}
+      ],
+      "currency": "€",
+      "notes": "Payment due within 30 days.",
+      "paymentDetails": "IBAN: AT12 3456 7890 1234 5678"
+    }
+  }' \
+  -o invoice.pdf
+ +

Response

+

200 OK — Returns application/pdf.

+
+ 200 PDF generated + 400 Missing data field + 404 Template not found + 401 Invalid/missing API key +
+
+
+ +
+

Sign Up (Get API Key)

+
+
+ POST + /v1/signup/free +
+

Get a free API key instantly. No authentication required.

+ +

Request Body

+ + + +
FieldTypeDescription
email requiredstringYour email address
+ +

Example

+
curl -X POST https://docfast.dev/v1/signup/free \
+  -H "Content-Type: application/json" \
+  -d '{"email": "dev@example.com"}'
+ +

Response

+
{
+  "message": "Welcome to DocFast! 🚀",
+  "apiKey": "df_free_abc123...",
+  "tier": "free",
+  "limit": "100 PDFs/month",
+  "docs": "https://docfast.dev/#endpoints"
+}
+
Save your API key immediately — it won't be shown again.
+
+
+ +
+

Error Handling

+

All errors return JSON with an error field:

+
{
+  "error": "Missing 'html' field"
+}
+ +

Status Codes

+ + + + + + + + +
CodeMeaning
200Success — PDF returned as binary stream
400Bad request — missing or invalid parameters
401Unauthorized — missing or invalid API key
404Not found — invalid endpoint or template ID
429Rate limited — too many requests (100/min)
500Server error — PDF generation failed
+ +

Common Mistakes

+
# ❌ Missing Authorization header
+curl -X POST https://docfast.dev/v1/convert/html \
+  -d '{"html": "test"}'
+# → {"error": "Missing API key. Use: Authorization: Bearer <key>"}
+
+# ❌ Wrong Content-Type
+curl -X POST https://docfast.dev/v1/convert/html \
+  -H "Authorization: Bearer YOUR_KEY" \
+  -d '{"html": "test"}'
+# → Make sure to include -H "Content-Type: application/json"
+
+# ✅ Correct request
+curl -X POST https://docfast.dev/v1/convert/html \
+  -H "Authorization: Bearer YOUR_KEY" \
+  -H "Content-Type: application/json" \
+  -d '{"html": "<h1>Hello</h1>"}' \
+  -o output.pdf
+
+ + +
+ + diff --git a/public/index.html b/public/index.html index ec99b8d..b7fd8fc 100644 --- a/public/index.html +++ b/public/index.html @@ -92,7 +92,7 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0.

One API call. Beautiful PDFs. Built-in invoice templates. No headless browser setup, no dependencies, no hassle.

- View Docs + View Docs
// Convert markdown to PDF in one call
@@ -233,7 +233,7 @@ footer { padding: 40px 0; text-align: center; color: var(--muted); font-size: 0.

Here's your API key. Save it now — it won't be shown again.

Click to copy
-

100 free PDFs/month • All endpoints • View docs →

+

100 free PDFs/month • All endpoints • View docs →

diff --git a/src/index.ts b/src/index.ts index 01e60ed..dfbafdf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -53,6 +53,11 @@ app.get("/v1/usage", authMiddleware, (_req, res) => { const __dirname = path.dirname(fileURLToPath(import.meta.url)); app.use(express.static(path.join(__dirname, "../public"))); +// Docs page (clean URL) +app.get("/docs", (_req, res) => { + res.sendFile(path.join(__dirname, "../public/docs.html")); +}); + // API root app.get("/api", (_req, res) => { res.json({ diff --git a/src/routes/billing.ts b/src/routes/billing.ts index 31298f9..16f8031 100644 --- a/src/routes/billing.ts +++ b/src/routes/billing.ts @@ -2,9 +2,15 @@ import { Router, Request, Response } from "express"; import Stripe from "stripe"; import { createProKey, revokeByCustomer } from "../services/keys.js"; -const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", { - apiVersion: "2025-01-27.acacia" as any, -}); +let _stripe: Stripe | null = null; +function getStripe(): Stripe { + if (!_stripe) { + const key = process.env.STRIPE_SECRET_KEY; + if (!key) throw new Error("STRIPE_SECRET_KEY not configured"); + _stripe = new Stripe(key, { apiVersion: "2025-01-27.acacia" as any }); + } + return _stripe; +} const router = Router(); @@ -13,7 +19,7 @@ router.post("/checkout", async (_req: Request, res: Response) => { try { const priceId = await getOrCreateProPrice(); - const session = await stripe.checkout.sessions.create({ + const session = await getStripe().checkout.sessions.create({ mode: "subscription", payment_method_types: ["card"], line_items: [{ price: priceId, quantity: 1 }], @@ -37,7 +43,7 @@ router.get("/success", async (req: Request, res: Response) => { } try { - const session = await stripe.checkout.sessions.retrieve(sessionId); + const session = await getStripe().checkout.sessions.retrieve(sessionId); const customerId = session.customer as string; const email = session.customer_details?.email || "unknown@docfast.dev"; @@ -66,7 +72,7 @@ a { color: #4f9; }
${keyInfo.key}

Save this key! It won't be shown again.

10,000 PDFs/month • All endpoints • Priority support

-

View API docs →

+

View API docs →

`); } catch (err: any) { console.error("Success page error:", err.message); @@ -83,7 +89,7 @@ router.post("/webhook", async (req: Request, res: Response) => { if (webhookSecret && sig) { try { - event = stripe.webhooks.constructEvent(req.body, sig, webhookSecret); + event = getStripe().webhooks.constructEvent(req.body, sig, webhookSecret); } catch (err: any) { console.error("Webhook signature verification failed:", err.message); res.status(400).json({ error: "Invalid signature" }); @@ -114,25 +120,25 @@ let cachedPriceId: string | null = null; async function getOrCreateProPrice(): Promise { if (cachedPriceId) return cachedPriceId; - const products = await stripe.products.search({ query: "name:'DocFast Pro'" }); + const products = await getStripe().products.search({ query: "name:'DocFast Pro'" }); let productId: string; if (products.data.length > 0) { productId = products.data[0].id; - const prices = await stripe.prices.list({ product: productId, active: true, limit: 1 }); + const prices = await getStripe().prices.list({ product: productId, active: true, limit: 1 }); if (prices.data.length > 0) { cachedPriceId = prices.data[0].id; return cachedPriceId; } } else { - const product = await stripe.products.create({ + const product = await getStripe().products.create({ name: "DocFast Pro", description: "Unlimited PDF conversions via API. HTML, Markdown, and URL to PDF.", }); productId = product.id; } - const price = await stripe.prices.create({ + const price = await getStripe().prices.create({ product: productId, unit_amount: 900, currency: "usd", diff --git a/src/routes/signup.ts b/src/routes/signup.ts index acfd013..02c7cca 100644 --- a/src/routes/signup.ts +++ b/src/routes/signup.ts @@ -25,7 +25,7 @@ router.post("/free", (req: Request, res: Response) => { apiKey: keyInfo.key, tier: "free", limit: "100 PDFs/month", - docs: "https://docfast.dev/#endpoints", + docs: "https://docfast.dev/docs", note: "Save this API key — it won't be shown again.", }); });