@taxora/sdk 1.1.0 → 1.2.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/dist/index.d.cts CHANGED
@@ -22,6 +22,75 @@ interface HttpClientInterface {
22
22
  request(method: string, url: string, options?: RequestInit): Promise<Response>;
23
23
  }
24
24
 
25
+ /**
26
+ * How the SDK retries transient failures.
27
+ *
28
+ * Only gateway-level failures are retried by default (502/503/504): those come
29
+ * from the infrastructure in front of the API, not from the API itself, and a
30
+ * second attempt a moment later usually succeeds. Everything else — 4xx, and any
31
+ * answer the API produced itself — is surfaced immediately.
32
+ *
33
+ * On top of that, a 429 is retried when the API answers with a `Retry-After`
34
+ * telling us how long to wait, and connection-level failures (reset connection,
35
+ * client-side timeout, DNS hiccup) are retried as well — those never reached the
36
+ * API's answer either.
37
+ *
38
+ * Retries are opt-in per operation and are only applied to read-only calls
39
+ * (lookups, downloads, polling). Calls that change state or cost quota — booking
40
+ * a transaction, starting an enrichment job, triggering a certificate export —
41
+ * are never retried automatically, because a gateway timeout does not tell us
42
+ * whether the API already processed the request.
43
+ */
44
+ interface RetryPolicyOptions {
45
+ /** Total attempts including the first one (1 disables retrying). */
46
+ maxAttempts?: number;
47
+ initialDelayMs?: number;
48
+ backoffMultiplier?: number;
49
+ maxDelayMs?: number;
50
+ retryableStatusCodes?: number[];
51
+ /** Honour a `Retry-After` header instead of the backoff, and retry a 429 that carries one. */
52
+ respectRetryAfter?: boolean;
53
+ /** Longest wait accepted from `Retry-After`; a longer one means we give up instead of blocking. */
54
+ maxRetryAfterMs?: number;
55
+ /** Retry connection-level failures (reset connection, client-side timeout, DNS hiccup). */
56
+ retryOnNetworkErrors?: boolean;
57
+ /** Receives the delay in milliseconds; defaults to setTimeout. */
58
+ sleeper?: (delayMs: number) => Promise<void> | void;
59
+ }
60
+ declare class RetryPolicy {
61
+ readonly maxAttempts: number;
62
+ readonly initialDelayMs: number;
63
+ readonly backoffMultiplier: number;
64
+ readonly maxDelayMs: number;
65
+ readonly retryableStatusCodes: number[];
66
+ readonly respectRetryAfter: boolean;
67
+ readonly maxRetryAfterMs: number;
68
+ readonly retryOnNetworkErrors: boolean;
69
+ private readonly sleeper;
70
+ constructor(options?: RetryPolicyOptions);
71
+ /** No retrying at all — every failure is surfaced on the first attempt. */
72
+ static disabled(): RetryPolicy;
73
+ /** Retry immediately, without waiting (mainly useful in tests). */
74
+ static withoutDelay(maxAttempts?: number): RetryPolicy;
75
+ isRetryable(statusCode: number, retryAfterHeader?: string | null): boolean;
76
+ /** @param attempt Number of attempts made so far (1 = the first one just failed). */
77
+ shouldRetry(statusCode: number, attempt: number, retryAfterHeader?: string | null): boolean;
78
+ /**
79
+ * A connection-level failure: the request never got an answer, so repeating it
80
+ * is as safe as repeating a gateway timeout.
81
+ */
82
+ isRetryableError(error: unknown): boolean;
83
+ shouldRetryError(error: unknown, attempt: number): boolean;
84
+ /**
85
+ * The wait the API asked for, in milliseconds — seconds or HTTP-date form — or
86
+ * null when there is none, it cannot be parsed, or the header is ignored.
87
+ */
88
+ retryAfterMs(header: string | null | undefined, now?: Date): number | null;
89
+ /** The wait before the next attempt: what the API asked for, else exponential backoff. */
90
+ delayMsForAttempt(attempt: number, retryAfterHeader?: string | null): number;
91
+ sleepBeforeRetry(attempt: number, retryAfterHeader?: string | null): Promise<void>;
92
+ }
93
+
25
94
  interface TokenStorageInterface {
26
95
  get(): Token | null;
27
96
  set(token: Token): void;
@@ -33,7 +102,8 @@ declare class AuthEndpoint {
33
102
  private readonly apiKey;
34
103
  private readonly tokenStorage;
35
104
  private readonly httpClient;
36
- constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface);
105
+ private readonly retryPolicy;
106
+ constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface, retryPolicy?: RetryPolicy);
37
107
  login(identifier: string, password: string, device?: string, loginIdentifier?: LoginIdentifier): Promise<Token>;
38
108
  loginWithClientId(clientId: string, password: string, device?: string): Promise<Token>;
39
109
  refresh(): Promise<Token>;
@@ -69,7 +139,8 @@ declare class CompanyEndpoint {
69
139
  private readonly apiKey;
70
140
  private readonly tokenStorage;
71
141
  private readonly httpClient;
72
- constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface);
142
+ private readonly retryPolicy;
143
+ constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface, retryPolicy?: RetryPolicy);
73
144
  get(): Promise<CompanyResource>;
74
145
  }
75
146
 
