> ## Documentation Index
> Fetch the complete documentation index at: https://facturas-sdk.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Low-Level WSFE and WSMTXCA Service Access

> Access arca.wsfe and arca.wsmtxca directly for advanced use cases: custom builders, raw service calls, and fiscal evidence inspection.

The exact layer gives you direct access to the raw ARCA service methods via `arca.wsfe` and `arca.wsmtxca`. Where the high-level `issue()` facade automatically derives the voucher type, reserves the next number, and handles indeterminate recovery, the exact layer keeps all of that control in your hands — you own the voucher number, you construct the full request, and you receive the raw fiscal evidence as returned by ARCA.

<Warning>
  Direct calls to `arca.wsfe.issue()` and `arca.wsmtxca.issue()` **bypass the SDK's automatic idempotency and indeterminate-recovery logic**. Use the facade (`arca.issue()`) for production invoice issuance unless you need the exact layer's additional control. If you do use it, your application is responsible for durably reserving the voucher number, handling indeterminate outcomes, and ensuring you never retry a potentially successful authorization.
</Warning>

***

## When to Use the Exact Layer

Use `arca.wsfe` or `arca.wsmtxca` directly when:

* You need a service-level method the facade does not expose, such as `getLastAuthorizedVoucher` or `getServerStatus`.
* You need to issue a voucher in a currency, with a receiver, or with fiscal fields the `issue()` facade does not support.
* Your application owns its own voucher number sequence and you cannot let the SDK call `getNextVoucherNumber` automatically.
* You want to read ARCA's runtime reference catalogs (voucher types, VAT rates, currencies) rather than using static constants.
* You are building a custom workflow — for example, constructing the request in one step and authorizing it in another.

***

## Builders

Before calling `arca.wsfe.issue()`, use a builder to construct the `WsfeVoucherInput`. Builders handle all fiscal arithmetic — net amounts, VAT, rounding — so you do not need to compute them manually.

### `buildFacturaB(input)`

Derives a complete `WsfeVoucherInput` for a Factura B. Accepts integer minor units (centavos) as `taxableAmount` and performs VAT calculation using Round Half Even, matching ARCA's documented rounding criterion.

```ts twoslash theme={null}
import { buildFacturaB, createArcaClient } from "facturas";
import {
  ARCA_CONCEPT_TYPES,
  ARCA_DOCUMENT_TYPES,
  ARCA_RECEIVER_VAT_CONDITIONS,
} from "facturas/constants";

const arca = createArcaClient({
  taxId: "20123456786",
  certificatePem: process.env.ARCA_CERTIFICATE_PEM!,
  privateKeyPem: process.env.ARCA_PRIVATE_KEY_PEM!,
  environment: "test",
});

const data = buildFacturaB({
  salesPoint: 1,
  concept: ARCA_CONCEPT_TYPES.PRODUCTOS,
  documentType: ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL,
  documentNumber: 0,
  receiverVatConditionId: ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL,
  voucherDate: "2026-09-02",
  taxableAmount: 10_000, // ARS 100.00 in minor units (centavos)
  vatRate: 21,
});
```

<Accordion title="BuildFacturaB input fields">
  <ParamField body="salesPoint" type="number" required>
    Sales point number.
  </ParamField>

  <ParamField body="concept" type="number" required>
    Concept type code. Use `ARCA_CONCEPT_TYPES`.
  </ParamField>

  <ParamField body="documentType" type="number" required>
    Receiver document type code. Use `ARCA_DOCUMENT_TYPES`.
  </ParamField>

  <ParamField body="documentNumber" type="number" required>
    Receiver document number. Pass `0` for consumidor final.
  </ParamField>

  <ParamField body="receiverVatConditionId" type="number" required>
    Receiver VAT condition code. Use `ARCA_RECEIVER_VAT_CONDITIONS`.
  </ParamField>

  <ParamField body="voucherDate" type="string" required>
    Invoice date. Accepts `YYYY-MM-DD` or `YYYYMMDD`.
  </ParamField>

  <ParamField body="taxableAmount" type="number" required>
    Taxable base amount in minor units (centavos). Must be a positive integer. When `vatRate` is positive, the amount must produce at least one centavo of VAT after rounding.
  </ParamField>

  <ParamField body="vatRate" type="number" required>
    VAT rate as a percentage (`0`, `2.5`, `5`, `10.5`, `21`, or `27`).
  </ParamField>

  <ParamField body="currency" type="&#x22;ARS&#x22; | &#x22;USD&#x22;" default="&#x22;ARS&#x22;">
    ISO currency code. The builder maps this to the ARCA currency ID internally.
  </ParamField>

  <ParamField body="exchangeRate" type="string">
    Exchange rate as a decimal string. Required when `currency` is not `"ARS"`.
  </ParamField>
</Accordion>

To issue in USD, pass the exchange rate as a decimal string:

