> ## 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.

# Issuing Electronic Invoices Using the facturas SDK

> Use arca.issue() to authorize electronic invoices through ARCA. Learn input fields, fiscal document types, amounts in centavos, and the issuance contract.

`arca.issue()` is the single call that takes your business data, derives the correct fiscal document type, reserves a voucher number, and authorizes it against ARCA via WSFE or WSMTXCA — all in one operation. You declare what you sold and who bought it; the SDK handles the SOAP envelope, WSAA token acquisition, and response parsing. Every call returns a discriminated union by `kind` so you can handle every possible outcome explicitly.

## Minimal example

The four environment variables — `ARCA_TAX_ID`, `ARCA_CERTIFICATE_PEM`, `ARCA_PRIVATE_KEY_PEM`, and `ARCA_ENVIRONMENT` — are all that's required to issue your first invoice. No database, no table, no external service.

```ts twoslash theme={null}
import { createArcaClient } from "facturas";

const arca = createArcaClient();

const factura = await arca.issue({
  issuer: "monotributo",
  salesPoint: 3,
  to: { condition: "consumidor_final" },
  items: [{ amount: 150_000 }], // ARS 1,500.00 in minor units (centavos)
});

switch (factura.kind) {
  case "authorized":
    console.log(factura.voucher);
    break;
  case "rejected":
    console.error(factura.issues);
    break;
  case "indeterminate":
    console.error(factura.attempted, factura.lookup);
    break;
  case "conflict":
    console.error(factura.attempted, factura.found);
    break;
  default:
    factura satisfies never;
}
```

## Input fields

<ParamField body="issuer" type="&#x22;monotributo&#x22; | &#x22;responsable_inscripto&#x22; | &#x22;exento&#x22; | &#x22;no_alcanzado&#x22;" required>
  Your legal fiscal condition for this voucher. The SDK never infers this from items or from the Padrón — you assert it on every call. A `responsable_inscripto` issuer produces class A vouchers for RI or Monotributo recipients, and class B for all other supported conditions. `monotributo`, `exento`, and `no_alcanzado` issuers always produce class C.
</ParamField>

<ParamField body="salesPoint" type="number" required>
  Your ARCA-registered sales point number (punto de venta). ARCA validates that it is enabled for your CUIT.
</ParamField>

<ParamField body="to" type="object" required>
  The fiscal recipient. `condition` is the recipient's VAT condition (e.g. `"consumidor_final"`, `"responsable_inscripto"`). Conditions other than `consumidor_final` require an 11-digit `cuit`. A `consumidor_final` recipient accepts a `cuit`, a `dni`, or no document at all — unless the invoice total reaches the identification threshold defined in RG 5866/2026, in which case identification becomes mandatory.
</ParamField>

<ParamField body="items" type="object[]" required>
  Line items for the voucher. Class C issuers (Monotributo, Exento, No Alcanzado) use `{ amount }`. Class A and B issuers use `{ gross | net, vat }`, where `vat` is one of `0 | 2.5 | 5 | 10.5 | 21 | 27 | "exempt" | "untaxed"`. Items are grouped by VAT rate before rounding using Round Half Even.
</ParamField>

<ParamField body="total" type="number">
  Optional assertion of the total to send. The SDK adjusts the header VAT by at most one centavo per numeric VAT rate and keeps VAT non-negative. Class C totals must match exactly. If you pass `total` and the computed value differs by more than the permitted adjustment, `issue()` throws before any network I/O.
</ParamField>

<ParamField body="family" type="&#x22;ordinary&#x22; | &#x22;retention_legend&#x22; | &#x22;fce&#x22;">
  Voucher family. Defaults to `"ordinary"` (types 1, 6, 11). `"retention_legend"` is class A with a retention legend (types 51, 52, 53). `"fce"` is Factura de Crédito Electrónica MiPyME (types 201–213) and requires `dueDate` and `fce: { cbu, alias?, transfer?, reference? }` with a 22-digit CBU. ARCA validates the bank account and your eligibility.
</ParamField>

### Amounts are always integer centavos

