File size: 16,876 Bytes
bcf46c3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>API Documentation — OpticParse & PhishVision</title>
  <meta name="description" content="Developer API documentation for OpticParse (AI Scraper) and PhishVision (AI Phishing Scan).">
  <link rel="preconnect" href="https://fonts.googleapis.com" />
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
  <link rel="preload" as="style" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500&display=swap" onload="this.onload=null;this.rel='stylesheet'" />
  <noscript>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500&display=swap" />
  </noscript>
  <style>
    *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
    :root {
      --bg: #0a0a12; --bg2: #0f0f1a; --bg3: #13131f;
      --border: rgba(255,255,255,0.07); --text: #e8e8f0; --muted: #a1a1aa;
      --purple: #8b5cf6; --purple2: #6d28d9; --cyan: #06b6d4; --cyan2: #0891b2;
    }
    body { font-family: 'Inter', sans-serif; background: var(--bg); color: var(--text); line-height: 1.6; display: flex; min-height: 100vh; }
    a { color: var(--purple); text-decoration: none; transition: color 0.2s; }
    a:hover { color: var(--text); }

    /* LAYOUT */
    aside { width: 280px; background: var(--bg2); border-right: 1px solid var(--border); padding: 2rem 1.5rem; position: fixed; top: 0; bottom: 0; left: 0; overflow-y: auto; z-index: 10; }
    main { margin-left: 280px; flex: 1; padding: 4rem 3rem; max-width: 900px; }

    /* NAV LOGO */
    .nav-logo { font-weight: 800; font-size: 1.1rem; letter-spacing: -0.5px; margin-bottom: 2rem; }
    .nav-logo span { background: linear-gradient(135deg, var(--purple), var(--cyan)); -webkit-background-clip: text; -webkit-text-fill-color: transparent; }

    /* SIDEBAR NAV */
    .nav-sec { margin-bottom: 2rem; }
    .nav-sec-title { font-size: 0.75rem; font-weight: 800; text-transform: uppercase; color: var(--muted); letter-spacing: 1px; margin-bottom: 0.8rem; }
    .nav-sec ul { list-style: none; display: flex; flex-direction: column; gap: 0.5rem; }
    .nav-sec a { color: var(--muted); font-size: 0.88rem; font-weight: 500; display: block; padding: 0.25rem 0; }
    .nav-sec a:hover, .nav-sec a.active { color: var(--text); }
    .method-tag { font-size: 0.65rem; font-weight: 800; padding: 0.15rem 0.4rem; border-radius: 4px; margin-right: 0.4rem; font-family: 'JetBrains Mono', monospace; display: inline-block; vertical-align: middle; }
    .method-post { background: rgba(16,185,129,0.15); color: #10b981; }
    .method-get { background: rgba(59,130,246,0.15); color: #3b82f6; }
    .method-delete { background: rgba(239,68,68,0.15); color: #ef4444; }

    /* CONTENT */
    h1 { font-size: 2.2rem; font-weight: 900; letter-spacing: -1px; margin-bottom: 0.5rem; }
    .lead { font-size: 1.1rem; color: var(--muted); margin-bottom: 3rem; }
    section { margin-bottom: 4rem; padding-top: 2rem; border-top: 1px solid var(--border); }
    h2 { font-size: 1.6rem; font-weight: 800; letter-spacing: -0.5px; margin-bottom: 1rem; }
    h3 { font-size: 1.1rem; font-weight: 700; margin: 1.5rem 0 0.8rem; }
    p { color: var(--muted); margin-bottom: 1rem; font-size: 0.95rem; }

    /* TABLES */
    table { width: 100%; border-collapse: collapse; margin: 1.5rem 0; font-size: 0.88rem; }
    th { text-align: left; padding: 0.75rem 1rem; border-bottom: 2px solid var(--border); color: var(--muted); font-weight: 700; }
    td { padding: 0.75rem 1rem; border-bottom: 1px solid var(--border); }
    .param-name { font-family: 'JetBrains Mono', monospace; font-weight: 600; color: var(--text); }
    .param-type { font-family: 'JetBrains Mono', monospace; font-size: 0.78rem; color: var(--purple); }
    .param-req { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; color: #ef4444; }
    .param-opt { font-size: 0.7rem; font-weight: 700; text-transform: uppercase; color: var(--muted); }

    /* CODE BLOCKS */
    pre { background: var(--bg2); border: 1px solid var(--border); border-radius: 12px; padding: 1.2rem; font-family: 'JetBrains Mono', monospace; font-size: 0.78rem; line-height: 1.6; overflow-x: auto; color: #c4c4e0; margin: 1rem 0 2rem; }
    .cmt { color: #4b5563; }
    .str { color: #6ee7b7; }
    .key { color: #7dd3fc; }
    .val { color: #fde68a; }

    /* BACK TO HOME */
    .back-home { font-size: 0.85rem; font-weight: 600; display: inline-flex; align-items: center; gap: 0.4rem; margin-bottom: 1rem; }
  </style>
</head>
<body>

  <aside>
    <div class="nav-logo">
      <a href="/"><span>Opticparse</span> & PhishVision</a>
    </div>
    
    <nav>
      <div class="nav-sec">
        <div class="nav-sec-title">Getting Started</div>
        <ul>
          <li><a href="#auth">Authentication</a></li>
          <li><a href="#errors">Error Codes</a></li>
        </ul>
      </div>

      <div class="nav-sec">
        <div class="nav-sec-title">OpticParse API</div>
        <ul>
          <li><a href="#op-scrape"><span class="method-tag method-post">POST</span>vision-scrape</a></li>
          <li><a href="#op-crawl"><span class="method-tag method-post">POST</span>crawl</a></li>
          <li><a href="#op-watch"><span class="method-tag method-post">POST</span>watch</a></li>
          <li><a href="#op-batch"><span class="method-tag method-post">POST</span>batch</a></li>
        </ul>
      </div>

      <div class="nav-sec">
        <div class="nav-sec-title">PhishVision API</div>
        <ul>
          <li><a href="#pv-detect"><span class="method-tag method-post">POST</span>phish-detect</a></li>
          <li><a href="#pv-batch"><span class="method-tag method-post">POST</span>phish-batch</a></li>
          <li><a href="#pv-report"><span class="method-tag method-get">GET</span>phish-report</a></li>
          <li><a href="#pv-monitor"><span class="method-tag method-post">POST</span>monitor</a></li>
        </ul>
      </div>
    </nav>
  </aside>

  <main>
    <a href="/" class="back-home">← Back to Homepage</a>
    <h1>API Reference Documentation</h1>
    <p class="lead">Welcome to the complete developer documentation. Integrate vision-powered dynamic scraping and automated phishing forensics into your apps in minutes.</p>

    <!-- AUTHENTICATION -->
    <section id="auth">
      <h2>Authentication</h2>
      <p>All API requests to production endpoints require authentication using a custom header. Create your keys in the developer dashboard.</p>
      <table>
        <thead>
          <tr>
            <th>Header</th>
            <th>Type</th>
            <th>Description</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td class="param-name">X-API-Key</td>
            <td class="param-type">string</td>
            <td>Your active production API Key. Starts with the prefix <code style="font-family:'JetBrains Mono',monospace;">op_live_</code>.</td>
          </tr>
        </tbody>
      </table>
      <pre><span class="cmt"># Authenticating with cURL</span>
curl -H <span class="str">"X-API-Key: op_live_your_actual_key_here"</span> \\
     https://opticparse-python-sg.onrender.com/health</pre>
    </section>

    <!-- ERROR CODES -->
    <section id="errors">
      <h2>Error Codes</h2>
      <p>Our APIs return standardized HTTP status codes for errors, with JSON detail bodies explaining the root cause.</p>
      <table>
        <thead>
          <tr>
            <th>Code</th>
            <th>Status</th>
            <th>Description / Cause</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td class="param-name">400</td>
            <td>Bad Request</td>
            <td>Invalid parameters, payload too large, or malformed queries.</td>
          </tr>
          <tr>
            <td class="param-name">401</td>
            <td>Unauthorized</td>
            <td>Invalid or missing API key. Key format must check out.</td>
          </tr>
          <tr>
            <td class="param-name">429</td>
            <td>Rate Limit Exceeded</td>
            <td>Monthly request usage quota exceeded, or too many concurrent requests.</td>
          </tr>
          <tr>
            <td class="param-name">500</td>
            <td>Internal Server Error</td>
            <td>AI reasoning error, Playwright navigation failure, or server-side issue.</td>
          </tr>
        </tbody>
      </table>
    </section>

    <!-- OPTICPARSE SCRAPE -->
    <section id="op-scrape">
      <h2>POST /api/vision-scrape</h2>
      <p>Analyze a web page visually using headful browser rendering and return structured data according to your query and JSON schema.</p>
      
      <h3>Request Body</h3>
      <table>
        <thead>
          <tr>
            <th>Field</th>
            <th>Type</th>
            <th>Required</th>
            <th>Description</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td class="param-name">target_url</td>
            <td class="param-type">string</td>
            <td><span class="param-req">required</span></td>
            <td>The full HTTP/HTTPS URL of the target webpage to scrape. Resolves only public URLs.</td>
          </tr>
          <tr>
            <td class="param-name">extraction_query</td>
            <td class="param-type">string</td>
            <td><span class="param-req">required</span></td>
            <td>Instructions in plain English describing what data fields you want to extract.</td>
          </tr>
          <tr>
            <td class="param-name">response_schema</td>
            <td class="param-type">object</td>
            <td><span class="param-opt">optional</span></td>
            <td>A raw JSON schema dict defining exactly how the returned JSON object must be shaped.</td>
          </tr>
          <tr>
            <td class="param-name">wait_until</td>
            <td class="param-type">string</td>
            <td><span class="param-opt">optional</span></td>
            <td>Browser wait condition. Allowed: <code style="font-family:'JetBrains Mono',monospace;">load</code> (default), <code style="font-family:'JetBrains Mono',monospace;">domcontentloaded</code>, <code style="font-family:'JetBrains Mono',monospace;">networkidle</code>.</td>
          </tr>
          <tr>
            <td class="param-name">timeout</td>
            <td class="param-type">integer</td>
            <td><span class="param-opt">optional</span></td>
            <td>Browser navigation timeout in ms. Default 30,000 (30 seconds). Max 60,000.</td>
          </tr>
        </tbody>
      </table>

      <pre><span class="cmt"># Example Request</span>
curl -X POST \\
  https://opticparse-python-sg.onrender.com/api/vision-scrape \\
  -H <span class="str">"X-API-Key: YOUR_API_KEY"</span> \\
  -H <span class="str">"Content-Type: application/json"</span> \\
  -d <span class="str">'{
    "target_url": "https://news.ycombinator.com",
    "extraction_query": "Extract the top story title and point score"
  }'</span></pre>
    </section>

    <!-- OPTICPARSE CRAWL -->
    <section id="op-crawl">
      <h2>POST /api/crawl</h2>
      <p>Crawl multiple pages of a website by following a CSS selector (e.g. "Next" pagination buttons) and extract structured data incrementally.</p>
      
      <h3>Request Body</h3>
      <table>
        <thead>
          <tr>
            <th>Field</th>
            <th>Type</th>
            <th>Required</th>
            <th>Description</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td class="param-name">start_url</td>
            <td class="param-type">string</td>
            <td><span class="param-req">required</span></td>
            <td>The starting URL of the crawl.</td>
          </tr>
          <tr>
            <td class="param-name">extraction_query</td>
            <td class="param-type">string</td>
            <td><span class="param-req">required</span></td>
            <td>The prompt detailing what structured data to collect from each page.</td>
          </tr>
          <tr>
            <td class="param-name">follow_selector</td>
            <td class="param-type">string</td>
            <td><span class="param-req">required</span></td>
            <td>CSS selector for the pagination button to click to advance to the next page.</td>
          </tr>
          <tr>
            <td class="param-name">max_pages</td>
            <td class="param-type">integer</td>
            <td><span class="param-opt">optional</span></td>
            <td>Maximum number of pages to crawl. Default 5. Max 20.</td>
          </tr>
        </tbody>
      </table>
    </section>

    <!-- OPTICPARSE WATCH -->
    <section id="op-watch">
      <h2>POST /api/watch</h2>
      <p>Establish a periodic monitoring task for a webpage. Compares visual states over time and fires a webhook when structural differences are detected.</p>
    </section>

    <!-- OPTICPARSE BATCH -->
    <section id="op-batch">
      <h2>POST /api/batch</h2>
      <p>Scrape up to 20 target URLs concurrently. Distributes tasks across browser runners to bypass standard sequential latencies.</p>
    </section>

    <!-- PHISHVISION DETECT -->
    <section id="pv-detect">
      <h2>POST /api/phish-detect</h2>
      <p>Visit a target URL, render the visual state, analyze it via our multimodal brand impersonation rules, and output threat forensics.</p>
      
      <h4>Parameters</h4>
      <table>
        <thead>
          <tr><th>Field</th><th>Type</th><th>Description</th></tr>
        </thead>
        <tbody>
          <tr><td><code>url</code></td><td>string</td><td>The target webpage to analyze. Must start with http:// or https://.</td></tr>
          <tr><td><code>dry_run</code></td><td>boolean (optional)</td><td>If <code>true</code>, bypasses AI analysis and instantly returns the raw base64 screenshot and text payload. Useful for tuning thresholds without consuming AI tokens. Default is <code>false</code>.</td></tr>
        </tbody>
      </table>

      <pre><span class="cmt"># Example Request</span>
curl -X POST \\
  https://opticparse-1opticparse-node-sg.onrender.com/api/phish-detect \\
  -H <span class="str">"X-API-Key: YOUR_API_KEY"</span> \\
  -H <span class="str">"Content-Type: application/json"</span> \\
  -d <span class="str">'{ "url": "https://example.com", "dry_run": false }'</span></pre>
      
      <pre><span class="cmt"># Example Response</span>
{
  <span class="str">"verdict"</span>: <span class="str">"malicious"</span>,
  <span class="str">"confidence_score_percentage"</span>: 98,
  <span class="str">"impersonated_brand"</span>: <span class="str">"Microsoft"</span>,
  <span class="str">"threat_type"</span>: <span class="str">"brand_impersonation"</span>,
  <span class="str">"visual_anomalies_detected"</span>: [<span class="str">"Mismatched logo aspect ratio"</span>],
  <span class="str">"hidden_payload_detected"</span>: <span class="kw">null</span>,
  <span class="str">"javascript_threats"</span>: [],
  <span class="str">"redirect_risk"</span>: <span class="str">"High (3 hops through bit.ly)"</span>,
  <span class="str">"domain_age_days"</span>: 2,
  <span class="str">"registrar"</span>: <span class="str">"Namecheap"</span>,
  <span class="str">"cached"</span>: <span class="kw">false</span>
}</pre>
    </section>

    <!-- PHISHVISION BATCH -->
    <section id="pv-batch">
      <h2>POST /api/phish-batch</h2>
      <p>Scan a list of URLs concurrently for visual threat analysis.</p>
    </section>

    <!-- PHISHVISION REPORT -->
    <section id="pv-report">
      <h2>GET /api/phish-report</h2>
      <p>Download a detailed, brand-impersonation investigation PDF report for a previously scanned URL.</p>
    </section>

    <!-- PHISHVISION MONITOR -->
    <section id="pv-monitor">
      <h2>POST /api/monitor</h2>
      <p>Create a scheduled checker that periodically visits a target URL to check for malicious payloads, domain redirection anomalies, or visual phishing signs.</p>
    </section>
  </main>

  <script>
    // Smooth scrolling nav highlights
    window.addEventListener('DOMContentLoaded', () => {
      const observer = new IntersectionObserver(entries => {
        entries.forEach(entry => {
          const id = entry.target.getAttribute('id');
          if (entry.intersectionRatio > 0.5) {
            document.querySelectorAll('nav a').forEach(a => a.classList.remove('active'));
            document.querySelector(`nav a[href="#${id}"]`)?.classList.add('active');
          }
        });
      });

      document.querySelectorAll('section').forEach(section => {
        observer.observe(section);
      });
    });
  </script>

</body>
</html>