```ts twoslash theme={null}
import { buildFacturaB } from "facturas";
import {
  ARCA_CONCEPT_TYPES,
  ARCA_DOCUMENT_TYPES,
  ARCA_RECEIVER_VAT_CONDITIONS,
} from "facturas/constants";
// ---cut---
const usdData = buildFacturaB({
  salesPoint: 1,
  concept: ARCA_CONCEPT_TYPES.PRODUCTOS,
  documentType: ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL,
  documentNumber: 0,
  receiverVatConditionId: ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL,
  voucherDate: "2026-09-02",
  taxableAmount: 10_000, // USD 100.00 in minor units
  vatRate: 21,
  currency: "USD",
  exchangeRate: "1095.500000",
});
```

***

### `buildFacturaC(input)`

Same pattern as `buildFacturaB`, but constructs a Factura C with zero VAT. Accepts the same currency and exchange rate options.

***

## `arca.wsfe` — WSFE Service

### Issuance

#### `wsfe.issue({ voucherNumber, data })`

Sends a single WSFE `FECAESolicitar` request for the exact voucher number you supply. Returns a typed outcome: `authorized`, `rejected`, or `indeterminate`.

```ts twoslash theme={null}
import { buildFacturaB, createArcaClient } from "facturas";
import { ARCA_CONCEPT_TYPES, ARCA_DOCUMENT_TYPES, ARCA_RECEIVER_VAT_CONDITIONS } from "facturas/constants";
const arca = createArcaClient({ taxId: "20123456786", certificatePem: "", privateKeyPem: "", environment: "test" });
const data = buildFacturaB({ salesPoint: 1, concept: ARCA_CONCEPT_TYPES.PRODUCTOS, documentType: ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL, documentNumber: 0, receiverVatConditionId: ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL, voucherDate: "2026-09-02", taxableAmount: 10_000, vatRate: 21 });
// ---cut---
// 1. Reserve the number durably in your own store.
const voucherNumber = await arca.wsfe.getNextVoucherNumber({
  salesPoint: data.salesPoint,
  voucherType: data.voucherType,
});

// 2. Issue exactly once.
const issued = await arca.wsfe.issue({ voucherNumber, data });

if (issued.kind === "authorized") {
  console.log(issued.cae, issued.caeExpiry, issued.voucherNumber);
} else if (issued.kind === "rejected") {
  console.error(issued.errors, issued.observations);
} else {
  // indeterminate — consult the same number before any new attempt
  const lookup = await arca.wsfe.lookupVoucher({
    number: voucherNumber,
    salesPoint: data.salesPoint,
    voucherType: data.voucherType,
  });
  console.log(lookup.kind);
}
```

<Warning>
  `wsfe.issue()` always makes **exactly one** transport attempt, regardless of the client's `retries` setting. It never refreshes credentials automatically and never retries on an indeterminate outcome. This prevents a potentially successful authorization from being silently duplicated.
</Warning>

Indeterminate outcomes can arise from timeouts, connection failures, invalid SOAP responses, or incomplete/contradictory results from ARCA. An explicit authentication rejection from ARCA comes back as `reason: "authentication_rejected"` with typed evidence in the `authentication` field.

***

### Voucher Queries

#### `wsfe.getNextVoucherNumber({ salesPoint, voucherType })`

Returns the next voucher number to use for the given sales point and voucher type. Call this immediately before authorizing if your number sequence may have moved since your last call.

```ts twoslash theme={null}
import { createArcaClient } from "facturas";
const arca = createArcaClient({ taxId: "20123456786", certificatePem: "", privateKeyPem: "", environment: "test" });
// ---cut---
const next = await arca.wsfe.getNextVoucherNumber({ salesPoint: 1, voucherType: 6 });
console.log(next); // number
```

#### `wsfe.lookupVoucher({ number, salesPoint, voucherType })`

Looks up a voucher at ARCA by coordinates. Returns the voucher summary or `not_found` (WSFE code 602). Use this after an indeterminate `wsfe.issue()` outcome to check whether the authorization actually went through.

#### `wsfe.getVoucherInfo({ number, salesPoint, voucherType })`

Returns full voucher detail from ARCA, or `null` if not found.

#### `wsfe.getSalesPoints(input?)`

Returns the list of sales points configured for the authenticated CUIT on WSFE. Accepts an optional `{ representedTaxId, forceRefresh }` input object. Returns an empty list when no sales points are registered for web services (WSFE code 602).

***

### Runtime Catalogs

These methods return live data from ARCA and are useful when the static constants in `facturas/constants` are not sufficient.

```ts twoslash theme={null}
import { createArcaClient } from "facturas";
const arca = createArcaClient({ taxId: "20123456786", certificatePem: "", privateKeyPem: "", environment: "test" });
// ---cut---
const voucherTypes  = await arca.wsfe.getVoucherTypes();
const documentTypes = await arca.wsfe.getDocumentTypes();
const currencies    = await arca.wsfe.getCurrencyTypes(); // returns ARCA IDs, not ISO codes
const vatRates      = await arca.wsfe.getVatRates();
```

<Note>
  All authenticated catalog methods accept `forceRefresh: true` to discard the cached WSAA ticket and request a fresh Token Authorization before the call. Pass this option when you know a ticket has expired or been invalidated externally.
</Note>

#### `wsfe.getServerStatus()`