@@ -553,7 +624,8 @@ declare class EReportingEndpoint {
553
624
  private readonly apiKey;
554
625
  private readonly tokenStorage;
555
626
  private readonly httpClient;
556
- constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface);
627
+ private readonly retryPolicy;
628
+ constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface, retryPolicy?: RetryPolicy);
557
629
  /**
558
630
  * Create a compliance enrollment and provision the provider account. By
559
631
  * default the DGFiP regime is activated right away; pass `autoActivate: false`
@@ -678,6 +750,30 @@ declare class EReportingEndpoint {
678
750
  private formatDate;
679
751
  }
680
752
 
753
+ /**
754
+ * How hard a Smart Enrichment lookup is allowed to search.
755
+ *
756
+ * `default` searches with one AI provider and consults a second one only when the first finds
757
+ * nothing — one paid search per lookup in the common case.
758
+ *
759
+ * `complex` has two independent AI providers search from the start, and every VAT number either of
760
+ * them proposes is put through the tax authority's checks until one confirms. Noticeably better on
761
+ * hard lookups — companies whose VAT only appears in a website Impressum — and it costs more per
762
+ * lookup, so it is opt-in per request rather than the default.
763
+ */
764
+ declare const SmartEnrichmentMode: {
765
+ readonly DEFAULT: "default";
766
+ readonly COMPLEX: "complex";
767
+ };
768
+ type SmartEnrichmentMode = (typeof SmartEnrichmentMode)[keyof typeof SmartEnrichmentMode];
769
+ /**
770
+ * Coercion for values coming back from the API. Returns undefined for anything unrecognized —
771
+ * unlike the status enum there is no `unknown` member to fall back to, and inventing a mode would
772
+ * misreport how a lookup actually ran.
773
+ */
774
+ declare function toSmartEnrichmentMode(value: unknown): SmartEnrichmentMode | undefined;
775
+ declare function describeSmartEnrichmentMode(mode: SmartEnrichmentMode): string;
776
+
681
777
  declare const SmartEnrichmentStatus: {
682
778
  readonly FOUND: "found";
683
779
  readonly NO_VAT_EXISTS: "no_vat_exists";
@@ -693,6 +789,34 @@ type SmartEnrichmentStatus = (typeof SmartEnrichmentStatus)[keyof typeof SmartEn
693
789
  declare function toSmartEnrichmentStatus(value: unknown): SmartEnrichmentStatus;
694
790
  declare function describeSmartEnrichmentStatus(status: SmartEnrichmentStatus): string;
695
791
 
792
+ /** What one AI provider independently concluded, when more than one searched. */
793
+ interface SmartEnrichmentProviderVerdict {
794
+ provider: string;
795
+ model: string | null;
796
+ status: string;
797
+ vatNumber: string | null;
798
+ confidence: number | null;
799
+ /** False when the model answered from memory instead of an actual web search. */
800
+ grounded: boolean;
801
+ sourceUrl: string | null;
802
+ }
803
+ /**
804
+ * What the API made of the address you submitted. The actionable part is `warnings`:
805
+ * `postal_code_city_mismatch` / `postal_code_unassigned` / `postal_code_format_invalid` mean the
806
+ * postal code in your source record is wrong, and `derivedPlace` is where it actually points —
807
+ * worth writing back into your own data.
808
+ */
809
+ interface SmartEnrichmentAddressQuality {
810
+ providedPostalCode: string | null;
811
+ providedCity: string | null;
812
+ postalCodeFormatValid: boolean | null;
813
+ postalCodeAssigned: boolean | null;
814
+ /** False when the postal code was excluded from the search because it could not be trusted. */
815
+ postalCodeTrusted: boolean;
816
+ derivedPlace: string | null;
817
+ cityMatchesPostalCode: boolean | null;
818
+ warnings: string[];
819
+ }
696
820
  /**
697
821
  * Result of resolving one reverse-VAT lookup (company name + address → VAT + confidence).
698
822
  * Mirrors the API response item shape (camelCase keys).
@@ -707,9 +831,26 @@ declare class SmartEnrichmentResource {
707
831
  readonly matchedAddress: string | undefined;
708
832
  readonly country: string | undefined;
709
833
  readonly source: string | undefined;
834
+ /** The search mode this lookup actually ran in. */
835
+ readonly mode: SmartEnrichmentMode | undefined;
836
+ /**
837
+ * What each AI provider independently concluded. Populated when more than one searched
838
+ * (complex mode, or a fallback). Two entries reporting the same vatNumber is the strongest
839
+ * confirmation this layer produces.
840
+ */
841
+ readonly providerVerdicts: SmartEnrichmentProviderVerdict[];
842
+ readonly addressQuality: SmartEnrichmentAddressQuality | undefined;
710
843
  constructor(status: SmartEnrichmentStatus, vatNumber: string | undefined, vatType: string | undefined, confidence: number | undefined, matchedCompanyName: string | undefined,
711
844
  /** Official/registered address of the matched company, when available. */
712
- matchedAddress: string | undefined, country: string | undefined, source: string | undefined);
845
+ matchedAddress: string | undefined, country: string | undefined, source: string | undefined,
846
+ /** The search mode this lookup actually ran in. */
847
+ mode?: SmartEnrichmentMode | undefined,
848
+ /**
849
+ * What each AI provider independently concluded. Populated when more than one searched
850
+ * (complex mode, or a fallback). Two entries reporting the same vatNumber is the strongest
851
+ * confirmation this layer produces.
852
+ */
853
+ providerVerdicts?: SmartEnrichmentProviderVerdict[], addressQuality?: SmartEnrichmentAddressQuality | undefined);
713
854
  static fromArray(data: Record<string, unknown>): SmartEnrichmentResource;
714
855
  toArray(): Record<string, unknown>;
715
856
  }
@@ -850,6 +991,11 @@ interface SmartEnrichmentInput {
850
991
  street?: string;
851
992
  postalCode?: string;
852
993
  city?: string;
994
+ /**
995
+ * Search depth. Omit to use the server default (`default`). `complex` searches with two
996
+ * independent providers and cross-checks them — better hit rate, higher cost per lookup.
997
+ */
998
+ mode?: SmartEnrichmentMode;
853
999
  }
854
1000
  /** One recent monthly billing entry, exposed on the usage response for transparency. */
