extractia-sdk 1.0.6 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,9 +1,33 @@
1
1
  # Extractia SDK
2
2
 
3
- A JavaScript SDK for interacting with the Extractia API.
3
+ JavaScript SDK for the [Extractia](https://extractia.info) document-extraction API.
4
+ Works in Node.js ≥ 18 and modern browsers via the provided UMD build.
4
5
 
5
- > **Note:** This SDK is designed for the implementation of the [extractia.info](https://extractia.info) public API.
6
- > You must have an Extractia account and a valid API token to use it.
6
+ > **Requires** an Extractia account and a valid API token.
7
+ > Generate one at **Settings API Keys** in the Extractia dashboard.
8
+
9
+ ---
10
+
11
+ ## Table of Contents
12
+
13
+ 1. [Installation](#installation)
14
+ 2. [Quick Start](#quick-start)
15
+ 3. [Authentication](#authentication)
16
+ 4. [Error Handling](#error-handling)
17
+ 5. [API Reference](#api-reference)
18
+ - [Profile & Webhook](#profile--webhook)
19
+ - [Templates](#templates)
20
+ - [Documents](#documents)
21
+ - [Processing Images](#processing-images)
22
+ - [AI Features](#ai-features)
23
+ - [Exports](#exports)
24
+ - [OCR Tools](#ocr-tools)
25
+ - [Credits & Analytics](#credits--analytics)
26
+ 6. [TypeScript](#typescript)
27
+ 7. [Rate Limits & Quotas](#rate-limits--quotas)
28
+ 8. [Changelog](#changelog)
29
+
30
+ ---
7
31
 
8
32
  ## Installation
9
33
 
@@ -11,58 +35,784 @@ A JavaScript SDK for interacting with the Extractia API.
11
35
  npm install extractia-sdk
12
36
  ```
13
37
 
14
- ## Usage
38
+ Or using yarn / pnpm:
39
+
40
+ ```sh
41
+ yarn add extractia-sdk
42
+ pnpm add extractia-sdk
43
+ ```
44
+
45
+ **Browser (IIFE build):**
46
+
47
+ ```html
48
+ <script src="https://unpkg.com/extractia-sdk/dist/extractia-sdk.browser.js"></script>
49
+ <script>
50
+ ExtractiaSDK.default.setToken("YOUR_API_TOKEN");
51
+ </script>
52
+ ```
53
+
54
+ ---
55
+
56
+ ## Quick Start
15
57
 
16
58
  ```js
17
- import { setToken, getMyProfile, getTemplates, processImage } from 'extractia-sdk';
59
+ import { setToken, suggestFields, createTemplate, processImage } from "extractia-sdk";
18
60
 
19
- // Set your API token
20
- setToken('YOUR_API_TOKEN');
61
+ // 1. Authenticate
62
+ setToken("ext_YOUR_API_TOKEN_HERE");
21
63
 
22
- // Get user profile
23
- const profile = await getMyProfile();
64
+ // 2. Let AI suggest fields for your document type
65
+ const fields = await suggestFields("Invoice", "header data plus all line items with product and quantity");
24
66
 
25
- // Get all templates
26
- const templates = await getTemplates();
67
+ // 3. Create a template from the suggestions
68
+ const template = await createTemplate({ label: "Invoice", fields });
69
+
70
+ // 4. Process an image
71
+ import { readFileSync } from "fs";
72
+ const base64 = readFileSync("./invoice.png").toString("base64");
73
+ const doc = await processImage(template.id, base64);
74
+
75
+ console.log(JSON.parse(doc.rawJson));
76
+ // → { "Vendor": "Acme Corp", "Total": "1500.00", "Line Items": [...] }
77
+ ```
78
+
79
+ ---
27
80
 
28
- // Process an image with a template
29
- const result = await processImage(templateId, base64Image);
81
+ ## Authentication
82
+
83
+ Every SDK method requires a valid API token. Call `setToken` **once** at
84
+ application startup — it is stored in module scope and attached automatically as
85
+ a `Bearer` header on every subsequent request.
86
+
87
+ ```js
88
+ import { setToken } from "extractia-sdk";
89
+
90
+ setToken(process.env.EXTRACTIA_TOKEN);
91
+ ```
92
+
93
+ > **Security**: Never hard-code your token in client-side code. Use environment
94
+ > variables or a secrets manager. Tokens can be rotated from the dashboard at any
95
+ > time.
96
+
97
+ ---
98
+
99
+ ## Error Handling
100
+
101
+ The SDK maps every HTTP error to a typed exception. Catch the specific subclass
102
+ you need, or catch the base `ExtractiaError` for a generic fallback.
103
+
104
+ ```js
105
+ import {
106
+ processImage,
107
+ AuthError,
108
+ TierError,
109
+ RateLimitError,
110
+ NotFoundError,
111
+ ExtractiaError,
112
+ } from "extractia-sdk";
113
+
114
+ try {
115
+ const doc = await processImage(templateId, base64Image);
116
+ } catch (err) {
117
+ if (err instanceof AuthError) {
118
+ // 401 — token is missing, expired, or revoked
119
+ console.error("Authentication failed:", err.message);
120
+ } else if (err instanceof TierError) {
121
+ // 402/403 — monthly quota exhausted or plan doesn't allow this action
122
+ console.error("Upgrade your plan:", err.message);
123
+ } else if (err instanceof RateLimitError) {
124
+ // 429 — too many requests in a short window
125
+ console.warn("Rate limited. Retrying in 60s...");
126
+ await new Promise((r) => setTimeout(r, 60_000));
127
+ } else if (err instanceof NotFoundError) {
128
+ // 404 — template or document does not exist
129
+ console.error("Not found:", err.message);
130
+ } else if (err instanceof ExtractiaError) {
131
+ // Fallback for any other API error
132
+ console.error(`API error [${err.status}]:`, err.message);
133
+ } else {
134
+ throw err; // Re-throw network / unexpected errors
135
+ }
136
+ }
30
137
  ```
31
138
 
139
+ ### Error class hierarchy
140
+
141
+ | Class | HTTP status | When thrown |
142
+ | ---------------- | ----------- | ------------------------------------------------- |
143
+ | `ExtractiaError` | any | Base class; fallback for unexpected codes |
144
+ | `AuthError` | 401 | Missing, expired, or revoked token |
145
+ | `ForbiddenError` | 403 | Unconfirmed account or sub-user permission denied |
146
+ | `TierError` | 402 / 403 | Monthly document quota exhausted |
147
+ | `RateLimitError` | 429 | Too many requests in time window |
148
+ | `NotFoundError` | 404 | Template or document not found |
149
+
150
+ ---
151
+
32
152
  ## API Reference
33
153
 
34
- ### Authentication
154
+ ### Profile & Webhook
155
+
156
+ #### `setToken(token)`
157
+
158
+ Sets the Bearer token used for all requests. Must be called before any other method.
159
+
160
+ | Parameter | Type | Required | Description |
161
+ | --------- | -------- | -------- | ---------------------------------- |
162
+ | `token` | `string` | ✅ | Your Extractia API token (`ext_…`) |
35
163
 
36
- - `setToken(token)` – Set the API token for requests.
37
- - `getMyProfile()` – Get the authenticated user's profile.
38
- - `updateWebhook(url)` – Update the webhook URL for the user.
164
+ ```js
165
+ setToken("ext_abc123");
166
+ ```
167
+
168
+ ---
169
+
170
+ #### `getMyProfile()`
171
+
172
+ Returns the profile and usage metrics of the authenticated user.
173
+
174
+ **Returns:** `Promise<AppUserProfile>`
175
+
176
+ ```js
177
+ const profile = await getMyProfile();
178
+ console.log(profile.email); // 'user@example.com'
179
+ console.log(profile.formTemplatesCount); // 5
180
+ console.log(profile.documentsCount); // 42
181
+ ```
182
+
183
+ ---
184
+
185
+ #### `updateWebhook(url)`
186
+
187
+ Updates the webhook URL. After each successful extraction, Extractia
188
+ sends a `POST` to this URL with the document payload.
189
+
190
+ | Parameter | Type | Required | Description |
191
+ | --------- | -------- | -------- | -------------------------------------------- |
192
+ | `url` | `string` | ✅ | Webhook URL (pass empty string `""` to remove) |
193
+
194
+ **Returns:** `Promise<void>`
195
+
196
+ ```js
197
+ await updateWebhook("https://myapp.example.com/hooks/extractia");
198
+ await updateWebhook(""); // remove webhook
199
+ ```
200
+
201
+ **Webhook POST payload:**
202
+
203
+ ```json
204
+ {
205
+ "documentId": "abc123",
206
+ "templateId": "tpl456",
207
+ "rawJson": "{ \"Total\": \"150.00\" }",
208
+ "createdAt": "2025-01-05T10:30:00Z"
209
+ }
210
+ ```
211
+
212
+ ---
39
213
 
40
214
  ### Templates
41
215
 
42
- - `getTemplates()` Retrieve all templates.
43
- - `getTemplateById(id)` – Get a template by its ID.
44
- - `getTemplateByName(name)` Get a template by its name.
45
- - `createTemplate(template)` – Create a new template.
46
- - `updateTemplate(id, template)` – Update an existing template.
47
- - `deleteTemplate(id)` – Delete a template.
48
- - `deleteAllTemplateDocuments(id)` – Delete all documents for a template.
216
+ A **template** defines the fields to extract from a document.
217
+
218
+ **Supported field types:** `TEXT` | `NUMBER` | `PERCENTAGE` | `DATE` | `BOOLEAN` | `EMAIL` | `PHONE` | `ADDRESS` | `CURRENCY` | `LIST`
219
+
220
+ ---
221
+
222
+ #### `getTemplates()`
223
+
224
+ Returns all templates owned by the authenticated user.
225
+
226
+ ```js
227
+ const templates = await getTemplates();
228
+ templates.forEach((t) => console.log(t.id, t.label));
229
+ ```
230
+
231
+ ---
232
+
233
+ #### `getTemplateById(id)`
234
+
235
+ Returns a single template by its ID. Throws `NotFoundError` if missing.
236
+
237
+ ```js
238
+ const template = await getTemplateById("tpl_abc123");
239
+ ```
240
+
241
+ ---
242
+
243
+ #### `getTemplateByName(name)`
244
+
245
+ Returns a template matched by its label name. Throws `NotFoundError` if missing.
246
+
247
+ ```js
248
+ const invoice = await getTemplateByName("Invoice");
249
+ ```
250
+
251
+ ---
252
+
253
+ #### `suggestFields(templateName, extractionContext?)`
254
+
255
+ Uses AI to suggest extraction field definitions for a given document type.
256
+ Results can be passed directly to `createTemplate`.
257
+
258
+ **Consumes AI credits.**
259
+
260
+ | Parameter | Type | Required | Description |
261
+ | ------------------- | -------- | -------- | ------------------------------------------------------------------ |
262
+ | `templateName` | `string` | ✅ | Document type name (e.g. `"Invoice"`, `"Driver's License"`) |
263
+ | `extractionContext` | `string` | — | Natural-language hint about what to extract or detect |
264
+
265
+ ```js
266
+ // Basic usage
267
+ const fields = await suggestFields("Receipt");
268
+
269
+ // With context — AI will only return what you describe
270
+ const fields = await suggestFields(
271
+ "Purchase Order",
272
+ "I need the supplier name, PO number, and all ordered products with quantity and unit price"
273
+ );
274
+
275
+ const template = await createTemplate({ label: "Purchase Order", fields });
276
+ ```
277
+
278
+ The returned array follows the `FormField` shape:
279
+ ```json
280
+ [
281
+ { "label": "PO Number", "type": "TEXT", "required": true },
282
+ { "label": "Supplier", "type": "TEXT", "required": true },
283
+ { "label": "Order Items", "type": "LIST", "required": false, "listLabel": "Product" }
284
+ ]
285
+ ```
286
+
287
+ ---
288
+
289
+ #### `createTemplate(template)`
290
+
291
+ Creates a new template.
292
+
293
+ ```js
294
+ const template = await createTemplate({
295
+ label: "Purchase Order",
296
+ fields: [
297
+ { label: "PO Number", type: "TEXT", required: true },
298
+ { label: "Vendor", type: "TEXT", required: true },
299
+ { label: "Total Amount", type: "CURRENCY", required: true },
300
+ { label: "Order Date", type: "DATE", required: false },
301
+ { label: "Line Items", type: "LIST", required: false, listLabel: "Product" },
302
+ ],
303
+ });
304
+ console.log(template.id); // 'tpl_newid'
305
+ ```
306
+
307
+ ---
308
+
309
+ #### `updateTemplate(id, template)`
310
+
311
+ Updates an existing template's label and/or fields.
312
+
313
+ ```js
314
+ const updated = await updateTemplate("tpl_abc123", {
315
+ fields: [
316
+ { label: "Vendor", type: "TEXT", required: true },
317
+ { label: "Total", type: "CURRENCY", required: true },
318
+ { label: "Due Date", type: "DATE", required: false },
319
+ ],
320
+ });
321
+ ```
322
+
323
+ ---
324
+
325
+ #### `deleteTemplate(id)`
326
+
327
+ Deletes a template. Returns a `409 Conflict` error if the template has
328
+ associated documents — call `deleteAllTemplateDocuments` first.
329
+
330
+ ```js
331
+ await deleteAllTemplateDocuments("tpl_abc123");
332
+ await deleteTemplate("tpl_abc123");
333
+ ```
334
+
335
+ ---
336
+
337
+ #### `deleteAllTemplateDocuments(id)`
338
+
339
+ Deletes all documents associated with a template in one call.
340
+
341
+ ```js
342
+ await deleteAllTemplateDocuments("tpl_abc123");
343
+ ```
344
+
345
+ ---
49
346
 
50
347
  ### Documents
51
348
 
52
- - `getDocumentsByTemplateId(templateId, options)` – Get documents for a template.
53
- - `options`:
54
- ```js
55
- {
56
- preconformed?: boolean,
57
- index?: number, // Paging index (each page contains 10 documents)
58
- sort?: number, // 1 for ascending, -1 for descending
59
- includeImage?: boolean
60
- }
61
- ```
62
- - `deleteDocument(documentId)` – Delete a document.
63
- - `processImage(templateId, base64Image)` – Process a single image.
64
- - `processImagesMultipage(templateId, base64ImagesArray)` Process multiple images (multipage).
349
+ #### `getDocumentsByTemplateId(templateId, options?)`
350
+
351
+ Returns a paginated list of documents for a given template (newest first by default).
352
+
353
+ | Option | Type | Default | Description |
354
+ | -------------- | --------- | ------- | ------------------------------------------------- |
355
+ | `preconformed` | `boolean` | — | Filter: reviewed (`true`) or unreviewed (`false`) |
356
+ | `index` | `number` | `0` | Zero-based page index (10 docs per page) |
357
+ | `sort` | `string` | `"-1"` | Sort direction: `"1"` = ASC, `"-1"` = DESC |
358
+ | `includeImage` | `boolean` | `false` | Include base64 source image in results |
359
+
360
+ ```js
361
+ const page = await getDocumentsByTemplateId("tpl_abc123", {
362
+ preconformed: false, // only unreviewed
363
+ index: 0,
364
+ });
365
+
366
+ console.log(`Page 1 of ${page.totalPages}`);
367
+ for (const doc of page.content) {
368
+ const fields = JSON.parse(doc.rawJson);
369
+ console.log("Total:", fields["Total Amount"]);
370
+ }
371
+ ```
372
+
373
+ ---
374
+
375
+ #### `getDocumentById(templateId, docId, options?)`
376
+
377
+ Returns a single document by template and document ID.
378
+
379
+ | Option | Type | Default | Description |
380
+ | -------------- | --------- | ------- | ---------------------------------- |
381
+ | `includeImage` | `boolean` | `false` | Include the base64 source image |
382
+
383
+ ```js
384
+ const doc = await getDocumentById("tpl_abc123", "doc_xyz789", { includeImage: true });
385
+ console.log(doc.imageBase64); // the original scan
386
+ ```
387
+
388
+ ---
389
+
390
+ #### `getRecentDocuments(size?)`
391
+
392
+ Returns the N most-recent documents across **all templates**. Useful for dashboard feeds.
393
+
394
+ ```js
395
+ const recent = await getRecentDocuments(5);
396
+ recent.forEach(doc => console.log(doc.createdAt, doc.formTemplateId));
397
+ ```
398
+
399
+ ---
400
+
401
+ #### `deleteDocument(documentId)`
402
+
403
+ Permanently deletes a single document.
404
+
405
+ ```js
406
+ await deleteDocument("doc_xyz789");
407
+ ```
408
+
409
+ ---
410
+
411
+ #### `updateDocumentStatus(docId, status)`
412
+
413
+ Updates the workflow status of a document. You define the status values — common
414
+ choices are `"PENDING"`, `"REVIEWED"`, `"APPROVED"`, `"REJECTED"`.
415
+
416
+ ```js
417
+ await updateDocumentStatus("doc_xyz789", "APPROVED");
418
+ ```
419
+
420
+ ---
421
+
422
+ #### `updateDocumentNotes(docId, notes)`
423
+
424
+ Saves reviewer annotations on a document. Pass `""` to clear.
425
+
426
+ ```js
427
+ await updateDocumentNotes("doc_xyz789", "Verified against original: amounts match.");
428
+ await updateDocumentNotes("doc_xyz789", ""); // clear notes
429
+ ```
430
+
431
+ ---
432
+
433
+ #### `updateDocumentData(docId, data, options?)`
434
+
435
+ Corrects or overwrites the extracted field data programmatically. Optionally
436
+ marks the document as preconformed (reviewed) in the same call.
437
+
438
+ | Option | Type | Default | Description |
439
+ | --------------- | --------- | ------- | ---------------------------------------- |
440
+ | `preconformed` | `boolean` | `false` | Mark document as reviewed/confirmed |
441
+
442
+ ```js
443
+ // Fix a wrong extraction and confirm
444
+ await updateDocumentData(
445
+ "doc_xyz789",
446
+ { "Total Amount": 1250.00, "Invoice Number": "INV-1042" },
447
+ { preconformed: true }
448
+ );
449
+ ```
450
+
451
+ ---
452
+
453
+ #### `bulkPreconform(ids)`
454
+
455
+ Marks multiple documents as reviewed/confirmed in a single API call. Returns
456
+ the count of documents actually updated.
457
+
458
+ ```js
459
+ const { updated } = await bulkPreconform(["doc_1", "doc_2", "doc_3"]);
460
+ console.log(`${updated} documents confirmed`);
461
+ ```
462
+
463
+ ---
464
+
465
+ ### Processing Images
466
+
467
+ #### `processImage(templateId, base64Image)`
468
+
469
+ Processes a **single image** — ideal for one-page documents.
470
+
471
+ Max image size: **5 MB** decoded. Supported formats: PNG, JPEG, WEBP, BMP.
472
+
473
+ ```js
474
+ // Node.js
475
+ import { readFileSync } from "fs";
476
+ const base64 = readFileSync("./invoice.jpg").toString("base64");
477
+ const doc = await processImage("tpl_invoice_id", base64);
478
+ const fields = JSON.parse(doc.rawJson);
479
+ console.log("Vendor:", fields["Vendor"]);
480
+ console.log("Total:", fields["Total Amount"]);
481
+ ```
482
+
483
+ ```js
484
+ // Browser — convert a File input to base64
485
+ function fileToBase64(file) {
486
+ return new Promise((resolve, reject) => {
487
+ const reader = new FileReader();
488
+ reader.onload = () => resolve(reader.result.split(",")[1]);
489
+ reader.onerror = reject;
490
+ reader.readAsDataURL(file);
491
+ });
492
+ }
493
+
494
+ const file = document.querySelector("#fileInput").files[0];
495
+ const base64 = await fileToBase64(file);
496
+ const doc = await processImage(templateId, base64);
497
+ ```
498
+
499
+ ---
500
+
501
+ #### `processImagesMultipage(templateId, base64ImagesArray)`
502
+
503
+ Processes **multiple images** as a single multi-page document. All pages are
504
+ merged into one result. Use this for PDFs split into images or multi-page scans.
505
+
506
+ ```js
507
+ import { readdirSync, readFileSync } from "fs";
508
+ import path from "path";
509
+
510
+ const pages = readdirSync("./scan-pages")
511
+ .sort()
512
+ .map((f) => readFileSync(path.join("./scan-pages", f)).toString("base64"));
513
+
514
+ const doc = await processImagesMultipage("tpl_contract_id", pages);
515
+ const result = JSON.parse(doc.rawJson);
516
+ console.log("Contract Date:", result["Contract Date"]);
517
+ ```
518
+
519
+ ---
520
+
521
+ ### AI Features
522
+
523
+ #### `generateDocumentSummary(docId)`
524
+
525
+ Asks the AI to generate a concise bullet-point summary of a document's
526
+ extracted data. Returns natural language — not JSON.
527
+
528
+ **Consumes AI credits.**
529
+
530
+ ```js
531
+ const { summary } = await generateDocumentSummary("doc_xyz789");
532
+ console.log(summary);
533
+ // • Invoice #1042 was issued by Acme Corp on January 5 2025.
534
+ // • The total amount due is $1,250.00, payable by February 4 2025.
535
+ // • The order includes 3 line items for a total of 15 units.
536
+ ```
537
+
538
+ ---
539
+
540
+ ### Exports
541
+
542
+ Export all documents in a template to a file format for offline processing,
543
+ spreadsheet import, or archiving.
544
+
545
+ #### `exportDocumentsCsv(templateId, options?)`
546
+
547
+ Returns all extracted data as a UTF-8 CSV string with BOM (Excel-compatible).
548
+ Each row is one document; columns are the extracted fields plus `preconformed`
549
+ and `uploadedAt`.
550
+
551
+ | Option | Type | Description |
552
+ | --------- | ---------- | ------------------------------------------------- |
553
+ | `fields` | `string[]` | Optional column subset. Dot-notation for nested. |
554
+
555
+ ```js
556
+ // Export everything
557
+ const csv = await exportDocumentsCsv("tpl_abc123");
558
+ fs.writeFileSync("invoices.csv", csv);
559
+
560
+ // Export a specific column subset
561
+ const csv = await exportDocumentsCsv("tpl_abc123", {
562
+ fields: ["Invoice Number", "Vendor", "Total Amount", "Issue Date"],
563
+ });
564
+ ```
565
+
566
+ ---
567
+
568
+ #### `exportDocumentsJson(templateId)`
569
+
570
+ Returns all documents as a plain JSON array. Each element is the extracted
571
+ field map plus three metadata keys:
572
+
573
+ - `_id` — document ID
574
+ - `_preconformed` — whether the document was reviewed
575
+ - `_uploadedAt` — ISO timestamp
576
+
577
+ ```js
578
+ const records = await exportDocumentsJson("tpl_abc123");
579
+ records.forEach(doc => {
580
+ console.log(doc._id, doc["Invoice Number"], doc["Total Amount"]);
581
+ });
582
+
583
+ // Save to file
584
+ import { writeFileSync } from "fs";
585
+ writeFileSync("invoices.json", JSON.stringify(records, null, 2));
586
+ ```
587
+
588
+ ---
589
+
590
+ ### OCR Tools
591
+
592
+ OCR Tools let you ask AI yes/no questions, classify documents into labels, or
593
+ extract free-form text from any image — without a template.
594
+
595
+ #### `getOcrTools()`
596
+
597
+ Returns all OCR tool configurations owned by the authenticated user.
598
+
599
+ ```js
600
+ const tools = await getOcrTools();
601
+ tools.forEach(t => console.log(t.id, t.name, t.outputType));
602
+ ```
603
+
604
+ ---
605
+
606
+ #### `createOcrTool(config)`
607
+
608
+ Creates a new OCR tool configuration.
609
+
610
+ | Field | Type | Required | Description |
611
+ | ------------ | ------------------------------ | -------- | -------------------------------------------------- |
612
+ | `name` | `string` | ✅ | Human-friendly display name |
613
+ | `prompt` | `string` | ✅ | Natural-language instruction for the AI |
614
+ | `outputType` | `"YES_NO" \| "LABEL" \| "TEXT"` | ✅ | Expected output shape |
615
+ | `labels` | `string[]` | ⚠️ | Required when `outputType === "LABEL"` |
616
+
617
+ ```js
618
+ // YES/NO check
619
+ const checker = await createOcrTool({
620
+ name: "Proof of Residence Check",
621
+ prompt: "Does this document appear to be a valid proof of residence? Look for an address, official stamp, and the person's name.",
622
+ outputType: "YES_NO",
623
+ });
624
+
625
+ // Document classifier
626
+ const classifier = await createOcrTool({
627
+ name: "Document Type Classifier",
628
+ prompt: "What type of document is this?",
629
+ outputType: "LABEL",
630
+ labels: ["invoice", "id_card", "receipt", "proof_of_residence", "contract", "other"],
631
+ });
632
+
633
+ // Free-form extraction
634
+ const extractor = await createOcrTool({
635
+ name: "Serial Number Extractor",
636
+ prompt: "Extract the serial number or product code printed on the label. Return only the code, nothing else.",
637
+ outputType: "TEXT",
638
+ });
639
+ ```
640
+
641
+ ---
642
+
643
+ #### `updateOcrTool(id, config)`
644
+
645
+ Updates an existing OCR tool configuration.
646
+
647
+ ```js
648
+ await updateOcrTool("tool_abc", {
649
+ prompt: "Does this document show a current address dated within the last 3 months?",
650
+ });
651
+ ```
652
+
653
+ ---
654
+
655
+ #### `deleteOcrTool(id)`
656
+
657
+ Deletes an OCR tool configuration.
658
+
659
+ ```js
660
+ await deleteOcrTool("tool_abc");
661
+ ```
662
+
663
+ ---
664
+
665
+ #### `runOcrTool(id, base64Image)`
666
+
667
+ Runs an OCR tool against a base64-encoded image. Max image size: **5 MB**.
668
+
669
+ **Consumes AI credits.**
670
+
671
+ ```js
672
+ import { readFileSync } from "fs";
673
+ const image = readFileSync("./id-card.jpg").toString("base64");
674
+
675
+ const result = await runOcrTool("tool_residence_check", image);
676
+ console.log(result.answer); // "YES"
677
+ console.log(result.explanation); // "The document shows a full address, an official municipal stamp, and the applicant's name."
678
+
679
+ // Document classification
680
+ const type = await runOcrTool("tool_classifier", image);
681
+ console.log(type.answer); // "id_card"
682
+
683
+ // Full workflow: classify first, then extract
684
+ const { answer: docType } = await runOcrTool("tool_classifier", invoiceBase64);
685
+ if (docType === "invoice") {
686
+ const doc = await processImage(invoiceTemplateId, invoiceBase64);
687
+ }
688
+ ```
689
+
690
+ ---
691
+
692
+ ### Credits & Analytics
693
+
694
+ #### `getCreditsBalance()`
695
+
696
+ Returns the current AI-credit balance for the authenticated user.
697
+
698
+ ```js
699
+ const balance = await getCreditsBalance();
700
+ console.log(`Monthly credits: ${balance.monthlyBalance}`);
701
+ console.log(`Add-on credits: ${balance.addonBalance}`);
702
+ console.log(`Total available: ${balance.totalBalance}`);
703
+ ```
704
+
705
+ ---
706
+
707
+ #### `getCreditsHistory(options?)`
708
+
709
+ Returns a paginated history of AI credit consumption events (newest first).
710
+
711
+ | Option | Type | Default | Description |
712
+ | ------- | -------- | ------- | ------------------------- |
713
+ | `page` | `number` | `0` | Zero-based page index |
714
+ | `size` | `number` | `20` | Page size (max 100) |
715
+
716
+ ```js
717
+ const history = await getCreditsHistory({ page: 0, size: 10 });
718
+ history.content.forEach(entry => {
719
+ console.log(entry.timestamp, entry.operation, entry.creditsConsumed);
720
+ });
721
+ ```
722
+
723
+ ---
724
+
725
+ ## TypeScript
726
+
727
+ The SDK ships with a full `index.d.ts` declaration file — no `@types` package needed.
728
+
729
+ ```ts
730
+ import {
731
+ setToken,
732
+ processImage,
733
+ runOcrTool,
734
+ suggestFields,
735
+ exportDocumentsJson,
736
+ UserDocument,
737
+ OcrRunResult,
738
+ FormField,
739
+ TierError,
740
+ RateLimitError,
741
+ } from "extractia-sdk";
742
+
743
+ setToken(process.env.EXTRACTIA_TOKEN!);
744
+
745
+ async function classifyAndExtract(
746
+ templateId: string,
747
+ ocrToolId: string,
748
+ imagePath: string,
749
+ ): Promise<UserDocument | null> {
750
+ const { readFileSync } = await import("fs");
751
+ const base64 = readFileSync(imagePath).toString("base64");
752
+
753
+ // Classify first
754
+ const check: OcrRunResult = await runOcrTool(ocrToolId, base64);
755
+ if (check.answer !== "YES") {
756
+ console.log("Document rejected:", check.explanation);
757
+ return null;
758
+ }
759
+
760
+ // Extract with template
761
+ return processImage(templateId, base64);
762
+ }
763
+ ```
764
+
765
+ ---
766
+
767
+ ## Rate Limits & Quotas
768
+
769
+ | Limit | Value |
770
+ | ------------------ | ---------------------------------------------------- |
771
+ | Max image size | 5 MB decoded |
772
+ | Processing timeout | 60 seconds |
773
+ | Monthly documents | Depends on plan (Free / Pro / Business / Enterprise) |
774
+ | Active API keys | 10 per account |
775
+
776
+ When the monthly quota is exhausted, processing calls throw a `TierError`.
777
+ Purchase extra document packs or upgrade your plan from the dashboard to continue.
778
+
779
+ ---
780
+
781
+ ## Changelog
782
+
783
+ ### v1.1.0
784
+
785
+ - **New:** `suggestFields(templateName, context?)` — AI-powered field suggestions
786
+ - **New:** `getDocumentById(templateId, docId)` — fetch a single document
787
+ - **New:** `getRecentDocuments(size?)` — latest documents across all templates
788
+ - **New:** `generateDocumentSummary(docId)` — AI bullet-point summary of a document
789
+ - **New:** `updateDocumentStatus(docId, status)` — set workflow status
790
+ - **New:** `updateDocumentNotes(docId, notes)` — save reviewer annotations
791
+ - **New:** `updateDocumentData(docId, data, opts?)` — correct extracted data
792
+ - **New:** `bulkPreconform(ids)` — confirm multiple documents in one call
793
+ - **New:** `exportDocumentsCsv(templateId, opts?)` — export to CSV
794
+ - **New:** `exportDocumentsJson(templateId)` — export to JSON array
795
+ - **New:** `getOcrTools()` / `createOcrTool()` / `updateOcrTool()` / `deleteOcrTool()` / `runOcrTool(id, image)` — full OCR Tools API
796
+ - **New:** `getCreditsBalance()` / `getCreditsHistory(opts?)` — AI credits tracking
797
+ - Extended `FieldType` with `BOOLEAN`, `EMAIL`, `PHONE`, `ADDRESS`, `CURRENCY`
798
+ - Full TypeScript declarations updated for all new methods
799
+
800
+ ### v1.0.6
801
+
802
+ - Added `processImagesMultipage` for multi-page document support
803
+ - Added typed error classes (`AuthError`, `TierError`, `RateLimitError`, `NotFoundError`, `ForbiddenError`)
804
+ - Added TypeScript declaration file (`index.d.ts`)
805
+ - Added `deleteAllTemplateDocuments` helper
806
+ - Added browser IIFE build
807
+
808
+ ### v1.0.0
809
+
810
+ - Initial release: `setToken`, `getMyProfile`, `updateWebhook`, `getTemplates`,
811
+ `createTemplate`, `updateTemplate`, `deleteTemplate`, `getDocumentsByTemplateId`,
812
+ `deleteDocument`, `processImage`
813
+
814
+ ---
65
815
 
66
816
  ## License
67
817
 
68
- ISC
818
+ MIT