# Cumplimiento SAT

> La opinión de cumplimiento (32-D), la constancia de situación fiscal y el buzón tributario de un emisor, traídos del SAT con su e.firma, y los hallazgos que Fiscalisto encontró en sus CFDI.

Con la e.firma del emisor usted trae del SAT tres papeles que sus clientes, bancos y proveedores le piden seguido:

- La **opinión de cumplimiento** (artículo 32-D del CFF): positiva, negativa (con lo que el SAT dice que falta), sin obligaciones o en suspensión, con su folio y su PDF.
- La **constancia de situación fiscal**: el PDF del SAT de hoy, con su idCIF, sus regímenes, su código postal y su estatus en el padrón ya leídos.
- Los avisos del **buzón tributario**: cada uno con su asunto, si no se ha leído y qué tan urgente es (un requerimiento o una multa piden atención; un boletín no).

Y sin ir al SAT, los **hallazgos** que Fiscalisto encontró en las facturas del emisor (las que timbró aquí y las que trajo con las [descargas del SAT](https://fiscalisto.com/docs/api/descargas-del-sat)): proveedores en la lista 69-B, pagos en efectivo de más de $2,000, PPD sin complemento de pago y los demás.

Solo se consulta: nunca se presenta, firma ni abre nada en el SAT. Un aviso del buzón se lee en Fiscalisto sin abrirlo en el SAT, así que no firma el acuse ni empieza los plazos.

## Lo que cuesta

| Qué | En producción | En pruebas |
| --- | --- | --- |
| Pedir al SAT la opinión, la constancia o el buzón (`…/refresh`) | 1 timbre por consulta | Gratis |
| Ver lo que ya trajo, bajar sus PDF y leer los hallazgos | Gratis | Gratis |

Si el SAT no contesta o no deja entrar con la e.firma, contesta `502` `sat_unavailable` y el timbre regresa. Cada papel de cada emisor se puede pedir una vez cada 10 minutos; antes, la API contesta `429` `sat_refresh_too_soon` con `Retry-After`. Dos solicitudes a la vez entran al SAT una sola vez: la otra recibe el `429`. Fiscalisto no consulta solo: estos papeles se actualizan cuando usted lo pide.

## En pruebas y en producción

- **En producción** se entra al SAT con la e.firma que el emisor guardó con `keep_for_downloads: true`, la misma de las [descargas del SAT](https://fiscalisto.com/docs/api/descargas-del-sat#guardar-la-efirma-del-emisor). Sin ella, o vencida, contesta `400` `efirma_required`.
- **En pruebas** el SAT no tiene un ambiente de pruebas, así que no se le pregunta: la opinión sale positiva, la constancia con los datos del emisor y el buzón con dos avisos de ejemplo (un requerimiento sin leer y un boletín). Cada uno con su PDF de ejemplo. No necesita e.firma.

## La opinión de cumplimiento

Pídala al SAT; la solicitud no lleva cuerpo.

```bash tab="cURL"
curl -X POST https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/compliance_opinion/refresh \
  -H "Authorization: Bearer $FISCALISTO_API_KEY"
```

```ts tab="TypeScript"
import { Fiscalisto } from "fiscalisto";

// La llave viene de FISCALISTO_API_KEY.
const fiscalisto = new Fiscalisto();

const satRefresh = await fiscalisto.issuers.refreshComplianceOpinion("iss_OufpKqnrvx9p");
```

```python tab="Python"
from fiscalisto import Fiscalisto

# La llave viene de FISCALISTO_API_KEY.
fiscalisto = Fiscalisto()

sat_refresh = fiscalisto.issuers.refresh_compliance_opinion("iss_OufpKqnrvx9p")
```

```php tab="PHP"
<?php

use Fiscalisto\Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
$fiscalisto = new Fiscalisto();

$satRefresh = $fiscalisto->issuers->refreshComplianceOpinion('iss_OufpKqnrvx9p');
```

```go tab="Go"
package main

import (
	"context"
	"fmt"
	"log"

	fiscalisto "fiscalisto.com/go"
)

func main() {
	// La llave viene de FISCALISTO_API_KEY.
	client, err := fiscalisto.NewClient("")
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	satRefresh, err := client.Issuers.RefreshComplianceOpinion(ctx, "iss_OufpKqnrvx9p")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%+v\n", *satRefresh)
}
```

```java tab="Java"
import com.fiscalisto.FiscalistoClient;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

var satRefresh = fiscalisto.issuers().refreshComplianceOpinion("iss_OufpKqnrvx9p");
System.out.println(satRefresh);
```

```csharp tab="C#"
using Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

var satRefresh = await fiscalisto.Issuers.RefreshComplianceOpinionAsync("iss_OufpKqnrvx9p");
```

La respuesta dice si terminó (`status: completed`) o si el SAT sigue trabajando (`processing`, cuando tarda más de 50 segundos): en ese caso el resultado llega solo a `GET /v1/issuers/{id}/compliance_opinion` en uno o dos minutos. Ahí está la última que trajo:

```bash tab="cURL"
curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/compliance_opinion \
  -H "Authorization: Bearer $FISCALISTO_API_KEY"
```

```ts tab="TypeScript"
import { Fiscalisto } from "fiscalisto";

// La llave viene de FISCALISTO_API_KEY.
const fiscalisto = new Fiscalisto();

const complianceOpinion = await fiscalisto.issuers.complianceOpinion("iss_OufpKqnrvx9p");
```

```python tab="Python"
from fiscalisto import Fiscalisto

# La llave viene de FISCALISTO_API_KEY.
fiscalisto = Fiscalisto()

compliance_opinion = fiscalisto.issuers.compliance_opinion("iss_OufpKqnrvx9p")
```

```php tab="PHP"
<?php

use Fiscalisto\Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
$fiscalisto = new Fiscalisto();

$complianceOpinion = $fiscalisto->issuers->complianceOpinion('iss_OufpKqnrvx9p');
```

```go tab="Go"
package main

import (
	"context"
	"fmt"
	"log"

	fiscalisto "fiscalisto.com/go"
)

func main() {
	// La llave viene de FISCALISTO_API_KEY.
	client, err := fiscalisto.NewClient("")
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	complianceOpinion, err := client.Issuers.ComplianceOpinion(ctx, "iss_OufpKqnrvx9p")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%+v\n", *complianceOpinion)
}
```

```java tab="Java"
import com.fiscalisto.FiscalistoClient;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

var complianceOpinion = fiscalisto.issuers().complianceOpinion("iss_OufpKqnrvx9p");
System.out.println(complianceOpinion);
```

```csharp tab="C#"
using Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

var complianceOpinion = await fiscalisto.Issuers.ComplianceOpinionAsync("iss_OufpKqnrvx9p");
```

```json
{
  "object": "compliance_opinion",
  "livemode": true,
  "issuer": "iss_OufpKqnrvx9p",
  "result": "negative",
  "date": "2026-10-27",
  "folio": "26NA1234567",
  "reasons": ["No se encuentra presentada la declaración anual de 2025."],
  "pdf": true,
  "checked_at": "2026-10-27T09:12:00-06:00",
  "last_error": null
}
```

`result` es `positive`, `negative`, `no_obligations` o `suspended` (`null` hasta la primera consulta), y `reasons` lo que el SAT lista como pendiente en una negativa. El PDF, como lo da el SAT:

```bash tab="cURL"
curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/compliance_opinion/pdf \
  -H "Authorization: Bearer $FISCALISTO_API_KEY" -o opinion.pdf
```

```ts tab="TypeScript"
import { writeFile } from "node:fs/promises";
import { Fiscalisto } from "fiscalisto";

// La llave viene de FISCALISTO_API_KEY.
const fiscalisto = new Fiscalisto();

await writeFile("factura.pdf", await fiscalisto.issuers.complianceOpinionPdf("iss_OufpKqnrvx9p"));
```

```python tab="Python"
from fiscalisto import Fiscalisto

# La llave viene de FISCALISTO_API_KEY.
fiscalisto = Fiscalisto()

with open("factura.pdf", "wb") as f:
    f.write(fiscalisto.issuers.compliance_opinion_pdf("iss_OufpKqnrvx9p"))
```

```php tab="PHP"
<?php

use Fiscalisto\Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
$fiscalisto = new Fiscalisto();

file_put_contents('factura.pdf', $fiscalisto->issuers->complianceOpinionPdf('iss_OufpKqnrvx9p'));
```

```go tab="Go"
package main

import (
	"context"
	"log"
	"os"

	fiscalisto "fiscalisto.com/go"
)

func main() {
	// La llave viene de FISCALISTO_API_KEY.
	client, err := fiscalisto.NewClient("")
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	pdf, err := client.Issuers.ComplianceOpinionPDF(ctx, "iss_OufpKqnrvx9p")
	if err != nil {
		log.Fatal(err)
	}
	if err := os.WriteFile("factura.pdf", pdf, 0o644); err != nil {
		log.Fatal(err)
	}
}
```

```java tab="Java"
import com.fiscalisto.FiscalistoClient;
import java.nio.file.Files;
import java.nio.file.Path;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

Files.write(Path.of("factura.pdf"), fiscalisto.issuers().complianceOpinionPdf("iss_OufpKqnrvx9p"));
```

```csharp tab="C#"
using Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

await File.WriteAllBytesAsync("factura.pdf", await fiscalisto.Issuers.ComplianceOpinionPdfAsync("iss_OufpKqnrvx9p"));
```

## La constancia de situación fiscal

```bash tab="cURL"
curl -X POST https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/tax_status_certificate/refresh \
  -H "Authorization: Bearer $FISCALISTO_API_KEY"
```

```ts tab="TypeScript"
import { Fiscalisto } from "fiscalisto";

// La llave viene de FISCALISTO_API_KEY.
const fiscalisto = new Fiscalisto();

const satRefresh = await fiscalisto.issuers.refreshTaxStatusCertificate("iss_OufpKqnrvx9p");
```

```python tab="Python"
from fiscalisto import Fiscalisto

# La llave viene de FISCALISTO_API_KEY.
fiscalisto = Fiscalisto()

sat_refresh = fiscalisto.issuers.refresh_tax_status_certificate("iss_OufpKqnrvx9p")
```

```php tab="PHP"
<?php

use Fiscalisto\Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
$fiscalisto = new Fiscalisto();

$satRefresh = $fiscalisto->issuers->refreshTaxStatusCertificate('iss_OufpKqnrvx9p');
```

```go tab="Go"
package main

import (
	"context"
	"fmt"
	"log"

	fiscalisto "fiscalisto.com/go"
)

func main() {
	// La llave viene de FISCALISTO_API_KEY.
	client, err := fiscalisto.NewClient("")
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	satRefresh, err := client.Issuers.RefreshTaxStatusCertificate(ctx, "iss_OufpKqnrvx9p")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%+v\n", *satRefresh)
}
```

```java tab="Java"
import com.fiscalisto.FiscalistoClient;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

var satRefresh = fiscalisto.issuers().refreshTaxStatusCertificate("iss_OufpKqnrvx9p");
System.out.println(satRefresh);
```

```csharp tab="C#"
using Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

var satRefresh = await fiscalisto.Issuers.RefreshTaxStatusCertificateAsync("iss_OufpKqnrvx9p");
```

`GET /v1/issuers/{id}/tax_status_certificate` trae lo que dice la última: `issued_on`, `id_cif`, `legal_name`, `tax_systems` (cada régimen con su clave y su nombre), `zip` y `status` (`ACTIVO` en el padrón). Su PDF en `GET /v1/issuers/{id}/tax_status_certificate/pdf`. Si el emisor cambió de régimen o de domicilio fiscal, pídala otra vez: la factura sale con los datos del emisor en Fiscalisto, no con los de la constancia.

## El buzón tributario

```bash tab="cURL"
curl -X POST https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/sat_mailbox/refresh \
  -H "Authorization: Bearer $FISCALISTO_API_KEY"
```

```ts tab="TypeScript"
import { Fiscalisto } from "fiscalisto";

// La llave viene de FISCALISTO_API_KEY.
const fiscalisto = new Fiscalisto();

const satRefresh = await fiscalisto.issuers.refreshSatMailbox("iss_OufpKqnrvx9p");
```

```python tab="Python"
from fiscalisto import Fiscalisto

# La llave viene de FISCALISTO_API_KEY.
fiscalisto = Fiscalisto()

sat_refresh = fiscalisto.issuers.refresh_sat_mailbox("iss_OufpKqnrvx9p")
```

```php tab="PHP"
<?php

use Fiscalisto\Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
$fiscalisto = new Fiscalisto();

$satRefresh = $fiscalisto->issuers->refreshSatMailbox('iss_OufpKqnrvx9p');
```

```go tab="Go"
package main

import (
	"context"
	"fmt"
	"log"

	fiscalisto "fiscalisto.com/go"
)

func main() {
	// La llave viene de FISCALISTO_API_KEY.
	client, err := fiscalisto.NewClient("")
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	satRefresh, err := client.Issuers.RefreshSATMailbox(ctx, "iss_OufpKqnrvx9p")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%+v\n", *satRefresh)
}
```

```java tab="Java"
import com.fiscalisto.FiscalistoClient;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

var satRefresh = fiscalisto.issuers().refreshSatMailbox("iss_OufpKqnrvx9p");
System.out.println(satRefresh);
```

```csharp tab="C#"
using Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

var satRefresh = await fiscalisto.Issuers.RefreshSatMailboxAsync("iss_OufpKqnrvx9p");
```

Los avisos quedan en `GET /v1/sat_mailbox_messages`, del más reciente al más antiguo y [paginados](https://fiscalisto.com/docs/api/paginacion); `unread=true` deja solo los que no se han leído en el SAT:

```bash tab="cURL"
curl "https://api.fiscalisto.com/v1/sat_mailbox_messages?issuer=iss_OufpKqnrvx9p&unread=true" \
  -H "Authorization: Bearer $FISCALISTO_API_KEY"
```

```ts tab="TypeScript"
import { Fiscalisto } from "fiscalisto";

// La llave viene de FISCALISTO_API_KEY.
const fiscalisto = new Fiscalisto();

for await (const item of fiscalisto.satMailboxMessages.list({ issuer: "iss_OufpKqnrvx9p", unread: true })) {
  console.log(item.id);
}
```

```python tab="Python"
from fiscalisto import Fiscalisto

# La llave viene de FISCALISTO_API_KEY.
fiscalisto = Fiscalisto()

for item in fiscalisto.sat_mailbox_messages.list({"issuer": "iss_OufpKqnrvx9p", "unread": True}):
    print(item["id"])
```

```php tab="PHP"
<?php

use Fiscalisto\Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
$fiscalisto = new Fiscalisto();

foreach ($fiscalisto->satMailboxMessages->list([
    'issuer' => 'iss_OufpKqnrvx9p',
    'unread' => true,
]) as $item) {
    echo $item['id'], "\n";
}
```

```go tab="Go"
package main

import (
	"context"
	"fmt"
	"log"

	fiscalisto "fiscalisto.com/go"
)

func main() {
	// La llave viene de FISCALISTO_API_KEY.
	client, err := fiscalisto.NewClient("")
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	for item, err := range client.SatMailboxMessages.ListAutoPaging(ctx, &fiscalisto.ListSatMailboxMessagesParams{
		Issuer: fiscalisto.String("iss_OufpKqnrvx9p"),
		Unread: fiscalisto.Bool(true),
	}) {
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(item.ID)
	}
}
```

```java tab="Java"
import com.fiscalisto.FiscalistoClient;
import com.fiscalisto.model.ListSatMailboxMessagesParams;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

for (var item : fiscalisto.satMailboxMessages().listAutoPaging(new ListSatMailboxMessagesParams()
        .setIssuer("iss_OufpKqnrvx9p")
        .setUnread(true))) {
    System.out.println(item.getId());
}
```

```csharp tab="C#"
using Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

await foreach (var item in fiscalisto.SatMailboxMessages.ListAutoPagingAsync(new ListSatMailboxMessagesParams
{
    Issuer = "iss_OufpKqnrvx9p",
    Unread = true,
}))
    Console.WriteLine(item.Id);
```

Cada aviso trae `urgency` (`urgent`, `attention` o `informational`), `kind` (qué parece: requerimiento, multa, invitación…) y `action_required`, para que su sistema avise a quien deba contestarlo. Para leerlo completo y contestar, el emisor entra a su buzón en el SAT.

## Los hallazgos

```bash tab="cURL"
curl "https://api.fiscalisto.com/v1/sat_findings?issuer=iss_OufpKqnrvx9p&direction=received" \
  -H "Authorization: Bearer $FISCALISTO_API_KEY"
```

```ts tab="TypeScript"
import { Fiscalisto } from "fiscalisto";

// La llave viene de FISCALISTO_API_KEY.
const fiscalisto = new Fiscalisto();

const { data } = await fiscalisto.satFindings.list({
  issuer: "iss_OufpKqnrvx9p",
  direction: "received",
});
```

```python tab="Python"
from fiscalisto import Fiscalisto

# La llave viene de FISCALISTO_API_KEY.
fiscalisto = Fiscalisto()

data = fiscalisto.sat_findings.list({
    "issuer": "iss_OufpKqnrvx9p",
    "direction": "received",
})["data"]
```

```php tab="PHP"
<?php

use Fiscalisto\Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
$fiscalisto = new Fiscalisto();

$data = $fiscalisto->satFindings->list([
    'issuer' => 'iss_OufpKqnrvx9p',
    'direction' => 'received',
])['data'];
```

```go tab="Go"
package main

import (
	"context"
	"fmt"
	"log"

	fiscalisto "fiscalisto.com/go"
)

func main() {
	// La llave viene de FISCALISTO_API_KEY.
	client, err := fiscalisto.NewClient("")
	if err != nil {
		log.Fatal(err)
	}
	ctx := context.Background()

	satFindingList, err := client.SatFindings.List(ctx, &fiscalisto.ListSatFindingsParams{
		Issuer:    fiscalisto.String("iss_OufpKqnrvx9p"),
		Direction: fiscalisto.String("received"),
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%+v\n", *satFindingList)
}
```

```java tab="Java"
import com.fiscalisto.FiscalistoClient;
import com.fiscalisto.model.ListSatFindingsParams;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

var satFindingList = fiscalisto.satFindings().list(new ListSatFindingsParams()
        .setIssuer("iss_OufpKqnrvx9p")
        .setDirection("received"));
System.out.println(satFindingList);
```

```csharp tab="C#"
using Fiscalisto;

// La llave viene de FISCALISTO_API_KEY.
var fiscalisto = new FiscalistoClient();

var satFindingList = await fiscalisto.SatFindings.ListAsync(new ListSatFindingsParams
{
    Issuer = "iss_OufpKqnrvx9p",
    Direction = "received",
});
```

Uno por tipo, de lo más grave a lo menos: `code` (estable, como `efos_definitivo` o `efectivo_2000`), `severity` (`critical`, `warning` o `info`), `title`, `explanation`, cuántas facturas lo tienen (`invoices`) y la suma de sus totales (`amount`). Filtra por `direction` (`issued` o `received`) y por fecha de la factura con `date_gte` y `date_lte` (días de la Ciudad de México). Los hallazgos que alguien marcó como revisados en Fiscalisto no aparecen. No usa timbres.