855
1001
  interface SmartEnrichmentUsageHistoryEntry {
@@ -912,7 +1058,8 @@ declare class SmartEnrichmentEndpoint {
912
1058
  private readonly apiKey;
913
1059
  private readonly tokenStorage;
914
1060
  private readonly httpClient;
915
- constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface);
1061
+ private readonly retryPolicy;
1062
+ constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface, retryPolicy?: RetryPolicy);
916
1063
  /**
917
1064
  * Single reverse lookup. Returns a confident result synchronously, or a `processing`
918
1065
  * job (poll with get(job.jobId) or waitForCompletion(job.jobId)) when deeper resolution
@@ -922,8 +1069,11 @@ declare class SmartEnrichmentEndpoint {
922
1069
  /**
923
1070
  * Bulk batch — always async. Returns a `processing` job; results arrive via the
924
1071
  * `enrichment.completed` webhook and are retrievable via get(job.jobId).
1072
+ *
1073
+ * @param mode Search depth for every row in the batch. A row carrying its own `mode` overrides
1074
+ * it, so a caller can pay for `complex` on just the rows that need it.
925
1075
  */
926
- bulkLookup(items: SmartEnrichmentInput[]): Promise<SmartEnrichmentJob>;
1076
+ bulkLookup(items: SmartEnrichmentInput[], mode?: SmartEnrichmentMode): Promise<SmartEnrichmentJob>;
927
1077
  /** Poll a job (single or bulk) by its id. */
928
1078
  get(jobId: string): Promise<SmartEnrichmentJob>;
929
1079
  /**
@@ -1139,9 +1289,9 @@ declare class VatEndpoint {
1139
1289
  private readonly apiKey;
1140
1290
  private readonly tokenStorage;
1141
1291
  private readonly httpClient;
1142
- constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface);
1292
+ private readonly retryPolicy;
1293
+ constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface, retryPolicy?: RetryPolicy);
1143
1294
  validate(vatUid: string, companyName?: string, provider?: string, addressInput?: VatValidationAddressInput | Record<string, string>): Promise<VatResource>;
1144
- private validateWithGatewayRetry;
1145
1295
  validateMultiple(vatUids: string[], companyNames?: string[], provider?: string): Promise<VatCollection>;
1146
1296
  validateSchema(vatUid: string): Promise<Record<string, unknown>>;
1147
1297
  state(vatUid: string): Promise<VatResource>;
@@ -1171,6 +1321,8 @@ interface TaxoraClientOptions {
1171
1321
  environment?: Environment;
1172
1322
  tokenStorage?: TokenStorageInterface;
1173
1323
  httpClient?: HttpClientInterface;
1324
+ /** Retry behaviour for transient gateway failures; defaults to 3 attempts with backoff. */
1325
+ retryPolicy?: RetryPolicy;
1174
1326
  }
