English
Fiscaza

Primeros pasos

De cero a tu primera factura registrada, en cuatro llamadas.

Fiscaza genera el registro de facturación, lo encadena con el anterior, calcula la huella y lo remite a la AEAT por ti. Tu software sigue emitiendo facturas como hasta ahora; lo único que cambia es que después de emitir una, se la cuentas a Fiscaza.

Todo lo que sigue funciona en el entorno de pruebas, que no envía nada a la AEAT y no tiene validez fiscal. Consigue tu clave en /registro.

Conseguir una clave de pruebas →

1. Comprueba tu clave

La clave va en la cabecera Authorization. Si esto responde 200, ya estás dentro. Las claves de pruebas empiezan por fz_test_ y las de producción por fz_live_; la clave determina el entorno, no hay que indicarlo en cada llamada.

curl -X GET https://fiscaza.com/api/v1/health \
  -H "Authorization: Bearer fz_test_…"

Respuesta

{
  "status": "ok",
  "environment": "sandbox",
  "checks": [ … ]
}

Detalle. Este endpoint es el mismo diagnóstico que usamos internamente, limitado a tus obligados. Devuelve 200 aunque encuentre problemas: un certificado tuyo caducado no es una caída nuestra.

2. Da de alta al obligado tributario

Un obligado es el negocio que emite las facturas: tu cliente. Se da de alta una sola vez. El NIF debe ser válido; el sistema lo comprueba antes de aceptarlo.

curl -X POST https://fiscaza.com/api/v1/taxpayers \
  -H "Authorization: Bearer fz_test_…"
 \
  -H "Content-Type: application/json" \
  -d '{
    "nif": "B12345674",
    "razonSocial": "Bar Ejemplo SL"
  }'

Respuesta

{
  "nif": "B12345674",
  "razon_social": "Bar Ejemplo SL",
  "environment": "sandbox",
  "status": "active",
  "credential_mode": "own_cert",
  "numero_instalacion": "20260823T120000-000001",
  "can_submit": false,
  "blocked": "sin certificado"
}

Detalle. can_submit es false hasta que el obligado tenga certificado. Puedes seguir emitiendo registros: se acumulan y se envían en cuanto haya credenciales. El numero_instalacion lo genera Fiscaza y es inmutable.

3. Registra una factura

Esta es la llamada que harás miles de veces. Devuelve la huella, el QR y la leyenda inmediatamente: no espera a la AEAT, porque tu cliente está esperando para imprimir el ticket.

curl -X POST https://fiscaza.com/api/v1/invoices \
  -H "Authorization: Bearer fz_test_…"
 \
  -H "Content-Type: application/json" \
  -d '{
    "nif": "B12345674",
    "serie": "A-",
    "tipoFactura": "F2",
    "fechaExpedicion": "23-08-2026",
    "descripcionOperacion": "Consumición",
    "cuotaTotal": "2.10",
    "importeTotal": "12.10",
    "desglose": [
      {
        "impuesto": "01",
        "claveRegimen": "01",
        "calificacionOperacion": "S1",
        "tipoImpositivo": "21.00",
        "baseImponibleOimporteNoSujeto": "10.00",
        "cuotaRepercutida": "2.10"
      }
    ]
  }'

Respuesta

{
  "id": "…",
  "num_serie": "A-1",
  "huella": "02BB1BAC4EFAE737…",
  "qr": "https://prewww2.aeat.es/wlpl/TIKE-CONT/ValidarQR?…",
  "leyenda": "VERI*FACTU",
  "submission": { "status": "queued" }
}

Detalle. Manda la cabecera Idempotency-Key con un identificador tuyo de la operación. Si tu proceso se cae entre la respuesta y tu commit, reintentar con la misma clave devuelve la misma factura en lugar de crear una segunda. La misma clave con un cuerpo distinto da 422.

4. Consulta el veredicto de la AEAT

El envío es asíncrono. Consulta la factura cuando quieras, o —mejor— registra un webhook y deja que te avisemos.

curl -X GET https://fiscaza.com/api/v1/invoices \
  -H "Authorization: Bearer fz_test_…"

Respuesta

{
  "data": [
    {
      "id": "…",
      "num_serie": "A-1",
      "submission": {
        "status": "accepted",
        "csv": "A-CSV-FROM-AEAT",
        "aeat": null
      }
    }
  ],
  "next_cursor": null
}

Detalle. Estados posibles: queued, sent, accepted, accepted_with_errors, rejected. accepted_with_errors significa que la AEAT lo ha guardado igualmente: NO lo reenvíes, corrígelo con una subsanación.

Y después

Todos los endpoints

POST/v1/signupCrea una cuenta de pruebas y devuelve la primera clave. Sin autenticar.
GET/v1/healthDiagnóstico de tus obligados, en tu idioma. Siempre 200.
POST/v1/taxpayersDa de alta un obligado tributario.
GET/v1/taxpayersLista tus obligados.
GET/v1/taxpayers/{nif}Consulta un obligado, incluido si puede enviar y por qué no.
DELETE/v1/taxpayers/{nif}Cierra un obligado. No borra: el libro es inalterable.
POST/v1/taxpayers/{nif}/certificateSube el certificado del obligado (base64 del .p12 y contraseña).
POST/v1/taxpayers/{nif}/certificate-linkGenera un enlace de un solo uso para que lo suba el titular.
POST/v1/invoicesRegistra una factura. Devuelve huella, QR y leyenda al instante.
GET/v1/invoicesLista facturas con paginación por cursor y filtros.
GET/v1/invoices/{id}Consulta una factura y el veredicto de la AEAT.
POST/v1/invoices/{id}/cancelAnula una factura mediante un registro de anulación encadenado.
POST/v1/invoices/{id}/subsanarCorrige una factura que la AEAT ya tiene, con la misma identidad.
POST/v1/webhooksRegistra un destino de webhook. Devuelve el secreto una sola vez.
GET/v1/webhooksLista tus webhooks y su salud de entrega.
POST/v1/webhooks/{id}/rotateRota el secreto de firma. El anterior deja de valer al instante.
POST/v1/webhooks/{id}/resumeReactiva un webhook pausado por fallos repetidos.
POST/v1/api-keysCrea otra clave en tu entorno actual. Se muestra una sola vez.
GET/v1/api-keysLista tus claves: prefijo, etiqueta, último uso, revocación. Nunca la clave.
DELETE/v1/api-keys/{prefix}Revoca una clave al instante. La única activa no se puede revocar a sí misma: crea otra antes.

Errores

Todos los errores tienen la misma forma: un código estable que puedes programar, un mensaje en tu idioma que puedes enseñar, y un request_id.

{
  "error": {
    "code": "invalid_request",
    "message": "Faltan campos obligatorios.",
    "fields": [ { "field": "nif", "problem": "required", "detail": "…" } ],
    "locale": "es",
    "request_id": "76020a7a-…"
  }
}
401unauthorizedClave ausente, desconocida, revocada o suspendida. No distinguimos entre ellas.
400invalid_requestValidación fallida. Trae fields[] con el campo y el motivo, en tu idioma.
404not_foundNo existe, o no es tuyo. Devolvemos 404 en ambos casos a propósito.
409conflictLa operación choca con el estado actual (por ejemplo, anular dos veces).
422idempotency_conflictMisma Idempotency-Key con un cuerpo distinto.
429rate_limitedHas superado el límite por minuto de tu clave.
500internalFallo nuestro. Trae un request_id; mándanoslo.