Every monetary value in `facturas` is an **integer in centavos** (minor units). There are no decimals anywhere in the public API.

```text theme={null}
150_000  →  ARS 1,500.00
  1_000  →  ARS    10.00
      1  →  ARS     0.01
```

Pass `items: [{ amount: 150_000 }]` for a class C voucher of ARS 1,500.00. Floating-point amounts are never accepted and will cause an input error before any network call is made.

## Preview before issuing

`preview()` derives exactly what `issue()` would send — without any I/O. It reaches no store, no WSAA service, and no SOAP endpoint. Use it to verify the derived `voucherClass`, `voucherType`, computed `amounts`, and the exact `request` object before committing to a real authorization.

```ts twoslash theme={null}
import { createArcaClient, createMemoryStore, type IssueInput } from "facturas";

const arca = createArcaClient({ store: createMemoryStore() });
const venta = { id: "sale-example-002", totalEnCentavos: 121_000 };

const input: IssueInput = {
  issuer: "responsable_inscripto",
  salesPoint: 3,
  to: { condition: "consumidor_final" },
  items: [{ gross: 121_000, vat: 21 }], // ARS 1,210.00 in centavos
};

// preview() is synchronous — no store, no WSAA, no SOAP.
const preview = arca.preview(input);
console.log(
  preview.voucherClass,  // "B"
  preview.voucherType,   // 6
  preview.amounts,       // computedTotal, sentTotal, vatAdjustment
  preview.request        // the exact WSFE input, without the voucher number
);

if (preview.amounts.sentTotal !== venta.totalEnCentavos) {
  throw new Error("The derived invoice does not match the sale total.");
}

const factura = await arca.issue(input, { idempotencyKey: venta.id });
if (factura.kind === "authorized") {
  // The issued amounts are the ones the preview showed.
  console.log(factura.voucher.amounts, preview.amounts);
}
```

<Note>
  `preview()` is synchronous and raises the same input errors that `issue()` raises before its first network call, so a clean preview guarantees no new local errors at issuance time. The voucher number does not appear in the preview because it is only known after the number is reserved during `issue()`.

  For credit and debit notes, use the async variants `previewCreditNote()` and `previewDebitNote()` — they need to fetch the original voucher first.
</Note>

## Recover a stored reservation

`recover(key, options)` reconciles without issuing. It consults the stored reservation — provider, number, and evidence — and never authorizes or reserves a new number.

```ts twoslash theme={null}
import { createArcaClient, createPostgresStore } from "facturas";
import { sql } from "@vercel/postgres";

const arca = createArcaClient({
  store: createPostgresStore({ query: (text, params) => sql.query(text, params) }),
});

// Consult the existing reservation for this sale without issuing again.
const result = await arca.recover("sale-abc-123");
```

If ARCA confirms that the reserved number has no authorized voucher, `recover()` returns an `indeterminate` result with `lookup.kind === "not_found"` — it does **not** issue. To actually authorize, call `issue()` with the same idempotency key. If no reservation exists for the key, `recover()` throws `ArcaInputError`.

## The fiscal contract

Understanding the issuance contract helps you handle failures correctly:

* **Without an idempotency key:** `issue()` reads the next available number and authorizes once, with at most one identity-check query after an indeterminate response.
* **First call with a key:** The number is reserved in your store *before* the authorization request is sent. If the process crashes mid-flight, the number is preserved.
* **Retry with a key:** `issue()` looks up the existing reservation. Only a `not_found` lookup result allows the stored number to be authorized. A voucher that is found is never re-sent.
* **Changing the input for an existing key:** throws `ARCA_INPUT_IDEMPOTENCY_MISMATCH` before any network I/O.

<Note>
  **WSFE vs. WSMTXCA:** By default, `issue()` routes through WSFE, which carries aggregate amounts at the header level. WSMTXCA carries full line-item detail and is required for certain fiscal regimes and for the electronic invoice MiPyME FCE. Pass `{ service: "wsmtxca" }` as a second-argument option to route through WSMTXCA. See [Guides → WSMTXCA](/guides/wsmtxca) for the full itemized-invoice workflow.
</Note>
