npm.io
0.4.0 • Published 4d ago

@mexapi/sdk

Licence
MIT
Version
0.4.0
Deps
0
Size
75 kB
Vulns
0
Weekly
0

@mexapi/sdk

Official TypeScript SDK for the mexapi REST API — the fiscal API for Mexico. Download CFDIs via the SAT's Descarga Masiva, verify invoices, check the Lista Negra 69-B (EFOS), and more, with a fully-typed client.

npm install @mexapi/sdk
# or: pnpm add @mexapi/sdk

Quickstart

import { Mexapi } from "@mexapi/sdk";

const client = new Mexapi({ apiKey: process.env.MEXAPI_KEY! });
// baseUrl defaults to https://api.mexapi.com

// List CFDIs (paginated)
const { data, meta } = await client.cfdi.list({ limit: 50 });
console.log(meta.data_status); // "complete" | "incomplete" | "degraded"

// Get one CFDI with flat fields + line items (conceptos)
const { data: cfdi } = await client.cfdi.get("CEE4BE01-ADFA-4DEB-8421-ADD60F0BEDAC");
console.log(cfdi.subTotal, cfdi.totalIvaTrasladado);
for (const c of cfdi.conceptos ?? []) {
  console.log(c.descripcion, c.importe, c.ivaTrasladado);
}

Use a mx_test_ key to get mock data without a FIEL; mx_live_ returns real data.

Authentication

Pass your API key to the constructor. All requests send Authorization: Bearer <apiKey>.

const client = new Mexapi({
  apiKey: "mx_live_...",
  baseUrl: "https://api.mexapi.com", // optional
  maxRetries: 2,                      // optional
  timeout: 30_000,                    // optional (ms)
});

Resources

Resource Methods
client.cfdi list, get, batch, cancellations, getXml, getStatus, verify
client.imports create, get, list, ensure
client.credentials upload / manage FIEL credentials
client.webhooks manage webhook endpoints
CFDIs
// Paginated list with filters
const page = await client.cfdi.list({
  limit: 100,
  cursor: undefined,
  rfc: "AAA010101AAA",      // either direction
  issuerRfc: "AAA010101AAA",
  recipientRfc: "BBB020202BB2",
});

// Detail: flat fields + conceptos + raw parsedData
const { data } = await client.cfdi.get(uuid);

// Bulk detail — up to 100 UUIDs in one request (1 credit per CFDI returned).
// Lean by default (no parsedData); pass includeParsedData for the full blob
// (caps the batch at 25). UUIDs not found / not authorized come back in
// `notFound` and cost nothing.
const { data: batch } = await client.cfdi.batch({
  uuids: [uuid1, uuid2, uuid3],
});
for (const cfdi of batch.results) console.log(cfdi.uuid, cfdi.amount);
console.log(batch.notFound, batch.creditsCharged);

// Original signed XML (string)
const xml = await client.cfdi.getXml(uuid);

// Real-time SAT status (vigente / cancelado / EFOS) — no FIEL needed
const { data: status } = await client.cfdi.getStatus(uuid);

// Reconciliation feed — catch up on cancellations missed while your
// `cfdi.cancelled` webhook endpoint was down (webhook retries span ~3 days).
// Poll oldest-first by watermark until there's nothing left to drain.
let detectedSince: number | undefined;
do {
  const { data, meta } = await client.cfdi.cancellations({ detectedSince });
  for (const c of data) console.log(c.uuid, c.cancelledAt);
  detectedSince = meta.next_detected_since ?? detectedSince;
  var more = meta.has_more;
} while (more);

The detail response exposes both normalized fields (subTotal, formaPago, metodoPago, pre-aggregated tax totals, and a conceptos[] array with per-line taxes) and the raw parsedData blob (the CFDI 4.0 XML parsed to an object, for fields not yet promoted to columns: Complementos, full TimbreFiscalDigital, etc.). Prefer the flat fields + conceptos — they're typed and numeric. See the CFDI API reference for the full response shape, the parsedData structure, and gotchas.

Imports (historical / on-demand)
const job = await client.imports.create({
  credentialId: "cred_...",
  startDate: "2026-01-01",
  endDate: "2026-01-31", // max 31 days per job
});

// Idempotent: only creates jobs for missing months/directions
await client.imports.ensure({ credentialId: "cred_...", months: ["2026-01", "2026-02"] });

Webhook signature verification

import { verifyWebhookSignature } from "@mexapi/sdk";

const ok = verifyWebhookSignature({
  payload: rawBody,
  signature: req.headers["x-mexapi-signature"],
  secret: process.env.MEXAPI_WEBHOOK_SECRET!,
});

Error handling

import { MexapiError, MexapiRateLimitError, MexapiTimeoutError } from "@mexapi/sdk";

try {
  await client.cfdi.get(uuid);
} catch (err) {
  if (err instanceof MexapiRateLimitError) { /* back off */ }
  else if (err instanceof MexapiError) { console.error(err.code, err.message); }
}

Documentation

Full docs: https://mexapi.com/docs

License

MIT

Keywords