Checks the WSFE backend health. Returns a status object from ARCA. Useful for health checks and operational dashboards.

***

## `arca.wsmtxca` — WSMTXCA Service

WSMTXCA follows the same pattern as WSFE but adds full line-item encoding. Use it when your invoices require itemized breakdowns at the ARCA service level.

The facade also routes to WSMTXCA when you pass `{ service: "wsmtxca" }` to `arca.issue()`. The exact layer is available for cases where you need to build the `WsmtxcaIssueRequest` yourself.

### Methods

#### `wsmtxca.issue({ data })`

Issues a WSMTXCA voucher from a fully constructed `WsmtxcaIssueRequest`. Same outcome semantics as `wsfe.issue()` — one transport attempt, no automatic retries, typed indeterminate result.

#### `wsmtxca.getLastAuthorizedVoucher({ voucherType, salesPoint })`

Returns the last authorized voucher number. When no voucher has been issued for the given coordinates, WSMTXCA code 1502 returns `0`.

#### `wsmtxca.getVoucher({ voucherType, salesPoint, voucherNumber })`

Returns full voucher detail. Returns `not_found` on WSMTXCA code 1503.

#### `wsmtxca.lookupVoucher({ voucherType, salesPoint, voucherNumber })`

Looks up a voucher at ARCA by coordinates. Returns the voucher summary or `not_found` on WSMTXCA code 1503. Use this after an indeterminate `wsmtxca.issue()` outcome to verify whether the authorization succeeded.

#### `wsmtxca.getSalesPoints(input?)`

Returns the sales points registered for the authenticated CUIT on WSMTXCA. Accepts an optional `{ representedTaxId, forceRefresh }` input object.

<Note>
  All authenticated WSMTXCA methods accept `forceRefresh: true` to renew the WSMTXCA WSAA ticket before the call.
</Note>

***

## `WsfeVoucherInput` — Full Escape Hatch

When you need fiscal fields the facade and builders do not expose — exemptions, untaxed amounts, multiple VAT rates, or custom totals — construct a `WsfeVoucherInput` directly. Amounts use decimal values in major units (e.g., `100.00` for ARS 100); the SDK validates them locally and serializes them as canonical two-decimal strings before sending to ARCA.

```ts twoslash theme={null}
import type { WsfeVoucherInput } from "facturas/wsfe";
import {
  ARCA_CONCEPT_TYPES,
  ARCA_CURRENCY_IDS,
  ARCA_DOCUMENT_TYPES,
  ARCA_RECEIVER_VAT_CONDITIONS,
  ARCA_VAT_RATES,
  ARCA_VOUCHER_TYPES,
} from "facturas/constants";

const exactData: WsfeVoucherInput = {
  salesPoint: 1,
  voucherType: ARCA_VOUCHER_TYPES.FACTURA_B,
  concept: ARCA_CONCEPT_TYPES.PRODUCTOS,
  documentType: ARCA_DOCUMENT_TYPES.CONSUMIDOR_FINAL,
  documentNumber: 0,
  receiverVatConditionId: ARCA_RECEIVER_VAT_CONDITIONS.CONSUMIDOR_FINAL,
  voucherDate: "2026-09-02",
  totalAmount: 121,
  nonTaxableAmount: 0,
  netAmount: 100,
  exemptAmount: 0,
  taxAmount: 0,
  vatAmount: 21,
  currencyId: ARCA_CURRENCY_IDS.ARS,
  exchangeRate: "1",
  vatRates: [{ id: ARCA_VAT_RATES.IVA_21, baseAmount: 100, amount: 21 }],
};
```

<Tip>
  `WsfeVoucherInput` uses ARCA protocol identifiers: `currencyId` is `"PES"` or `"DOL"`, and `vatRates[].id` uses ARCA's internal codes (e.g., `5` for 21%). The high-level facade and builders accept ISO currency codes and percentage values and translate them for you.
</Tip>

***

## Absence and Not-Found Semantics

Exact-layer absence handling varies by operation and service:

<Accordion title="Not-found codes by operation">
  | Operation                                      | Code | Result                                        |
  | ---------------------------------------------- | ---- | --------------------------------------------- |
  | WSFE `FECompConsultar`                         | 602  | `not_found`                                   |
  | WSFE `FEParamGetPtosVenta`                     | 602  | Empty list (no sales points for web services) |
  | WSMTXCA `consultarComprobante`                 | 1503 | `not_found`                                   |
  | WSMTXCA `consultarUltimoComprobanteAutorizado` | 1502 | Voucher number `0`                            |

  Note: WSMTXCA code 602 is **not** an absence indicator — it remains an error.
</Accordion>

***

## Authentication Retry Behavior

Catalog and query operations (read-only methods) may retry once with a forced credential refresh after an explicit, typed `ArcaAuthenticationError`. Issuance operations (`wsfe.issue()`, `wsmtxca.issue()`) never refresh credentials or retry automatically — any form of uncertainty results in an indeterminate outcome, not a retry.

Pass `forceRefresh: true` to any authenticated method to disable automatic authentication recovery for that call and force a fresh WSAA ticket request unconditionally.