1175
1327
  declare class TaxoraClient {
1176
1328
  readonly auth: AuthEndpoint;
@@ -1204,9 +1356,17 @@ declare class AuthenticationException extends TaxoraException {
1204
1356
  declare class HttpException extends TaxoraException {
1205
1357
  readonly statusCode: number;
1206
1358
  readonly responseBody: string;
1207
- constructor(message: string, statusCode: number, responseBody: string, context?: Record<string, unknown>);
1359
+ /** Raw `Retry-After` header of the failed response, when it carried one. */
1360
+ readonly retryAfter: string | null;
1361
+ constructor(message: string, statusCode: number, responseBody: string, context?: Record<string, unknown>, retryAfter?: string | null);
1362
+ /**
1363
+ * The final failure after the SDK retried a transient gateway error, e.g.
1364
+ * "Taxora VAT validation failed after 3 attempts (HTTP 504 Gateway Timeout)."
1365
+ */
1366
+ static afterAttempts(operation: string, attempts: number, last: HttpException): HttpException;
1208
1367
  getStatusCode(): number;
1209
1368
  getResponseBody(): string;
1369
+ getRetryAfter(): string | null;
1210
1370
  }
1211
1371
 
1212
1372
  declare class ValidationException extends TaxoraException {
@@ -1216,6 +1376,24 @@ declare class ValidationException extends TaxoraException {
1216
1376
  getErrors(): Record<string, string[]>;
1217
1377
  }
1218
1378
 
1379
+ /**
1380
+ * Derives a short, human readable exception message from an API response body.
1381
+ *
1382
+ * A response body is never used verbatim as an exception message: the gateways
1383
+ * in front of the API (DigitalOcean App Platform, load balancers, proxies)
1384
+ * answer 5xx with a full HTML error page, and that page would otherwise end up
1385
+ * in every log line and stack trace of the integrating application.
1386
+ *
1387
+ * Only the human readable message of a JSON error body is used; everything else
1388
+ * (HTML, empty bodies, binary payloads) falls back to the HTTP status line. The
1389
+ * untouched body always stays available via `HttpException.getResponseBody()`.
1390
+ */
1391
+ /**
1392
+ * The message for a failed request: the API message when the body carries one,
1393
+ * otherwise a description of the HTTP status.
1394
+ */
1395
+ declare function describeApiError(body: string | null | undefined, statusCode: number): string;
1396
+
1219
1397
  declare class InMemoryTokenStorage implements TokenStorageInterface {
1220
1398
  private token;
1221
1399
  get(): Token | null;
@@ -1242,4 +1420,4 @@ declare class AuthRetryHttpClient implements HttpClientInterface {
1242
1420
  private injectAuthHeader;
1243
1421
  }
1244
1422
 
1245
- export { ApiVersion, AuthEndpoint, AuthRetryHttpClient, AuthenticationException, CompanyAddress, CompanyEndpoint, type CompanyResource, ComplianceEnrollment, ComplianceEnrollmentPage, ComplianceEnrollmentStatus, type ComplianceEnterpriseSize, type ComplianceInvoiceLineInput, type ComplianceInvoiceTaxInput, ComplianceTaxReport, ComplianceTaxReportPage, type ComplianceTaxReportRequestState, ComplianceTaxReportState, ComplianceTransaction, type ComplianceTransactionFilters, ComplianceTransactionPage, type ComplianceTransactionRequestState, type ComplianceTransactionRequestType, ComplianceTransactionState, ComplianceTransactionType, type ComplianceTypeOperation, type CreateComplianceEnrollmentInput, type CreateComplianceTransactionInput, type EReportingAccessRequestInput, EReportingEndpoint, type EReportingInterval, Environment, Environment as EnvironmentType, FetchHttpClient, type HttpClientInterface, HttpException, ImportResult, InMemoryTokenStorage, Language, LoginIdentifier, ProviderDocument, ProviderDocumentLine, type RevenueCountryBreakdown, type RevenueCurrencyBreakdown, type RevenueStateBreakdown, RevenueStatistics, type RevenueStatisticsFilters, type RevenueTimeBucket, type RevenueTotals, type RevenueTypeBreakdown, ScoreBreakdown, SireneLookupResult, type SmartEnrichmentConfidenceBucket, SmartEnrichmentEndpoint, type SmartEnrichmentExportOptions, type SmartEnrichmentHistoryInput, SmartEnrichmentHistoryPage, SmartEnrichmentHistoryRow, type SmartEnrichmentHistoryStats, type SmartEnrichmentInput, SmartEnrichmentJob, type SmartEnrichmentOutcomeBreakdown, SmartEnrichmentResource, type SmartEnrichmentSourceBreakdown, SmartEnrichmentStatistics, type SmartEnrichmentStatisticsInterval, type SmartEnrichmentStatisticsOptions, type SmartEnrichmentStatisticsRange, type SmartEnrichmentStatisticsTotals, SmartEnrichmentStatus, type SmartEnrichmentTimeBucket, type SmartEnrichmentUsage, type SmartEnrichmentUsageHistoryEntry, type SmartEnrichmentWaitOptions, TaxoraClient, TaxoraClientFactory, type TaxoraClientOptions, TaxoraException, Token, type TokenStorageInterface, type UpdateComplianceTransactionInput, ValidationException, VatCertificateExport, VatCollection, VatEndpoint, type VatRate, VatRates, VatResource, VatState, VatValidationAddressInput, describeComplianceEnrollmentStatus, describeComplianceTaxReportState, describeComplianceTransactionState, describeComplianceTransactionType, describeSmartEnrichmentStatus, describeVatState, getBaseUrl, getFailedVatStates, toComplianceEnrollmentStatus, toComplianceTaxReportState, toComplianceTransactionState, toComplianceTransactionType, toSmartEnrichmentStatus };
1423
+ export { ApiVersion, AuthEndpoint, AuthRetryHttpClient, AuthenticationException, CompanyAddress, CompanyEndpoint, type CompanyResource, ComplianceEnrollment, ComplianceEnrollmentPage, ComplianceEnrollmentStatus, type ComplianceEnterpriseSize, type ComplianceInvoiceLineInput, type ComplianceInvoiceTaxInput, ComplianceTaxReport, ComplianceTaxReportPage, type ComplianceTaxReportRequestState, ComplianceTaxReportState, ComplianceTransaction, type ComplianceTransactionFilters, ComplianceTransactionPage, type ComplianceTransactionRequestState, type ComplianceTransactionRequestType, ComplianceTransactionState, ComplianceTransactionType, type ComplianceTypeOperation, type CreateComplianceEnrollmentInput, type CreateComplianceTransactionInput, type EReportingAccessRequestInput, EReportingEndpoint, type EReportingInterval, Environment, Environment as EnvironmentType, FetchHttpClient, type HttpClientInterface, HttpException, ImportResult, InMemoryTokenStorage, Language, LoginIdentifier, ProviderDocument, ProviderDocumentLine, RetryPolicy, type RetryPolicyOptions, type RevenueCountryBreakdown, type RevenueCurrencyBreakdown, type RevenueStateBreakdown, RevenueStatistics, type RevenueStatisticsFilters, type RevenueTimeBucket, type RevenueTotals, type RevenueTypeBreakdown, ScoreBreakdown, SireneLookupResult, type SmartEnrichmentAddressQuality, type SmartEnrichmentConfidenceBucket, SmartEnrichmentEndpoint, type SmartEnrichmentExportOptions, type SmartEnrichmentHistoryInput, SmartEnrichmentHistoryPage, SmartEnrichmentHistoryRow, type SmartEnrichmentHistoryStats, type SmartEnrichmentInput, SmartEnrichmentJob, SmartEnrichmentMode, type SmartEnrichmentOutcomeBreakdown, type SmartEnrichmentProviderVerdict, SmartEnrichmentResource, type SmartEnrichmentSourceBreakdown, SmartEnrichmentStatistics, type SmartEnrichmentStatisticsInterval, type SmartEnrichmentStatisticsOptions, type SmartEnrichmentStatisticsRange, type SmartEnrichmentStatisticsTotals, SmartEnrichmentStatus, type SmartEnrichmentTimeBucket, type SmartEnrichmentUsage, type SmartEnrichmentUsageHistoryEntry, type SmartEnrichmentWaitOptions, TaxoraClient, TaxoraClientFactory, type TaxoraClientOptions, TaxoraException, Token, type TokenStorageInterface, type UpdateComplianceTransactionInput, ValidationException, VatCertificateExport, VatCollection, VatEndpoint, type VatRate, VatRates, VatResource, VatState, VatValidationAddressInput, describeApiError, describeComplianceEnrollmentStatus, describeComplianceTaxReportState, describeComplianceTransactionState, describeComplianceTransactionType, describeSmartEnrichmentMode, describeSmartEnrichmentStatus, describeVatState, getBaseUrl, getFailedVatStates, toComplianceEnrollmentStatus, toComplianceTaxReportState, toComplianceTransactionState, toComplianceTransactionType, toSmartEnrichmentMode, toSmartEnrichmentStatus };
package/dist/index.d.ts CHANGED
@@ -22,6 +22,75 @@ interface HttpClientInterface {
22
22
  request(method: string, url: string, options?: RequestInit): Promise<Response>;
23
23
  }
24
24
 
25
+ /**
26
+ * How the SDK retries transient failures.
27
+ *
28
+ * Only gateway-level failures are retried by default (502/503/504): those come
29
+ * from the infrastructure in front of the API, not from the API itself, and a
30
+ * second attempt a moment later usually succeeds. Everything else — 4xx, and any
31
+ * answer the API produced itself — is surfaced immediately.
32
+ *
33
+ * On top of that, a 429 is retried when the API answers with a `Retry-After`
34
+ * telling us how long to wait, and connection-level failures (reset connection,
35
+ * client-side timeout, DNS hiccup) are retried as well — those never reached the
36
+ * API's answer either.
37
+ *
38
+ * Retries are opt-in per operation and are only applied to read-only calls
39
+ * (lookups, downloads, polling). Calls that change state or cost quota — booking
40
+ * a transaction, starting an enrichment job, triggering a certificate export —
41
+ * are never retried automatically, because a gateway timeout does not tell us
42
+ * whether the API already processed the request.
43
+ */
44
+ interface RetryPolicyOptions {
45
+ /** Total attempts including the first one (1 disables retrying). */
46
+ maxAttempts?: number;
47
+ initialDelayMs?: number;
48
+ backoffMultiplier?: number;
49
+ maxDelayMs?: number;
50
+ retryableStatusCodes?: number[];
51
+ /** Honour a `Retry-After` header instead of the backoff, and retry a 429 that carries one. */
52
+ respectRetryAfter?: boolean;
53
+ /** Longest wait accepted from `Retry-After`; a longer one means we give up instead of blocking. */
54
+ maxRetryAfterMs?: number;
55
+ /** Retry connection-level failures (reset connection, client-side timeout, DNS hiccup). */
56
+ retryOnNetworkErrors?: boolean;
57
+ /** Receives the delay in milliseconds; defaults to setTimeout. */
58
+ sleeper?: (delayMs: number) => Promise<void> | void;
59
+ }
60
+ declare class RetryPolicy {
61
+ readonly maxAttempts: number;
62
+ readonly initialDelayMs: number;
63
+ readonly backoffMultiplier: number;
64
+ readonly maxDelayMs: number;
65
+ readonly retryableStatusCodes: number[];
66
+ readonly respectRetryAfter: boolean;
67
+ readonly maxRetryAfterMs: number;
68
+ readonly retryOnNetworkErrors: boolean;
69
+ private readonly sleeper;
70
+ constructor(options?: RetryPolicyOptions);
71
+ /** No retrying at all — every failure is surfaced on the first attempt. */
72
+ static disabled(): RetryPolicy;
73
+ /** Retry immediately, without waiting (mainly useful in tests). */
74
+ static withoutDelay(maxAttempts?: number): RetryPolicy;
75
+ isRetryable(statusCode: number, retryAfterHeader?: string | null): boolean;
76
+ /** @param attempt Number of attempts made so far (1 = the first one just failed). */
77
+ shouldRetry(statusCode: number, attempt: number, retryAfterHeader?: string | null): boolean;
78
+ /**
79
+ * A connection-level failure: the request never got an answer, so repeating it
80
+ * is as safe as repeating a gateway timeout.
81
+ */
82
+ isRetryableError(error: unknown): boolean;
83
+ shouldRetryError(error: unknown, attempt: number): boolean;
84
+ /**
85
+ * The wait the API asked for, in milliseconds — seconds or HTTP-date form — or
86
+ * null when there is none, it cannot be parsed, or the header is ignored.
87
+ */
88
+ retryAfterMs(header: string | null | undefined, now?: Date): number | null;
89
+ /** The wait before the next attempt: what the API asked for, else exponential backoff. */
90
+ delayMsForAttempt(attempt: number, retryAfterHeader?: string | null): number;
91
+ sleepBeforeRetry(attempt: number, retryAfterHeader?: string | null): Promise<void>;
92
+ }
93
+
25
94
  interface TokenStorageInterface {
26
95
  get(): Token | null;
27
96
  set(token: Token): void;
@@ -33,7 +102,8 @@ declare class AuthEndpoint {
33
102
  private readonly apiKey;
34
103
  private readonly tokenStorage;
35
104
  private readonly httpClient;
36
- constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface);
105
+ private readonly retryPolicy;
106
+ constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface, retryPolicy?: RetryPolicy);
37
107
  login(identifier: string, password: string, device?: string, loginIdentifier?: LoginIdentifier): Promise<Token>;
38
108
  loginWithClientId(clientId: string, password: string, device?: string): Promise<Token>;
39
109
  refresh(): Promise<Token>;
@@ -69,7 +139,8 @@ declare class CompanyEndpoint {
69
139
  private readonly apiKey;
70
140
  private readonly tokenStorage;
71
141
  private readonly httpClient;
72
- constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface);
142
+ private readonly retryPolicy;
143
+ constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface, retryPolicy?: RetryPolicy);
73
144
  get(): Promise<CompanyResource>;
74
145
  }
75
146
 
@@ -553,7 +624,8 @@ declare class EReportingEndpoint {
553
624
  private readonly apiKey;
554
625
  private readonly tokenStorage;
555
626
  private readonly httpClient;
556
- constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface);
627
+ private readonly retryPolicy;
628
+ constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface, retryPolicy?: RetryPolicy);
557
629
  /**
558
630
  * Create a compliance enrollment and provision the provider account. By
559
631
  * default the DGFiP regime is activated right away; pass `autoActivate: false`
@@ -678,6 +750,30 @@ declare class EReportingEndpoint {
678
750
  private formatDate;
679
751
  }
680
752
 
753
+ /**
754
+ * How hard a Smart Enrichment lookup is allowed to search.
755
+ *
756
+ * `default` searches with one AI provider and consults a second one only when the first finds
757
+ * nothing — one paid search per lookup in the common case.
758
+ *
759
+ * `complex` has two independent AI providers search from the start, and every VAT number either of
760
+ * them proposes is put through the tax authority's checks until one confirms. Noticeably better on
761
+ * hard lookups — companies whose VAT only appears in a website Impressum — and it costs more per
762
+ * lookup, so it is opt-in per request rather than the default.
763
+ */
764
+ declare const SmartEnrichmentMode: {
765
+ readonly DEFAULT: "default";
766
+ readonly COMPLEX: "complex";
767
+ };
768
+ type SmartEnrichmentMode = (typeof SmartEnrichmentMode)[keyof typeof SmartEnrichmentMode];
769
+ /**
770
+ * Coercion for values coming back from the API. Returns undefined for anything unrecognized —
771
+ * unlike the status enum there is no `unknown` member to fall back to, and inventing a mode would
772
+ * misreport how a lookup actually ran.
773
+ */
774
+ declare function toSmartEnrichmentMode(value: unknown): SmartEnrichmentMode | undefined;
775
+ declare function describeSmartEnrichmentMode(mode: SmartEnrichmentMode): string;
776
+
681
777
  declare const SmartEnrichmentStatus: {
682
778
  readonly FOUND: "found";
683
779
  readonly NO_VAT_EXISTS: "no_vat_exists";
@@ -693,6 +789,34 @@ type SmartEnrichmentStatus = (typeof SmartEnrichmentStatus)[keyof typeof SmartEn
693
789
  declare function toSmartEnrichmentStatus(value: unknown): SmartEnrichmentStatus;
694
790
  declare function describeSmartEnrichmentStatus(status: SmartEnrichmentStatus): string;
695
791
 
792
+ /** What one AI provider independently concluded, when more than one searched. */
793
+ interface SmartEnrichmentProviderVerdict {
794
+ provider: string;
795
+ model: string | null;
796
+ status: string;
797
+ vatNumber: string | null;
798
+ confidence: number | null;
799
+ /** False when the model answered from memory instead of an actual web search. */
800
+ grounded: boolean;
801
+ sourceUrl: string | null;
802
+ }
803
+ /**
804
+ * What the API made of the address you submitted. The actionable part is `warnings`:
805
+ * `postal_code_city_mismatch` / `postal_code_unassigned` / `postal_code_format_invalid` mean the
806
+ * postal code in your source record is wrong, and `derivedPlace` is where it actually points —
807
+ * worth writing back into your own data.
808
+ */
809
+ interface SmartEnrichmentAddressQuality {
810
+ providedPostalCode: string | null;
811
+ providedCity: string | null;
812
+ postalCodeFormatValid: boolean | null;
813
+ postalCodeAssigned: boolean | null;
814
+ /** False when the postal code was excluded from the search because it could not be trusted. */
815
+ postalCodeTrusted: boolean;
816
+ derivedPlace: string | null;
817
+ cityMatchesPostalCode: boolean | null;
818
+ warnings: string[];
819
+ }
696
820
  /**
697
821
  * Result of resolving one reverse-VAT lookup (company name + address → VAT + confidence).
698
822
  * Mirrors the API response item shape (camelCase keys).
@@ -707,9 +831,26 @@ declare class SmartEnrichmentResource {
707
831
  readonly matchedAddress: string | undefined;
708
832
  readonly country: string | undefined;
709
833
  readonly source: string | undefined;
834
+ /** The search mode this lookup actually ran in. */
835
+ readonly mode: SmartEnrichmentMode | undefined;
836
+ /**
837
+ * What each AI provider independently concluded. Populated when more than one searched
838
+ * (complex mode, or a fallback). Two entries reporting the same vatNumber is the strongest
839
+ * confirmation this layer produces.
840
+ */
841
+ readonly providerVerdicts: SmartEnrichmentProviderVerdict[];
842
+ readonly addressQuality: SmartEnrichmentAddressQuality | undefined;
710
843
  constructor(status: SmartEnrichmentStatus, vatNumber: string | undefined, vatType: string | undefined, confidence: number | undefined, matchedCompanyName: string | undefined,
711
844
  /** Official/registered address of the matched company, when available. */
712
- matchedAddress: string | undefined, country: string | undefined, source: string | undefined);
845
+ matchedAddress: string | undefined, country: string | undefined, source: string | undefined,
846
+ /** The search mode this lookup actually ran in. */
847
+ mode?: SmartEnrichmentMode | undefined,
848
+ /**
849
+ * What each AI provider independently concluded. Populated when more than one searched
850
+ * (complex mode, or a fallback). Two entries reporting the same vatNumber is the strongest
851
+ * confirmation this layer produces.
852
+ */
853
+ providerVerdicts?: SmartEnrichmentProviderVerdict[], addressQuality?: SmartEnrichmentAddressQuality | undefined);
713
854
  static fromArray(data: Record<string, unknown>): SmartEnrichmentResource;
714
855
  toArray(): Record<string, unknown>;
715
856
  }
@@ -850,6 +991,11 @@ interface SmartEnrichmentInput {
850
991
  street?: string;
851
992
  postalCode?: string;
852
993
  city?: string;
994
+ /**
995
+ * Search depth. Omit to use the server default (`default`). `complex` searches with two
996
+ * independent providers and cross-checks them — better hit rate, higher cost per lookup.
997
+ */
998
+ mode?: SmartEnrichmentMode;
853
999
  }
854
1000
  /** One recent monthly billing entry, exposed on the usage response for transparency. */
855
1001
  interface SmartEnrichmentUsageHistoryEntry {
@@ -912,7 +1058,8 @@ declare class SmartEnrichmentEndpoint {
912
1058
  private readonly apiKey;
913
1059
  private readonly tokenStorage;
914
1060
  private readonly httpClient;
915
- constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface);
1061
+ private readonly retryPolicy;
1062
+ constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface, retryPolicy?: RetryPolicy);
916
1063
  /**
917
1064
  * Single reverse lookup. Returns a confident result synchronously, or a `processing`
918
1065
  * job (poll with get(job.jobId) or waitForCompletion(job.jobId)) when deeper resolution
@@ -922,8 +1069,11 @@ declare class SmartEnrichmentEndpoint {
922
1069
  /**
923
1070
  * Bulk batch — always async. Returns a `processing` job; results arrive via the
924
1071
  * `enrichment.completed` webhook and are retrievable via get(job.jobId).
1072
+ *
1073
+ * @param mode Search depth for every row in the batch. A row carrying its own `mode` overrides
1074
+ * it, so a caller can pay for `complex` on just the rows that need it.
925
1075
  */
926
- bulkLookup(items: SmartEnrichmentInput[]): Promise<SmartEnrichmentJob>;
1076
+ bulkLookup(items: SmartEnrichmentInput[], mode?: SmartEnrichmentMode): Promise<SmartEnrichmentJob>;
927
1077
  /** Poll a job (single or bulk) by its id. */
928
1078
  get(jobId: string): Promise<SmartEnrichmentJob>;
929
1079
  /**
@@ -1139,9 +1289,9 @@ declare class VatEndpoint {
1139
1289
  private readonly apiKey;
1140
1290
  private readonly tokenStorage;
1141
1291
  private readonly httpClient;
1142
- constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface);
1292
+ private readonly retryPolicy;
1293
+ constructor(baseUrl: string, apiKey: string, tokenStorage: TokenStorageInterface, httpClient: HttpClientInterface, retryPolicy?: RetryPolicy);
1143
1294
  validate(vatUid: string, companyName?: string, provider?: string, addressInput?: VatValidationAddressInput | Record<string, string>): Promise<VatResource>;
1144
- private validateWithGatewayRetry;
1145
1295
  validateMultiple(vatUids: string[], companyNames?: string[], provider?: string): Promise<VatCollection>;
1146
1296
  validateSchema(vatUid: string): Promise<Record<string, unknown>>;
1147
1297
  state(vatUid: string): Promise<VatResource>;
@@ -1171,6 +1321,8 @@ interface TaxoraClientOptions {
1171
1321
  environment?: Environment;
1172
1322
  tokenStorage?: TokenStorageInterface;
1173
1323
  httpClient?: HttpClientInterface;
1324
+ /** Retry behaviour for transient gateway failures; defaults to 3 attempts with backoff. */
1325
+ retryPolicy?: RetryPolicy;
1174
1326
  }
