@taxora/sdk 1.0.7 → 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 +297 -0
- package/dist/index.cjs +1871 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +901 -1
- package/dist/index.d.ts +901 -1
- package/dist/index.js +1838 -22
- package/dist/index.js.map +1 -1
- package/package.json +10 -5
package/README.md
CHANGED
|
@@ -301,6 +301,303 @@ const results = await client.vat.search('Alpha Handels', 25 /* perPage */);
|
|
|
301
301
|
|
|
302
302
|
---
|
|
303
303
|
|
|
304
|
+
## 🔎 Smart Enrichment (reverse VAT lookup)
|
|
305
|
+
|
|
306
|
+
Resolve a company **name + address + country → VAT number + confidence**. A confident match
|
|
307
|
+
returns synchronously; harder cases (and all bulk batches) are processed asynchronously and
|
|
308
|
+
delivered via the `enrichment.completed` webhook — poll with `get(jobId)` in the meantime.
|
|
309
|
+
Requires the Smart Enrichment add-on to be active on your account.
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
// Single lookup
|
|
313
|
+
let job = await client.smartEnrichment.lookup({
|
|
314
|
+
companyName: 'Example Company GmbH',
|
|
315
|
+
country: 'AT',
|
|
316
|
+
street: 'Main Street 10',
|
|
317
|
+
postalCode: '1010',
|
|
318
|
+
city: 'Vienna',
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
if (job.isProcessing()) {
|
|
322
|
+
// Resolved asynchronously — poll until done (or wait for the webhook)
|
|
323
|
+
job = await client.smartEnrichment.waitForCompletion(job.jobId, {
|
|
324
|
+
pollIntervalMs: 2000, // optional, default 2000
|
|
325
|
+
timeoutMs: 120_000, // optional, default 120000 — throws TaxoraException on timeout
|
|
326
|
+
});
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
const result = job.result();
|
|
330
|
+
console.log(result?.status); // 'found' | 'no_vat_exists' | 'not_found'
|
|
331
|
+
console.log(result?.vatNumber); // e.g. 'ATU12345678'
|
|
332
|
+
console.log(result?.confidence); // 0–100
|
|
333
|
+
|
|
334
|
+
// Bulk lookup (always async → webhook + polling)
|
|
335
|
+
const bulk = await client.smartEnrichment.bulkLookup([
|
|
336
|
+
{ companyName: 'A GmbH', country: 'DE', city: 'Berlin' },
|
|
337
|
+
{ companyName: 'B SARL', country: 'FR' },
|
|
338
|
+
]);
|
|
339
|
+
const done = await client.smartEnrichment.waitForCompletion(bulk.jobId);
|
|
340
|
+
for (const r of done.results) {
|
|
341
|
+
console.log(r.status, r.vatNumber ?? '—', r.matchedAddress ?? '');
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// Lookup history (paginated, newest first; perPage is capped at 100 server-side)
|
|
345
|
+
const history = await client.smartEnrichment.history(1, 25, 'Example GmbH' /* optional free-text search */);
|
|
346
|
+
console.log(`${history.total} lookups`);
|
|
347
|
+
console.log(history.stats?.total, history.stats?.found); // account-wide tiles (independent of the search filter)
|
|
348
|
+
for (const row of history) {
|
|
349
|
+
console.log(row.queryCompanyName, '→', row.result?.vatNumber ?? row.status);
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### Quota usage & billing history
|
|
354
|
+
|
|
355
|
+
```ts
|
|
356
|
+
const usage = await client.smartEnrichment.usage();
|
|
357
|
+
console.log(usage.period, `${usage.used}/${usage.included}`, 'remaining:', usage.remaining);
|
|
358
|
+
console.log(usage.overageCount, '×', usage.overagePrice, '=', usage.overageAmount);
|
|
359
|
+
for (const entry of usage.history) {
|
|
360
|
+
console.log(entry.period, entry.matches, entry.amount, entry.state); // 'billed' | 'pending'
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
### CSV export
|
|
365
|
+
|
|
366
|
+
Download your lookups as CSV in the bulk-input shape plus the resolved VAT columns (bulk jobs
|
|
367
|
+
are flattened to one line per input row). All filters are optional.
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
import { SmartEnrichmentStatus } from '@taxora/sdk';
|
|
371
|
+
import { writeFileSync } from 'fs';
|
|
372
|
+
|
|
373
|
+
const csv = await client.smartEnrichment.export({
|
|
374
|
+
dateFrom: '2026-01-01', // job created_at lower bound (YYYY-MM-DD)
|
|
375
|
+
dateTo: '2026-06-30', // job created_at upper bound (YYYY-MM-DD)
|
|
376
|
+
minConfidence: 80, // 0–100
|
|
377
|
+
status: [SmartEnrichmentStatus.FOUND, SmartEnrichmentStatus.NO_VAT_EXISTS],
|
|
378
|
+
onlyFound: true, // only rows that resolved to a VAT number
|
|
379
|
+
});
|
|
380
|
+
writeFileSync('smart-enrichment.csv', csv);
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Statistics
|
|
384
|
+
|
|
385
|
+
Aggregated lookup statistics: headline totals, a time series and breakdowns by source, outcome
|
|
386
|
+
and confidence band. Defaults to the last 12 months and a monthly interval (server-side).
|
|
387
|
+
|
|
388
|
+
```ts
|
|
389
|
+
const stats = await client.smartEnrichment.statistics({
|
|
390
|
+
dateFrom: '2026-01-01', // optional (YYYY-MM-DD)
|
|
391
|
+
dateTo: '2026-06-30', // optional (YYYY-MM-DD)
|
|
392
|
+
interval: 'month', // 'day' | 'week' | 'month'
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
console.log(stats.totals.items, 'items,', stats.totals.found, `found (${stats.totals.foundRate}%)`);
|
|
396
|
+
console.log('avg confidence:', stats.totals.avgConfidence);
|
|
397
|
+
for (const bucket of stats.timeSeries) {
|
|
398
|
+
console.log(`${bucket.bucket}: ${bucket.found}/${bucket.items}`);
|
|
399
|
+
}
|
|
400
|
+
for (const band of stats.confidenceBuckets) {
|
|
401
|
+
console.log(band.bucket, band.count); // high / medium / low
|
|
402
|
+
}
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
> Note: `no_vat_exists` means the company was identified but legitimately has **no** VAT/UID
|
|
406
|
+
> number (common for purely-domestic German firms). It is a definitive answer — not a failure —
|
|
407
|
+
> and is not billed.
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
## 🇫🇷 E-Reporting / Compliance (DGFiP Flux 10)
|
|
412
|
+
|
|
413
|
+
Full French e-reporting integration: enroll a company with the compliance provider, record and
|
|
414
|
+
submit transactions (individually or via CSV import), and track the resulting DGFiP tax reports.
|
|
415
|
+
All `compliance` routes require an active E-Reporting subscription / feature grant on your
|
|
416
|
+
account — except `requestEReportingAccess()`, which is how you ask for one.
|
|
417
|
+
|
|
418
|
+
### Request feature activation
|
|
419
|
+
|
|
420
|
+
Not enabled yet? This route is deliberately **not** gated — it requests activation of the
|
|
421
|
+
E-Reporting feature for your account (the authenticated user's name/email take precedence over
|
|
422
|
+
the form values):
|
|
423
|
+
|
|
424
|
+
```ts
|
|
425
|
+
await client.eReporting.requestEReportingAccess({
|
|
426
|
+
company: 'Example GmbH',
|
|
427
|
+
phone: '+43 660 1234567',
|
|
428
|
+
message: 'Please activate e-reporting for our account.',
|
|
429
|
+
language: 'de', // locale of the confirmation mail
|
|
430
|
+
});
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### 1. SIRENE lookup (prefill enrollment data)
|
|
434
|
+
|
|
435
|
+
Resolve company data by SIRET, SIREN or French VAT number via the French SIRENE registry.
|
|
436
|
+
**Rate-limited to 3 requests/minute per user** (protects the external French government API).
|
|
437
|
+
|
|
438
|
+
```ts
|
|
439
|
+
const company = await client.eReporting.sireneLookup('FR32123456789'); // or a SIRET/SIREN
|
|
440
|
+
|
|
441
|
+
console.log(company.companyName, company.siret);
|
|
442
|
+
console.log(company.nafCode, company.enterpriseSize); // e.g. "47", "pme"
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
### 2. Create an enrollment
|
|
446
|
+
|
|
447
|
+
Creates the local enrollment, provisions the provider account and (by default) activates the
|
|
448
|
+
DGFiP regime. Either `siret` or `siren` is required. On provider failure the API responds 502
|
|
449
|
+
(an `HttpException`) and the enrollment is persisted in an error state for retries.
|
|
450
|
+
|
|
451
|
+
```ts
|
|
452
|
+
const enrollment = await client.eReporting.createEnrollment({
|
|
453
|
+
siret: company.siret!, // or siren: company.siren
|
|
454
|
+
companyName: company.companyName,
|
|
455
|
+
address: company.address ?? undefined,
|
|
456
|
+
city: company.city ?? undefined,
|
|
457
|
+
postalcode: company.postalcode ?? undefined,
|
|
458
|
+
email: 'tax@acme.fr',
|
|
459
|
+
nafCode: company.nafCode!, // 2-digit NAF division, e.g. "47"
|
|
460
|
+
enterpriseSize: 'pme', // 'micro' | 'pme' | 'eti' | 'ge'
|
|
461
|
+
typeOperation: 'mixed', // 'services' | 'goods' | 'mixed'
|
|
462
|
+
reportingStartDate: '2026-09-01', // or a Date
|
|
463
|
+
autoActivate: true, // optional — false creates the account without regime activation
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
console.log(enrollment.id, enrollment.status, enrollment.statusLabel);
|
|
467
|
+
|
|
468
|
+
const page = await client.eReporting.listEnrollments(1, 25); // paginated
|
|
469
|
+
const single = await client.eReporting.getEnrollment(enrollment.id);
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
### 3. Record & submit transactions
|
|
473
|
+
|
|
474
|
+
Records the transaction and (by default) submits it right away; pass `submitNow: false` to defer.
|
|
475
|
+
Creation is **idempotent** on the invoice's natural key (enrollment, type, invoice number,
|
|
476
|
+
invoice date, counterparty VAT) — a client retry returns the existing transaction instead of
|
|
477
|
+
creating a duplicate.
|
|
478
|
+
|
|
479
|
+
> **Note:** Immediate per-invoice submission (and `submitTransaction()`) requires the
|
|
480
|
+
> **e-invoicing** feature on your account. In pure e-reporting mode transactions are only
|
|
481
|
+
> recorded here and reported automatically via the aggregated daily ledgers —
|
|
482
|
+
> `submitTransaction()` then returns a 422 `ValidationException` explaining this.
|
|
483
|
+
|
|
484
|
+
```ts
|
|
485
|
+
const tx = await client.eReporting.createTransaction({
|
|
486
|
+
complianceEnrollmentId: enrollment.id,
|
|
487
|
+
transactionType: 'b2c_outbound', // | 'b2b_domestic_outbound' | 'b2b_domestic_inbound'
|
|
488
|
+
// | 'crossborder_outbound' | 'crossborder_inbound'
|
|
489
|
+
invoiceNumber: 'TKT-2026-00001',
|
|
490
|
+
invoiceDate: '2026-07-01', // or a Date
|
|
491
|
+
currency: 'EUR', // optional, default EUR
|
|
492
|
+
subtotal: 100,
|
|
493
|
+
taxAmount: 20,
|
|
494
|
+
total: 120,
|
|
495
|
+
invoiceLines: [
|
|
496
|
+
{
|
|
497
|
+
description: 'Ticket',
|
|
498
|
+
quantity: 1,
|
|
499
|
+
price: 100,
|
|
500
|
+
taxes: [{ name: 'TVA', percent: 20, category: 'S' }], // category E needs a VATEX `comment`
|
|
501
|
+
},
|
|
502
|
+
],
|
|
503
|
+
submitNow: false, // record now, submit later
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
// Submit (or retry) a pending/error transaction later — replays the stored invoice lines
|
|
507
|
+
const submitted = await client.eReporting.submitTransaction(tx.id);
|
|
508
|
+
console.log(submitted.state, submitted.stateLabel); // e.g. "submitted"
|
|
509
|
+
|
|
510
|
+
// Manage recorded transactions (only `pending` ones can be edited/deleted)
|
|
511
|
+
const transactions = await client.eReporting.listTransactions({
|
|
512
|
+
dateFrom: '2026-07-01', // or a Date
|
|
513
|
+
dateTo: '2026-07-31',
|
|
514
|
+
state: 'pending', // 'pending' | 'sending' | 'submitted' | 'error'
|
|
515
|
+
transactionType: 'b2c_outbound',
|
|
516
|
+
page: 1,
|
|
517
|
+
perPage: 25,
|
|
518
|
+
});
|
|
519
|
+
const one = await client.eReporting.getTransaction(tx.id);
|
|
520
|
+
await client.eReporting.updateTransaction(tx.id, { total: 130, taxAmount: 21.67 });
|
|
521
|
+
await client.eReporting.deleteTransaction(tx.id); // resolves void on 204
|
|
522
|
+
```
|
|
523
|
+
|
|
524
|
+
### CSV import
|
|
525
|
+
|
|
526
|
+
Bulk-import from a semicolon-separated CSV (one row per invoice line; rows sharing an
|
|
527
|
+
`invoice_number` are grouped into one transaction). Idempotent: re-uploading skips rows whose
|
|
528
|
+
invoice already exists.
|
|
529
|
+
|
|
530
|
+
```ts
|
|
531
|
+
import { readFileSync } from 'fs';
|
|
532
|
+
|
|
533
|
+
const result = await client.eReporting.importTransactions(
|
|
534
|
+
enrollment.id,
|
|
535
|
+
readFileSync('transactions.csv', 'utf8'),
|
|
536
|
+
'transactions.csv', // optional filename
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
console.log(result.created, result.skippedDuplicates);
|
|
540
|
+
for (const error of result.errors) console.warn(error); // row-level errors
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
### 4. Track tax reports
|
|
544
|
+
|
|
545
|
+
Every submitted transaction is tracked through the aggregated DGFiP ledger lifecycle
|
|
546
|
+
(`new → sent → acknowledged → registered` or `refused`/`error`).
|
|
547
|
+
|
|
548
|
+
```ts
|
|
549
|
+
const reports = await client.eReporting.listTaxReports(1, 25, 'refused'); // state filter optional
|
|
550
|
+
for (const report of reports) {
|
|
551
|
+
console.log(report.state, report.stateLabel, report.isTerminal, report.refusalReason);
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
const report = await client.eReporting.getTaxReport(reports.rows[0]!.id);
|
|
555
|
+
console.log(report.baseAmount, report.taxAmount, report.totalAmount); // precision-safe strings
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
### VAT rates & DGFiP tax categories
|
|
559
|
+
|
|
560
|
+
The canonical rates for a reporting country (the seller's reporting-country rates). Use these to
|
|
561
|
+
build the `taxes` entries of your invoice lines.
|
|
562
|
+
|
|
563
|
+
```ts
|
|
564
|
+
const rates = await client.eReporting.getVatRates('FR'); // defaults to FR when omitted
|
|
565
|
+
|
|
566
|
+
for (const rate of rates.rates) {
|
|
567
|
+
console.log(rate.percent, rate.category, rate.taxName, rate.requiresVatex);
|
|
568
|
+
// 20 'S' 'TVA' false … 0 'E' 'TVA' true (VATEX code required in the line's comment)
|
|
569
|
+
}
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
### Revenue statistics
|
|
573
|
+
|
|
574
|
+
Read-only turnover aggregation over your e-reporting transactions — totals, a time series and
|
|
575
|
+
breakdowns by transaction type, counterparty country and state. All headline figures are
|
|
576
|
+
expressed in the most frequent currency in the range (`primaryCurrency`); when more than one
|
|
577
|
+
currency is present, `isMultiCurrency` is `true` and `byCurrency` lists each one. Monetary values
|
|
578
|
+
are returned as 4-decimal strings to preserve precision.
|
|
579
|
+
|
|
580
|
+
```typescript
|
|
581
|
+
const stats = await client.eReporting.getRevenueStatistics({
|
|
582
|
+
dateFrom: '2026-01-01', // optional — defaults to 12 months ago (server-side)
|
|
583
|
+
dateTo: new Date(), // optional — defaults to today
|
|
584
|
+
interval: 'month', // 'day' | 'week' | 'month' (default 'month')
|
|
585
|
+
transactionType: 'b2c_outbound', // optional filter
|
|
586
|
+
state: 'submitted', // optional filter
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
console.log(stats.primaryCurrency); // e.g. EUR
|
|
590
|
+
console.log(stats.totals.total); // e.g. 120000.0000
|
|
591
|
+
for (const bucket of stats.timeSeries) {
|
|
592
|
+
console.log(`${bucket.bucket}: ${bucket.total}`); // 2026-01: 10800.0000
|
|
593
|
+
}
|
|
594
|
+
for (const row of stats.byCountry) {
|
|
595
|
+
console.log(`${row.country} → ${row.total}`); // 'unknown' groups NULL partners
|
|
596
|
+
}
|
|
597
|
+
```
|
|
598
|
+
|
|
599
|
+
---
|
|
600
|
+
|
|
304
601
|
## 📜 Certificates
|
|
305
602
|
|
|
306
603
|
### Single Certificate (PDF)
|