# Emisores

> Agregue los RFC que timbran desde su cuenta, con su CSD y su e.firma, y sepa en todo momento si ya pueden timbrar.

Un emisor es un RFC por el que su cuenta timbra. Una plataforma que factura por sus clientes tiene un emisor por cada uno; una empresa con varias razones sociales, uno por cada RFC. Todos usan los timbres de la misma cuenta.

## 1. Crear el emisor

```bash tab="cURL"
curl https://api.fiscalisto.com/v1/issuers \
  -H "Authorization: Bearer $FISCALISTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "tax_id": "TOL190312KT4",
    "legal_name": "TOLPI",
    "tax_system": "601",
    "zip": "64000",
    "metadata": { "cliente": "42" }
  }'
```

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

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

const issuer = await fiscalisto.issuers.create({
  tax_id: "TOL190312KT4",
  legal_name: "TOLPI",
  tax_system: "601",
  zip: "64000",
  metadata: { cliente: "42" },
});
```

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

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

issuer = fiscalisto.issuers.create({
    "tax_id": "TOL190312KT4",
    "legal_name": "TOLPI",
    "tax_system": "601",
    "zip": "64000",
    "metadata": {"cliente": "42"},
})
```

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

use Fiscalisto\Fiscalisto;

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

$issuer = $fiscalisto->issuers->create([
    'tax_id' => 'TOL190312KT4',
    'legal_name' => 'TOLPI',
    'tax_system' => '601',
    'zip' => '64000',
    'metadata' => ['cliente' => '42'],
]);
```

```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()

	issuer, err := client.Issuers.Create(ctx, &fiscalisto.CreateIssuerParams{
		TaxID:     "TOL190312KT4",
		LegalName: "TOLPI",
		TaxSystem: "601",
		Zip:       "64000",
		Metadata:  map[string]string{"cliente": "42"},
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(issuer.ID)
}
```

```java tab="Java"
import com.fiscalisto.FiscalistoClient;
import com.fiscalisto.model.CreateIssuerParams;
import java.util.Map;

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

var issuer = fiscalisto.issuers().create(new CreateIssuerParams()
        .setTaxId("TOL190312KT4")
        .setLegalName("TOLPI")
        .setTaxSystem("601")
        .setZip("64000")
        .setMetadata(Map.of("cliente", "42")));
System.out.println(issuer.getId());
```

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

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

var issuer = await fiscalisto.Issuers.CreateAsync(new CreateIssuerParams
{
    TaxId = "TOL190312KT4",
    LegalName = "TOLPI",
    TaxSystem = "601",
    Zip = "64000",
    Metadata = new() { ["cliente"] = "42" },
});
```

Escriba `legal_name`, `tax_system` y `zip` exactamente como en la constancia de situación fiscal del RFC: el SAT rechaza los comprobantes si no coinciden. La razón social va sin el régimen de capital (sin «S.A. de C.V.»). El código postal es también el lugar de expedición de sus comprobantes.

La respuesta es el emisor con `status: "needs_certificate"` y, en `requirement`, lo que falta.

## 2. Subir el CSD

El certificado de sello digital (CSD) es con el que se sella cada comprobante. Se tramita en el SAT con la e.firma y son dos archivos, `.cer` y `.key`, con su contraseña. Mándelos en base64:

```bash tab="cURL"
curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/certificates \
  -H "Authorization: Bearer $FISCALISTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"cer\": \"$(base64 < CSD.cer | tr -d '\n')\",
    \"key\": \"$(base64 < CSD.key | tr -d '\n')\",
    \"password\": \"$CSD_PASSWORD\"
  }"
```

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

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

const issuer = await fiscalisto.issuers.uploadCertificate("iss_OufpKqnrvx9p", {
  cer: (await readFile("CSD.cer")).toString("base64"),
  key: (await readFile("CSD.key")).toString("base64"),
  password: process.env.CSD_PASSWORD!,
});
```

```python tab="Python"
import base64
import os

from fiscalisto import Fiscalisto

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

issuer = fiscalisto.issuers.upload_certificate("iss_OufpKqnrvx9p", {
    "cer": base64.b64encode(open("CSD.cer", "rb").read()).decode(),
    "key": base64.b64encode(open("CSD.key", "rb").read()).decode(),
    "password": os.environ["CSD_PASSWORD"],
})
```

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

use Fiscalisto\Fiscalisto;

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

$issuer = $fiscalisto->issuers->uploadCertificate('iss_OufpKqnrvx9p', [
    'cer' => base64_encode(file_get_contents('CSD.cer')),
    'key' => base64_encode(file_get_contents('CSD.key')),
    'password' => getenv('CSD_PASSWORD'),
]);
```

```go tab="Go"
package main

import (
	"encoding/base64"
	"context"
	"fmt"
	"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()

	issuer, err := client.Issuers.UploadCertificate(ctx, "iss_OufpKqnrvx9p", &fiscalisto.UploadCertificateParams{
		Cer:      readBase64("CSD.cer"),
		Key:      readBase64("CSD.key"),
		Password: os.Getenv("CSD_PASSWORD"),
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(issuer.ID)
}

func readBase64(path string) string {
	b, err := os.ReadFile(path)
	if err != nil {
		log.Fatal(err)
	}
	return base64.StdEncoding.EncodeToString(b)
}
```

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

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

var issuer = fiscalisto.issuers().uploadCertificate("iss_OufpKqnrvx9p", new UploadCertificateParams()
        .setCer(Base64.getEncoder().encodeToString(Files.readAllBytes(Path.of("CSD.cer"))))
        .setKey(Base64.getEncoder().encodeToString(Files.readAllBytes(Path.of("CSD.key"))))
        .setPassword(System.getenv("CSD_PASSWORD")));
System.out.println(issuer.getId());
```

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

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

var issuer = await fiscalisto.Issuers.UploadCertificateAsync("iss_OufpKqnrvx9p", new UploadCertificateParams
{
    Cer = Convert.ToBase64String(File.ReadAllBytes("CSD.cer")),
    Key = Convert.ToBase64String(File.ReadAllBytes("CSD.key")),
    Password = Environment.GetEnvironmentVariable("CSD_PASSWORD")!,
});
```

Fiscalisto revisa que sea un CSD (no una e.firma), que sea de ese RFC, que no esté vencido, que el `.key` corresponda y que la contraseña lo abra; si algo no, responde `certificate_invalid` con el motivo. Los archivos se guardan cifrados y solo se usan para sellar. Cuando el CSD vence, suba el nuevo con la misma ruta: el emisor conserva sus comprobantes y sus folios.

## 3. Firmar con la e.firma (solo producción)

Antes de su primer timbre en producción, cada RFC firma una vez el contrato de servicio con el PAC usando su e.firma (antes FIEL):

```bash tab="cURL"
curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/efirma \
  -H "Authorization: Bearer $FISCALISTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d "{
    \"cer\": \"$(base64 < efirma.cer | tr -d '\n')\",
    \"key\": \"$(base64 < efirma.key | tr -d '\n')\",
    \"password\": \"$EFIRMA_PASSWORD\"
  }"
```

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

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

const issuer = await fiscalisto.issuers.signEfirma("iss_OufpKqnrvx9p", {
  cer: (await readFile("efirma.cer")).toString("base64"),
  key: (await readFile("efirma.key")).toString("base64"),
  password: process.env.EFIRMA_PASSWORD!,
});
```

```python tab="Python"
import base64
import os

from fiscalisto import Fiscalisto

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

issuer = fiscalisto.issuers.sign_efirma("iss_OufpKqnrvx9p", {
    "cer": base64.b64encode(open("efirma.cer", "rb").read()).decode(),
    "key": base64.b64encode(open("efirma.key", "rb").read()).decode(),
    "password": os.environ["EFIRMA_PASSWORD"],
})
```

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

use Fiscalisto\Fiscalisto;

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

$issuer = $fiscalisto->issuers->signEfirma('iss_OufpKqnrvx9p', [
    'cer' => base64_encode(file_get_contents('efirma.cer')),
    'key' => base64_encode(file_get_contents('efirma.key')),
    'password' => getenv('EFIRMA_PASSWORD'),
]);
```

```go tab="Go"
package main

import (
	"encoding/base64"
	"context"
	"fmt"
	"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()

	issuer, err := client.Issuers.SignEfirma(ctx, "iss_OufpKqnrvx9p", &fiscalisto.SignEfirmaParams{
		Cer:      readBase64("efirma.cer"),
		Key:      readBase64("efirma.key"),
		Password: os.Getenv("EFIRMA_PASSWORD"),
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(issuer.ID)
}

func readBase64(path string) string {
	b, err := os.ReadFile(path)
	if err != nil {
		log.Fatal(err)
	}
	return base64.StdEncoding.EncodeToString(b)
}
```

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

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

var issuer = fiscalisto.issuers().signEfirma("iss_OufpKqnrvx9p", new SignEfirmaParams()
        .setCer(Base64.getEncoder().encodeToString(Files.readAllBytes(Path.of("efirma.cer"))))
        .setKey(Base64.getEncoder().encodeToString(Files.readAllBytes(Path.of("efirma.key"))))
        .setPassword(System.getenv("EFIRMA_PASSWORD")));
System.out.println(issuer.getId());
```

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

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

var issuer = await fiscalisto.Issuers.SignEfirmaAsync("iss_OufpKqnrvx9p", new SignEfirmaParams
{
    Cer = Convert.ToBase64String(File.ReadAllBytes("efirma.cer")),
    Key = Convert.ToBase64String(File.ReadAllBytes("efirma.key")),
    Password = Environment.GetEnvironmentVariable("EFIRMA_PASSWORD")!,
});
```

La e.firma se usa en ese momento para firmar y no se guarda. Si manda el CSD en lugar de la e.firma, o la de otro RFC, responde `efirma_invalid`. Para pedirle al SAT las facturas del emisor, agregue `"keep_for_downloads": true` y se guarda cifrada (vea [Descargas del SAT](https://fiscalisto.com/docs/api/descargas-del-sat)).

## Que su cliente suba sus archivos

Si factura por sus clientes, lo más fácil es no pedirles sus archivos: mándelos a una página de Fiscalisto donde ellos suben su CSD y firman con su e.firma. Ni los archivos ni las contraseñas pasan por su plataforma.

```bash tab="cURL"
curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p/onboarding_links \
  -H "Authorization: Bearer $FISCALISTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "return_url": "https://miplataforma.mx/configuracion/facturas" }'
```

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

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

const issuerOnboardingLink = await fiscalisto.issuers.createOnboardingLink("iss_OufpKqnrvx9p", {
  return_url: "https://miplataforma.mx/configuracion/facturas",
});
```

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

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

issuer_onboarding_link = fiscalisto.issuers.create_onboarding_link("iss_OufpKqnrvx9p", {
    "return_url": "https://miplataforma.mx/configuracion/facturas",
})
```

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

use Fiscalisto\Fiscalisto;

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

$issuerOnboardingLink = $fiscalisto->issuers->createOnboardingLink('iss_OufpKqnrvx9p', [
    'return_url' => 'https://miplataforma.mx/configuracion/facturas',
]);
```

```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()

	issuerOnboardingLink, err := client.Issuers.CreateOnboardingLink(ctx, "iss_OufpKqnrvx9p", &fiscalisto.CreateIssuerOnboardingLinkParams{
		ReturnURL: fiscalisto.String("https://miplataforma.mx/configuracion/facturas"),
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%+v\n", *issuerOnboardingLink)
}
```

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

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

var issuerOnboardingLink = fiscalisto.issuers().createOnboardingLink("iss_OufpKqnrvx9p", new CreateIssuerOnboardingLinkParams()
        .setReturnUrl("https://miplataforma.mx/configuracion/facturas"));
System.out.println(issuerOnboardingLink);
```

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

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

var issuerOnboardingLink = await fiscalisto.Issuers.CreateOnboardingLinkAsync("iss_OufpKqnrvx9p", new CreateIssuerOnboardingLinkParams
{
    ReturnUrl = "https://miplataforma.mx/configuracion/facturas",
});
```

```json
{
  "object": "issuer_onboarding_link",
  "issuer": "iss_OufpKqnrvx9p",
  "url": "https://fiscalisto.com/alta/fko_…",
  "return_url": "https://miplataforma.mx/configuracion/facturas",
  "expires_at": "2026-10-25T10:15:00-06:00"
}
```

Abra `url` en el navegador de su cliente (con un enlace o una redirección, no dentro de un iframe). La página le pide solo lo que falta: el CSD, la firma del contrato o los dos, los revisa antes de mandarlos (que sean de ese RFC, vigentes y que la contraseña abra la llave) y al terminar le muestra **Volver a** su plataforma, que lleva a `return_url`. En ese momento llega el evento `issuer.ready`.

La liga sirve 24 horas (hasta 168 con `expires_in_hours`) y solo para ese emisor; cree otra cuando la necesite, por ejemplo cuando su CSD esté por vencer. Tras 20 intentos con archivos o contraseñas equivocados deja de servir. En pruebas la página revisa los archivos sin guardarlos y tiene **Completar en pruebas**, para que pruebe el flujo completo sin un CSD real.

## Estados

| `status` | Qué significa | Qué hacer |
| --- | --- | --- |
| `active` | Puede timbrar. | Nada. |
| `needs_certificate` | Falta su CSD. | Paso 2. |
| `needs_efirma` | Falta firmar el contrato con el PAC. | Paso 3. |
| `certificate_expired` | Su CSD venció. | Suba el nuevo (paso 2). |
| `archived` | Ya no timbra. | Cree otro emisor si lo necesita. |

`certificate.expires_at` dice cuándo vence el CSD: avise a su cliente antes de esa fecha. Cuando un emisor de producción pasa a `active` (por la API, el panel o una liga de alta) llega el evento `issuer.ready`.

## Pruebas

En pruebas no hace falta el CSD ni la e.firma: un emisor de pruebas nace `active` y timbra como el emisor de pruebas del SAT (`stamps_as: "EKU9003173C9"`), con el RFC que usted le dé. Si sube un CSD en pruebas se revisa igual que en producción, para que pruebe su flujo, pero se sigue timbrando con el de pruebas.

## Nómina

Para timbrar recibos de nómina, el emisor necesita su registro patronal (`employer_registration`) y, si es persona física, su `curp`:

```bash tab="cURL"
curl https://api.fiscalisto.com/v1/issuers/iss_OufpKqnrvx9p \
  -H "Authorization: Bearer $FISCALISTO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "employer_registration": "Y5412345109" }'
```

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

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

const issuer = await fiscalisto.issuers.update("iss_OufpKqnrvx9p", { employer_registration: "Y5412345109" });
```

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

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

issuer = fiscalisto.issuers.update("iss_OufpKqnrvx9p", {"employer_registration": "Y5412345109"})
```

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

use Fiscalisto\Fiscalisto;

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

$issuer = $fiscalisto->issuers->update('iss_OufpKqnrvx9p', ['employer_registration' => 'Y5412345109']);
```

```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()

	issuer, err := client.Issuers.Update(ctx, "iss_OufpKqnrvx9p", &fiscalisto.UpdateIssuerParams{
		EmployerRegistration: fiscalisto.String("Y5412345109"),
	})
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(issuer.ID)
}
```

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

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

var issuer = fiscalisto.issuers().update("iss_OufpKqnrvx9p", new UpdateIssuerParams().setEmployerRegistration("Y5412345109"));
System.out.println(issuer.getId());
```

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

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

var issuer = await fiscalisto.Issuers.UpdateAsync("iss_OufpKqnrvx9p", new UpdateIssuerParams
{
    EmployerRegistration = "Y5412345109",
});
```

## Archivar

[`DELETE /v1/issuers/{id}`](https://fiscalisto.com/docs/api/referencia/emisores/archive-issuer) archiva el emisor: deja de timbrar, pero sus comprobantes siguen disponibles y se pueden cancelar. El RFC de un emisor no cambia; si se equivocó, archívelo y cree otro.