1175
1327
  declare class TaxoraClient {
1176
1328
  readonly auth: AuthEndpoint;
@@ -1204,9 +1356,17 @@ declare class AuthenticationException extends TaxoraException {
1204
1356
  declare class HttpException extends TaxoraException {
1205
1357
  readonly statusCode: number;
1206
1358
  readonly responseBody: string;
1207
- constructor(message: string, statusCode: number, responseBody: string, context?: Record<string, unknown>);
1359
+ /** Raw `Retry-After` header of the failed response, when it carried one. */
1360
+ readonly retryAfter: string | null;
1361
+ constructor(message: string, statusCode: number, responseBody: string, context?: Record<string, unknown>, retryAfter?: string | null);
1362
+ /**
1363
+ * The final failure after the SDK retried a transient gateway error, e.g.
1364
+ * "Taxora VAT validation failed after 3 attempts (HTTP 504 Gateway Timeout)."
1365
+ */
1366
+ static afterAttempts(operation: string, attempts: number, last: HttpException): HttpException;
1208
1367
  getStatusCode(): number;
1209
1368
  getResponseBody(): string;
1369
+ getRetryAfter(): string | null;
1210
1370
  }
1211
1371
 
1212
1372
  declare class ValidationException extends TaxoraException {
@@ -1216,6 +1376,24 @@ declare class ValidationException extends TaxoraException {
1216
1376
  getErrors(): Record<string, string[]>;
1217
1377
  }
1218
1378
 
1379
+ /**
1380
+ * Derives a short, human readable exception message from an API response body.
1381
+ *
1382
+ * A response body is never used verbatim as an exception message: the gateways
1383
+ * in front of the API (DigitalOcean App Platform, load balancers, proxies)
1384
+ * answer 5xx with a full HTML error page, and that page would otherwise end up
1385
+ * in every log line and stack trace of the integrating application.
1386
+ *
1387
+ * Only the human readable message of a JSON error body is used; everything else
1388
+ * (HTML, empty bodies, binary payloads) falls back to the HTTP status line. The
1389
+ * untouched body always stays available via `HttpException.getResponseBody()`.
1390
+ */
1391
+ /**
1392
+ * The message for a failed request: the API message when the body carries one,
1393
+ * otherwise a description of the HTTP status.
1394
+ */
1395
+ declare function describeApiError(body: string | null | undefined, statusCode: number): string;
1396
+
1219
1397
  declare class InMemoryTokenStorage implements TokenStorageInterface {
1220
1398
  private token;
1221
1399
  get(): Token | null;
@@ -1242,4 +1420,4 @@ declare class AuthRetryHttpClient implements HttpClientInterface {
1242
1420
  private injectAuthHeader;
1243
1421
  }
1244
1422
 
1245
- export { ApiVersion, AuthEndpoint, AuthRetryHttpClient, AuthenticationException, CompanyAddress, CompanyEndpoint, type CompanyResource, ComplianceEnrollment, ComplianceEnrollmentPage, ComplianceEnrollmentStatus, type ComplianceEnterpriseSize, type ComplianceInvoiceLineInput, type ComplianceInvoiceTaxInput, ComplianceTaxReport, ComplianceTaxReportPage, type ComplianceTaxReportRequestState, ComplianceTaxReportState, ComplianceTransaction, type ComplianceTransactionFilters, ComplianceTransactionPage, type ComplianceTransactionRequestState, type ComplianceTransactionRequestType, ComplianceTransactionState, ComplianceTransactionType, type ComplianceTypeOperation, type CreateComplianceEnrollmentInput, type CreateComplianceTransactionInput, type EReportingAccessRequestInput, EReportingEndpoint, type EReportingInterval, Environment, Environment as EnvironmentType, FetchHttpClient, type HttpClientInterface, HttpException, ImportResult, InMemoryTokenStorage, Language, LoginIdentifier, ProviderDocument, ProviderDocumentLine, type RevenueCountryBreakdown, type RevenueCurrencyBreakdown, type RevenueStateBreakdown, RevenueStatistics, type RevenueStatisticsFilters, type RevenueTimeBucket, type RevenueTotals, type RevenueTypeBreakdown, ScoreBreakdown, SireneLookupResult, type SmartEnrichmentConfidenceBucket, SmartEnrichmentEndpoint, type SmartEnrichmentExportOptions, type SmartEnrichmentHistoryInput, SmartEnrichmentHistoryPage, SmartEnrichmentHistoryRow, type SmartEnrichmentHistoryStats, type SmartEnrichmentInput, SmartEnrichmentJob, type SmartEnrichmentOutcomeBreakdown, SmartEnrichmentResource, type SmartEnrichmentSourceBreakdown, SmartEnrichmentStatistics, type SmartEnrichmentStatisticsInterval, type SmartEnrichmentStatisticsOptions, type SmartEnrichmentStatisticsRange, type SmartEnrichmentStatisticsTotals, SmartEnrichmentStatus, type SmartEnrichmentTimeBucket, type SmartEnrichmentUsage, type SmartEnrichmentUsageHistoryEntry, type SmartEnrichmentWaitOptions, TaxoraClient, TaxoraClientFactory, type TaxoraClientOptions, TaxoraException, Token, type TokenStorageInterface, type UpdateComplianceTransactionInput, ValidationException, VatCertificateExport, VatCollection, VatEndpoint, type VatRate, VatRates, VatResource, VatState, VatValidationAddressInput, describeComplianceEnrollmentStatus, describeComplianceTaxReportState, describeComplianceTransactionState, describeComplianceTransactionType, describeSmartEnrichmentStatus, describeVatState, getBaseUrl, getFailedVatStates, toComplianceEnrollmentStatus, toComplianceTaxReportState, toComplianceTransactionState, toComplianceTransactionType, toSmartEnrichmentStatus };
1423
+ export { ApiVersion, AuthEndpoint, AuthRetryHttpClient, AuthenticationException, CompanyAddress, CompanyEndpoint, type CompanyResource, ComplianceEnrollment, ComplianceEnrollmentPage, ComplianceEnrollmentStatus, type ComplianceEnterpriseSize, type ComplianceInvoiceLineInput, type ComplianceInvoiceTaxInput, ComplianceTaxReport, ComplianceTaxReportPage, type ComplianceTaxReportRequestState, ComplianceTaxReportState, ComplianceTransaction, type ComplianceTransactionFilters, ComplianceTransactionPage, type ComplianceTransactionRequestState, type ComplianceTransactionRequestType, ComplianceTransactionState, ComplianceTransactionType, type ComplianceTypeOperation, type CreateComplianceEnrollmentInput, type CreateComplianceTransactionInput, type EReportingAccessRequestInput, EReportingEndpoint, type EReportingInterval, Environment, Environment as EnvironmentType, FetchHttpClient, type HttpClientInterface, HttpException, ImportResult, InMemoryTokenStorage, Language, LoginIdentifier, ProviderDocument, ProviderDocumentLine, RetryPolicy, type RetryPolicyOptions, type RevenueCountryBreakdown, type RevenueCurrencyBreakdown, type RevenueStateBreakdown, RevenueStatistics, type RevenueStatisticsFilters, type RevenueTimeBucket, type RevenueTotals, type RevenueTypeBreakdown, ScoreBreakdown, SireneLookupResult, type SmartEnrichmentAddressQuality, type SmartEnrichmentConfidenceBucket, SmartEnrichmentEndpoint, type SmartEnrichmentExportOptions, type SmartEnrichmentHistoryInput, SmartEnrichmentHistoryPage, SmartEnrichmentHistoryRow, type SmartEnrichmentHistoryStats, type SmartEnrichmentInput, SmartEnrichmentJob, SmartEnrichmentMode, type SmartEnrichmentOutcomeBreakdown, type SmartEnrichmentProviderVerdict, SmartEnrichmentResource, type SmartEnrichmentSourceBreakdown, SmartEnrichmentStatistics, type SmartEnrichmentStatisticsInterval, type SmartEnrichmentStatisticsOptions, type SmartEnrichmentStatisticsRange, type SmartEnrichmentStatisticsTotals, SmartEnrichmentStatus, type SmartEnrichmentTimeBucket, type SmartEnrichmentUsage, type SmartEnrichmentUsageHistoryEntry, type SmartEnrichmentWaitOptions, TaxoraClient, TaxoraClientFactory, type TaxoraClientOptions, TaxoraException, Token, type TokenStorageInterface, type UpdateComplianceTransactionInput, ValidationException, VatCertificateExport, VatCollection, VatEndpoint, type VatRate, VatRates, VatResource, VatState, VatValidationAddressInput, describeApiError, describeComplianceEnrollmentStatus, describeComplianceTaxReportState, describeComplianceTransactionState, describeComplianceTransactionType, describeSmartEnrichmentMode, describeSmartEnrichmentStatus, describeVatState, getBaseUrl, getFailedVatStates, toComplianceEnrollmentStatus, toComplianceTaxReportState, toComplianceTransactionState, toComplianceTransactionType, toSmartEnrichmentMode, toSmartEnrichmentStatus };