Saltar a contenido

Referencia de la API

Clientes

smartdoc.Client

Punto de entrada al SDK.

::

import smartdoc

sd = smartdoc.Client(api_key="pk_...")

factura = sd.invoices.create(
    receptor=smartdoc.Receptor.entity(
        ruc="80012345-1", social_name="Cliente S.A."
    ),
    items=[smartdoc.Item("Consultoría", quantity=1, unit_amount=1_100_000)],
)

Parameters:

Name Type Description Default
api_key str | None

la API Key del contribuyente. Si no se pasa, se lee de SMARTDOC_API_KEY.

None
base_url str | None

la URL de la API v2. Si no se pasa, se lee de SMARTDOC_BASE_URL, y si tampoco está, se usa la instancia de producción. Solo hace falta indicarla con una instancia propia.

None
establishment str | int | None

código de establecimiento por defecto. Solo hace falta si el contribuyente tiene más de uno.

None
dispatch_point str | int | None

punto de expedición por defecto. Ídem.

None
stamp tuple[str, str] | str | None

timbrado, como (identificador, fecha_inicio). Solo hace falta si hay más de uno habilitado.

None
timeout float

segundos de espera por solicitud.

30.0
max_retries int

reintentos ante 429, 5xx y fallas de red.

3
rate_limit_per_second float

regulación preventiva. La API permite 5 por segundo; poner 0 la desactiva.

5.0
auto_fill bool

si el SDK completa los datos del emisor. Apagarlo obliga a mandar los siete campos en cada documento.

True

taxpayer property

taxpayer: Any

El contribuyente de esta API Key. Se consulta una vez y se cachea.

refresh_config

refresh_config() -> None

Olvida lo descubierto: contribuyente, timbrado, establecimiento y punto.

Hace falta si se cargó un timbrado nuevo mientras el proceso corría.

smartdoc.async_client.AsyncClient

Igual que :class:smartdoc.Client, con await.

::

async with smartdoc.AsyncClient(api_key="pk_...") as sd:
    factura = await sd.invoices.create(
        receptor=smartdoc.Receptor.entity(
            ruc="80012345-1", social_name="Cliente S.A."
        ),
        items=[smartdoc.Item("Consultoría", quantity=1, unit_amount=1_100_000)],
    )

Los parámetros son los mismos que los del cliente síncrono.

taxpayer async

taxpayer() -> Any

El contribuyente de esta API Key. Se consulta una vez y se cachea.

refresh_config

refresh_config() -> None

Olvida lo descubierto: contribuyente, timbrado, establecimiento y punto.

Constructores de datos

smartdoc.Recipient dataclass

Datos del receptor de un documento.

En general no se instancia directo: se usan los constructores :meth:person, :meth:entity, :meth:foreign, :meth:not_nominated, :meth:government, :meth:diplomatic o :meth:from_client_id.

Parameters:

Name Type Description Default
client_type ClientType

tipo de receptor. Uno de PERSON, ENTITY, FOREIGN_ENTITY, NOT_NOMINATED, GOVERNMENT o DIPLOMATIC. Lo fija el constructor que se use.

required
ruc str

RUC con dígito verificador. Con una persona que no es contribuyente va la cédula; con un extranjero, su documento.

''
social_name str

razón social o nombre, tal como va impreso.

''
fantasy_name str | None

nombre de fantasía.

None
is_taxpayer bool

si el receptor es contribuyente.

False
country_code str

código de país. Por defecto PRY.

PARAGUAY
city City | None

ciudad, con :func:smartdoc.geo.find_city. Obligatoria si se pasa address, salvo con un receptor extranjero.

None
address str | None

dirección. Si se pasa, hacen falta también city y house_number.

None
house_number int | None

número de casa.

None
email str | None

correo al que SmartDoc manda el documento.

None
phone str | None

teléfono.

None
code str | None

identificador propio, de 3 a 15 caracteres. Queda impreso en el documento y sirve para conciliar contra tu base.

None
client_id int | None

id del cliente en SmartDoc, si ya está cargado.

None
extra dict[str, Any]

campos crudos que el SDK no modela.

dict()

person classmethod

person(
    *,
    ruc: str,
    name: str,
    is_taxpayer: bool = False,
    **kw: Any,
) -> Recipient

Una persona física.

ruc es el RUC con dígito verificador, o la cédula si no es contribuyente.

entity classmethod

entity(
    *,
    ruc: str,
    social_name: str,
    is_taxpayer: bool = True,
    **kw: Any,
) -> Recipient

Una empresa o entidad con RUC.

foreign classmethod

foreign(
    *,
    name: str,
    country_code: str,
    document_number: str = "",
    **kw: Any,
) -> Recipient

Un cliente del exterior.

Es el único tipo que puede llevar dirección sin códigos geográficos paraguayos.

not_nominated classmethod

not_nominated(**kw: Any) -> Recipient

Venta sin identificar al comprador.

Es la factura innominada del mostrador. Se puede nominar después con sd.invoices.nominate(id, client_id=...).

government classmethod

government(
    *, ruc: str, social_name: str, **kw: Any
) -> Recipient

Un organismo del Estado.

diplomatic classmethod

diplomatic(
    *, ruc: str, social_name: str, **kw: Any
) -> Recipient

Una misión diplomática con exoneración fiscal.

from_client_id classmethod

from_client_id(client_id: int, **kw: Any) -> Recipient

Un cliente ya cargado en SmartDoc, por su id.

Igual hay que dar los datos que van impresos en el documento; el id sirve para que SmartDoc lo asocie a su ficha.

to_api

to_api() -> dict[str, Any]

Los campos client* que espera la API.

validate_for

validate_for(allowed_types: frozenset[ClientType]) -> None

Verifica que este tipo de receptor sirva para el documento.

Los recibos, por ejemplo, aceptan solo persona, entidad y extranjero.

smartdoc.Item dataclass

Un ítem de un documento.

total_amount se calcula como quantity * unit_amount salvo que se pase explícitamente.

Parameters:

Name Type Description Default
description str

descripción del producto o servicio.

required
quantity float | int | Decimal

cantidad.

required
unit_amount float | int | Decimal

precio unitario, con el IVA ya incluido.

required
iva Iva | str

tratamiento de IVA de este ítem. Si no se pasa, se emite al 10%, que es la tasa general. Los cinco valores posibles son TEN, FIVE, EXEMPT, MIXED_TEN y MIXED_FIVE.

El tratamiento es una propiedad del producto, no de su categoría comercial: dentro de cualquier categoría hay excepciones. Emitir con el tratamiento equivocado obliga a anular y reemitir, así que conviene guardarlo en la ficha del producto y no inferirlo.

TEN
code str | None

código interno. Si no se pasa, se numera por posición.

None
measure_unit MeasureUnit | int

unidad de medida SIFEN. Por defecto UNIT (77), que es lo que usa SmartDoc en toda su interfaz.

UNIT
discount float | int | Decimal

descuento por unidad, no del total de la línea. Así lo interpreta la validación del servidor, que multiplica por la cantidad.

0
iva_base float | int | Decimal | None

porcentaje gravado, solo para los tipos de IVA mixtos.

None
obs str | None

observación de la línea.

None
total_amount float | int | Decimal | None

total de la línea, si se quiere fijar en vez de dejar que se calcule como quantity * unit_amount.

None
dncp_level_code_general str | None

código de nivel general de la DNCP, en contrataciones públicas.

None
dncp_level_code_specific str | None

código de nivel específico de la DNCP.

None

iva_type property

iva_type: Iva

El IVA como miembro del catálogo, sin importar cómo se haya pasado.

line_total property

line_total: Decimal

Total de la línea, antes de descuentos.

line_discount property

line_discount: Decimal

Descuento total de la línea: el unitario por la cantidad.

net property

net: Decimal

Lo que la línea aporta al total del documento.

taxable_share property

taxable_share: Decimal

Fracción de la línea que está gravada.

Es 1 salvo en los tipos mixtos, donde la define iva_base.

iva_amount property

iva_amount: Decimal

IVA contenido en la línea.

El precio ya lo incluye: al 10% se divide por 11, al 5% por 21.

to_api

to_api(*, position: int = 1) -> dict[str, Any]

El ítem en el formato que espera la API.

smartdoc.AssociatedDocument dataclass

Referencia al documento que se corrige o recibe.

Se construye con :meth:electronic, :meth:printed o :meth:none, que completan los campos que corresponden a cada tipo.

Parameters:

Name Type Description Default
type AssociatedDocumentType

uno de ELECTRONIC_INVOICE, PRINTED_INVOICE, RETENTION_CERTIFICATE o NONE. NONE solo lo aceptan los recibos y las notas de remisión.

required
cdc str | None

CDC del documento electrónico, de 44 caracteres.

None
printed_type PrintedDocumentType | int | None

qué documento impreso es. Uno de INVOICE, CREDIT_NOTE, DEBIT_NOTE, REMISSION_NOTE o WITHHOLDING_RECEIPT.

None
stamp_identifier str | None

timbrado del documento impreso, 8 dígitos.

None
establishment_code str | None

establecimiento del documento impreso, 3 dígitos.

None
dispatch_point_code str | None

punto de expedición del impreso, 3 dígitos.

None
number str | None

número del documento impreso, 7 dígitos.

None
document_date date | str | None

fecha del documento impreso.

None

electronic classmethod

electronic(cdc: str) -> AssociatedDocument

Una factura electrónica, referida por su CDC de 44 caracteres.

printed classmethod

printed(
    *,
    stamp_identifier: str,
    establishment_code: str,
    dispatch_point_code: str,
    number: str,
    document_date: date | str,
    printed_type: PrintedDocumentType
    | int = PrintedDocumentType.INVOICE,
) -> AssociatedDocument

Un documento preimpreso, de antes de la facturación electrónica.

none classmethod

none() -> AssociatedDocument

Sin documento asociado. Solo lo aceptan los recibos.

smartdoc.builders.items.totals_for

totals_for(
    items: Sequence[Item],
    *,
    global_discount: float | int | Decimal = 0,
    sedeco_discount: float | int | Decimal = 0,
) -> dict[str, Any]

Totales del documento a partir de sus ítems.

Devuelve amount más el desglose de IVA, listo para el cuerpo. El amount replica la fórmula que valida el servidor: suma de las líneas, menos los descuentos por ítem, menos el global, menos el de SEDECO.

Documentos

smartdoc.resources.invoices.Invoices

Bases: DocumentResource

Facturas electrónicas (/invoices).

Es el documento con más operaciones: seis acciones, borradores y nominación.

create

create(
    *,
    recipient: Recipient,
    items: Sequence[Item],
    sale_type: SaleType | str = SaleType.CASH,
    transaction_type: TransactionType
    | int = TransactionType.SERVICES,
    currency: Currency | str = Currency.PYG,
    invoice_date: date | datetime | str | None = None,
    exchange_rate: float | None = None,
    description: str | None = None,
    obs: str | None = None,
    term: str | None = None,
    invoice_number: str | None = None,
    global_discount: float = 0,
    sedeco_discount: float = 0,
    draft: bool = False,
    establishment: str | int | None = None,
    dispatch_point: str | int | None = None,
    idempotency_key: str | None = None,
    **extra: Any,
) -> Document

Emite una factura.

Los datos del emisor, el timbrado, los totales y el desglose de IVA los completa el SDK. Lo que hay que dar es a quién se le factura y qué se le factura.

Parameters:

Name Type Description Default
recipient Recipient

a quién se le factura. Ver :class:smartdoc.Recipient.

required
items Sequence[Item]

qué. Ver :class:smartdoc.Item.

required
sale_type SaleType | str

contado o crédito. Ojo: los valores son "cash" y "credit", no "Contado" — ver :class:smartdoc.SaleType.

CASH
transaction_type TransactionType | int

tipo de operación según SIFEN.

SERVICES
currency Currency | str

moneda. Con cualquiera distinta de PYG hace falta exchange_rate.

PYG
invoice_date date | datetime | str | None

fecha de emisión. Por defecto, ahora.

None
draft bool

si es True la factura queda en borrador y no se envía a la DNIT hasta llamar a :meth:confirm_draft.

False
establishment str | int | None

código de establecimiento, si no es el del cliente.

None
dispatch_point str | int | None

punto de expedición, si no es el del cliente.

None
idempotency_key str | None

si se quiere controlar la clave. Por defecto se genera una nueva por llamada.

None
**extra Any

campos crudos que el SDK no modela.

{}

Returns:

Type Description
Document

La factura recién creada. Su status va a ser pending o

Document

draft: la aprobación de la DNIT llega después, por webhook o

Document

consultando con :meth:wait_until_final.

update

update(
    id: int,
    *,
    recipient: Recipient,
    items: Sequence[Item],
    **kw: Any,
) -> Document

Reemplaza una factura.

No es un parche: la API valida el cuerpo completo igual que al crear, así que hay que pasar el documento entero.

Es la forma de corregir una factura que quedó en error. Al editarla, SmartDoc la vuelve a procesar sola: no hay que reenviarla ni reemitirla.

Se puede editar en cualquier estado salvo uploaded_to_set y approved_by_set; ahí la API responde 400 con EDIT_NOT_ALLOWED y la vía es anular y reemitir.

nominate

nominate(
    id: int, client_id: int, reason: str | None = None
) -> None

Le pone nombre a una factura innominada.

Se usa cuando se emitió sin identificar al comprador y después el cliente pide la factura a su nombre. El cliente tiene que estar cargado en SmartDoc: client_id es su id, el que devuelve sd.clients.

Parameters:

Name Type Description Default
client_id int

el cliente al que se nomina la factura.

required
reason str | None

motivo del evento, entre 5 y 500 caracteres. Si no se pasa, SmartDoc usa uno por omisión.

None

La nominación es asíncrona y la API responde su id, no el documento: hay que releerlo con :meth:get para ver cómo quedó.

confirm_draft

confirm_draft(id: int) -> Document

Confirma un borrador y lo envía a la DNIT.

discard_draft

discard_draft(id: int) -> Document

Descarta un borrador.

list

list(
    *,
    status: str | None = None,
    stamp_id: int | None = None,
    client_id: str | None = None,
    establishment_id: int | None = None,
    date_from: date | str | None = None,
    date_to: date | str | None = None,
    limit: int | None = None,
    cursor: str | None = None,
) -> Any

Lista facturas, de la más reciente a la más vieja.

date_to incluye el día que nombra: pedir hasta el 31 de julio trae las facturas del 31.

smartdoc.resources.receipts.Receipts

Bases: DocumentResource

Recibos electrónicos (/receipts).

create

create(
    *,
    recipient: Recipient,
    amount: float | int | Decimal,
    payment_method: ReceiptPaymentMethod
    | str = ReceiptPaymentMethod.CASH,
    associated_document: AssociatedDocument | None = None,
    items: Sequence[ReceiptItem] | None = None,
    currency: Currency | str = Currency.PYG,
    receipt_date: date | datetime | str | None = None,
    exchange_rate: float | None = None,
    invoice_id: int | None = None,
    check_bank: str | None = None,
    check_number: str | None = None,
    description: str | None = None,
    obs: str | None = None,
    draft: bool = False,
    establishment: str | int | None = None,
    dispatch_point: str | int | None = None,
    idempotency_key: str | None = None,
    **extra: Any,
) -> Document

Emite un recibo.

Parameters:

Name Type Description Default
amount float | int | Decimal

monto cobrado.

required
payment_method ReceiptPaymentMethod | str

efectivo, cheque o transferencia. Con cheque hay que dar además check_bank y check_number.

CASH
associated_document AssociatedDocument | None

la factura que se está cobrando. A diferencia de las notas, acá es opcional: se puede usar AssociatedDocument.none().

None

Returns:

Type Description
Document

El recibo. Su estado final de éxito es generated, no

Document

approved_by_set.

confirm_draft

confirm_draft(id: int) -> Document

Confirma un borrador y lo genera.

discard_draft

discard_draft(id: int) -> Document

Descarta un borrador.

resend_to_set

resend_to_set(id: int) -> None

No existe para recibos: no se envían a la DNIT.

smartdoc.resources.notes.CreditNotes

Bases: _Nota

Notas de crédito electrónicas (/credit_notes).

smartdoc.resources.notes.DebitNotes

Bases: _Nota

Notas de débito electrónicas (/debit_notes).

No tienen /kude en la API v2.

smartdoc.resources.remission_notes.RemissionNotes

Bases: DocumentResource

Notas de remisión electrónicas (/remission_notes).

create

create(
    *,
    recipient: Recipient,
    items: Sequence[RemissionItem],
    departure: TransportEndpoint,
    arrival: TransportEndpoint,
    vehicle: Vehicle,
    carrier: Carrier,
    estimated_kilometers: int,
    start_date: date | str,
    end_date: date | str,
    associated_document: Any = None,
    reason: RemissionReason | int = RemissionReason.SALE,
    responsible: RemissionResponsible
    | int = RemissionResponsible.INVOICE_ISSUER,
    transport_type: TransportType | int = TransportType.OWN,
    transport_mode: TransportMode
    | int = TransportMode.LAND,
    freight_responsible: FreightResponsible
    | int = FreightResponsible.OWN_TRANSPORT,
    remission_date: date | datetime | str | None = None,
    invoice_id: int | None = None,
    description: str | None = None,
    obs: str | None = None,
    number: str | None = None,
    establishment: str | int | None = None,
    dispatch_point: str | int | None = None,
    idempotency_key: str | None = None,
    **extra: Any,
) -> Document

Emite una nota de remisión.

Parameters:

Name Type Description Default
departure TransportEndpoint

de dónde sale la mercadería.

required
arrival TransportEndpoint

a dónde llega.

required
estimated_kilometers int

distancia estimada del traslado. Obligatorio.

required
start_date date | str

fecha estimada de inicio del traslado. Obligatorio.

required
end_date date | str

fecha estimada de fin. Obligatorio.

required
associated_document Any

documento que ampara el traslado. Si no se pasa, se manda AssociatedDocument.none().

None
reason RemissionReason | int

motivo del traslado. SIFEN define 15; SmartDoc solo ofrece CONSIGNMENT en su interfaz, pero la API acepta todos.

SALE

smartdoc.resources.auto_invoices.AutoInvoices

Bases: DocumentResource

Autofacturas electrónicas (/auto_invoices).

create

create(
    *,
    provider: Provider,
    items: Sequence[Item],
    transaction_address: str,
    transaction_city: City,
    transaction_house_number: int,
    certificate_code: str,
    certificate_number: str,
    currency: Currency | str = Currency.PYG,
    auto_invoice_date: date | datetime | str | None = None,
    exchange_rate: float | None = None,
    description: str | None = None,
    obs: str | None = None,
    term: str | None = None,
    number: str | None = None,
    global_discount: float = 0,
    establishment: str | int | None = None,
    dispatch_point: str | int | None = None,
    idempotency_key: str | None = None,
    **extra: Any,
) -> Document

Emite una autofactura.

Parameters:

Name Type Description Default
provider Provider

a quién se le compró. Ver :class:Provider.

required
transaction_address str

dónde se hizo la operación. La DNIT lo pide aparte del domicilio del proveedor, porque pueden no coincidir.

required

Piezas de la nota de remisión

smartdoc.resources.remission_notes.RemissionItem dataclass

Un bien trasladado. Sin monto: la remisión no liquida impuestos.

Parameters:

Name Type Description Default
description str

qué se traslada.

required
quantity float | int

cantidad.

required
code str | None

código interno. Si no se pasa, se numera por posición.

None
measure_unit MeasureUnit | int

unidad de medida SIFEN. Por defecto UNIT (77).

UNIT
obs str | None

observación de la línea.

None

smartdoc.resources.remission_notes.TransportEndpoint dataclass

Un extremo del traslado: de dónde sale o a dónde llega.

Parameters:

Name Type Description Default
address str

dirección del local.

required
city City

ciudad, con :func:smartdoc.geo.find_city. De ahí salen los códigos de departamento, distrito y ciudad.

required
house_number int

número de casa. Obligatorio en los dos extremos.

0

to_api

to_api(prefix: str) -> dict[str, Any]

Serializa con prefijo transportDeparture o transportArrival.

smartdoc.resources.remission_notes.Vehicle dataclass

El vehículo que traslada.

Parameters:

Name Type Description Default
brand str

marca, de 1 a 10 caracteres. "Mercedes-Benz" no entra: abreviala.

required
type str

descripción libre del vehículo, de 4 a 10 caracteres: "CAMION", "Camioneta", "Furgón". No es transport_type de :meth:RemissionNotes.create, que es un código y define si el transporte es propio o de terceros.

required
document_type VehicleIdentificationType | int

cómo se identifica el vehículo. Decide cuál de los dos números hay que dar; el otro se ignora.

REGISTRATION
document_number str

número de identificación, de 1 a 20 caracteres. Obligatorio con document_type=VIN.

''
registration_number str

matrícula, de 6 o 7 caracteres. Obligatorio con document_type=REGISTRATION, que es el valor por omisión.

''

smartdoc.resources.remission_notes.Carrier dataclass

El transportista y su conductor.

Parameters:

Name Type Description Default
social_name str

razón social o nombre del transportista.

required
ruc str

RUC con dígito verificador, si es contribuyente.

''
is_taxpayer bool

si el transportista es contribuyente.

False
document_type CarrierDocumentType | int

tipo de documento del transportista. Uno de PARAGUAYAN_ID, PASSPORT, FOREIGN_ID, RESIDENCE_CARD, UNNAMED, DIPLOMATIC_CARD o UNSPECIFIED.

PARAGUAYAN_ID
document_number str

número de ese documento.

''
address str

dirección del transportista.

''
driver_name str

nombre del conductor.

''
driver_document_number str

documento del conductor.

''
driver_address str

dirección del conductor.

''

Piezas de la autofactura

smartdoc.resources.auto_invoices.Provider

El proveedor al que se le compró.

Se construye con :meth:no_ruc o :meth:foreign, según por qué el proveedor no puede emitir factura.

Parameters:

Name Type Description Default
provider_type ProviderType | str

por qué no puede emitir factura. NO_RUC si es paraguayo y no es contribuyente, FOREIGN si es del exterior.

required
full_name str

nombre completo del proveedor.

required
document_type ProviderDocumentType | str

tipo de documento. Uno de PARAGUAYAN_ID, PASSPORT, FOREIGN_ID o RESIDENCE_CARD.

required
document_number str

número de ese documento.

required
address str

domicilio del proveedor. Va aparte del lugar donde se hizo la operación, que se pasa en transaction_address.

required
city City

ciudad, con :func:smartdoc.geo.find_city.

required
house_number int

número de casa.

0

no_ruc classmethod

no_ruc(
    *,
    full_name: str,
    document_number: str,
    address: str,
    city: City,
    document_type: ProviderDocumentType
    | str = ProviderDocumentType.PARAGUAYAN_ID,
    house_number: int = 0,
) -> Provider

Un proveedor paraguayo que no es contribuyente.

foreign classmethod

foreign(
    *,
    full_name: str,
    document_number: str,
    address: str,
    city: City,
    document_type: ProviderDocumentType
    | str = ProviderDocumentType.PASSPORT,
    house_number: int = 0,
) -> Provider

Un proveedor del exterior.

Piezas del recibo

smartdoc.resources.receipts.ReceiptItem dataclass

Un concepto del recibo.

Mucho más simple que el ítem de una factura: acá no se liquida IVA.

Parameters:

Name Type Description Default
description str

concepto que se cobra.

required
amount float | int | Decimal

monto del concepto.

required

Recursos de soporte

smartdoc.resources.support.Stamps

Bases: Resource[Stamp]

Timbrados (/stamps).

create

create(**fields: Any) -> StampModel

Carga un timbrado nuevo.

list

list(
    *,
    enabled: bool | None = None,
    limit: int | None = None,
    cursor: str | None = None,
) -> Any

Lista timbrados. enabled=True deja solo los vigentes.

smartdoc.resources.support.Clients

Bases: Resource[Client]

Clientes (/clients).

Cargar un cliente acá es opcional para facturar —los datos del receptor van en el documento— pero sirve para reusarlos y para nominar una factura innominada después.

smartdoc.resources.support.Establishments

Bases: Resource[Establishment]

Establecimientos (/establishments).

La API no expone borrado: un establecimiento con documentos emitidos no puede desaparecer sin romper la trazabilidad.

smartdoc.resources.support.DispatchPoints

Bases: Resource[DispatchPoint]

Puntos de expedición (/dispatch_points).

Como los establecimientos, no se pueden borrar por la API.

smartdoc.resources.support.TaxPayerResource

Bases: Resource[TaxPayer]

El contribuyente dueño de la API Key (/taxpayer). Solo lectura.

get

get(id: int | None = None) -> TaxPayerModel

Los datos del contribuyente.

No lleva id: la API Key ya determina de quién se trata.

smartdoc.resources.support.ReceivedDocuments

Bases: Resource[Record]

Documentos recibidos de proveedores (/received_documents).

Sirven para registrar las compras: facturas, notas de crédito y de débito que otros contribuyentes emitieron a nombre propio.

lookup_by_cdc

lookup_by_cdc(cdc: str) -> Record

Consulta un documento en la DNIT por su CDC.

Trae los datos del documento tal como los tiene la DNIT, sin registrarlo. Sirve para verificar una factura de proveedor antes de cargarla.

smartdoc.resources.support.Sandbox

Operaciones que solo existen en cuentas demo (/sandbox).

En una cuenta demo los documentos se generan de forma válida pero no se envían a la DNIT: la respuesta se simula. Eso permite integrar y probar todo el circuito, webhooks incluidos.

Por defecto todo se aprueba. Para probar el camino de error hay dos formas:

  • RUC de receptor reservado. 99999901 fuerza un rechazo de la DNIT con código 0160; 99999902 fuerza un error de procesamiento de lote con código 0301. Son la parte numérica del RUC: hay que mandarlos con dígito verificador, "99999901-7", porque la API lo exige.
  • Este endpoint, que cambia el estado de un documento ya emitido.

En ambos casos se disparan los mismos eventos y webhooks que en el flujo real.

force_status

force_status(cdc: str, status: str) -> Document

Fuerza el estado de un documento ya emitido.

Parameters:

Name Type Description Default
cdc str

el CDC del documento.

required
status str

"approved" o "error".

required

Raises:

Type Description
ValidationError

si la cuenta no es demo, la API responde SANDBOX_ONLY.

Base compartida

smartdoc.resources.base.Resource

Bases: Generic[R]

CRUD sobre un recurso de la API.

get

get(id: int) -> R

Un recurso por su id.

list

list(
    *,
    limit: int | None = None,
    cursor: str | None = None,
    **filtros: Any,
) -> Page[R]

Una página de resultados.

Para recorrer todo sin escribir el bucle del cursor::

for factura in sd.invoices.list().auto_paging_iter():
    ...

delete

delete(id: int) -> None

Borra el recurso.

smartdoc.resources.base.DocumentResource

Bases: Resource[Document]

Un recurso que además emite documentos electrónicos.

cancel

cancel(id: int, reason: str) -> None

Anula el documento ante la DNIT.

Solo se puede anular un documento aprobado, y el motivo es obligatorio. La anulación es asíncrona: el documento pasa a cancellation_requested y termina en cancelled.

No devuelve el documento porque la API no lo manda —responde el id de la anulación—; para ver cómo quedó hay que releerlo con :meth:get o esperarlo con :meth:wait_until_final.

resend_email

resend_email(id: int) -> None

Reenvía el documento por correo al receptor.

resend_to_set

resend_to_set(id: int) -> None

Reenvía a la DNIT un documento que quedó en error.

Como :meth:cancel, no devuelve el documento: hay que releerlo.

kude_url

kude_url(id: int) -> str

URL temporal del KuDE en PDF.

Está disponible recién cuando el documento se generó. Antes, la API responde 404.

wait_until_final

wait_until_final(
    id: int,
    *,
    timeout: float = 300.0,
    poll_interval: float = 2.0,
) -> Document

Consulta el documento hasta que llegue a un estado final.

Sirve para el flujo "emito y necesito el CDC ahora". Si se puede escuchar webhooks, es mejor eso: esto consume cuota del límite de 5 solicitudes por segundo.

El plazo por omisión es holgado a propósito: un documento pasa por varios estados intermedios antes de quedar aprobado, y cada salto lo da el servidor por su cuenta, así que el recorrido completo puede llevar minutos. Agotar la espera no cancela nada —el documento sigue su curso—, así que cortar temprano solo confunde.

Ojo con los estados intermedios que parecen finales: recoverable_error no corta la espera, porque SmartDoc lo reintenta solo.

Raises:

Type Description
TimeoutError

si se agota el tiempo. El documento sigue su curso; lo que se agotó es la espera.

Objetos de respuesta

smartdoc.models.Document

Bases: Record

Un documento electrónico: factura, recibo, nota o autofactura.

status property

status: DocumentStatus | str

Estado actual. Devuelve el valor crudo si SmartDoc agregó uno nuevo.

cdc property

cdc: str | None

Código de Control de 44 caracteres.

Es None mientras el documento no se generó, y no existe en los recibos, que no se envían a la DNIT.

is_terminal property

is_terminal: bool

Si el documento ya no se mueve solo.

is_approved property

is_approved: bool

Si quedó válido fiscalmente.

Para un recibo, alcanza con estar generado: no pasa por la DNIT.

error_code property

error_code: str | None

Código del error, o None si el documento no está en error.

Puede ser None aun con el documento en error, según de dónde venga el rechazo; :attr:error_message trae el detalle en todos los casos. Para ramificar conviene :attr:is_error.

error_message property

error_message: str | None

Descripción del error, o None si el documento no está en error.

amount property

amount: float | None

Monto total. Las notas de remisión no tienen.

smartdoc.models.TaxPayer

Bases: Record

El contribuyente dueño de la API Key.

is_demo property

is_demo: bool

En una cuenta demo los documentos no se envían a la DNIT.

La respuesta se simula, así que se puede integrar y probar todo el circuito, webhooks incluidos.

Con una salvedad: antes de enviar nada, SmartDoc verifica que el certificado del contribuyente esté vigente, y esa verificación no distingue si la cuenta es demo. Con el certificado vencido el documento queda en error con código 2450 y nunca llega a la simulación. Los recibos no aplican esa verificación.

smartdoc.models.Record

Un objeto de la API, con acceso por clave a lo que el SDK no modela.

No hereda de Mapping a propósito: :class:Document necesita exponer items como los ítems del documento, y eso chocaría con el items() de la interfaz de mapeo. Para el JSON completo está :attr:raw.

raw property

raw: dict[str, Any]

El JSON completo tal como lo devolvió la API.

get

get(key: str, default: Any = None) -> Any

El valor de un campo que el SDK no modela.

Paginación

smartdoc._core.pagination.Page dataclass

Bases: Generic[T]

Una página de resultados.

Se comporta como una secuencia: len(page), page[0], for x in page recorren solo esta página. Para recorrer todas, usar :meth:auto_paging_iter.

next_page

next_page() -> Page[T] | None

La página siguiente, o None si esta era la última.

auto_paging_iter

auto_paging_iter() -> Iterator[T]

Recorre todos los resultados, siguiendo el cursor solo.

El transporte regula a 5 solicitudes por segundo, así que recorrer un listado largo es lento pero no dispara 429.

smartdoc._core.pagination.AsyncPage dataclass

Bases: Generic[T]

Igual que :class:Page, para el cliente asíncrono.

next_page async

next_page() -> AsyncPage[T] | None

La página siguiente, o None si esta era la última.

auto_paging_iter async

auto_paging_iter() -> Any

Recorre todos los resultados. Se usa con async for.

Webhooks

smartdoc.Webhooks

Recibe, verifica y rutea los webhooks de SmartDoc.

Parameters:

Name Type Description Default
secret str

el secreto del endpoint, que SmartDoc muestra al crearlo en el panel. Cada endpoint tiene el suyo.

required
max_age float | None

segundos de tolerancia para el timestamp del evento. None desactiva la comprobación. El default de 5 minutos es holgado: un reintento legítimo trae siempre un timestamp nuevo.

300.0
dedupe bool

si se descartan las entregas repetidas.

True
dedupe_size int

cuántos ids de entrega recordar.

1000

on

on(
    *event_types: Event | str,
) -> Callable[[Handler], Handler]

Registra un handler para uno o varios tipos de evento.

::

@webhooks.on("invoice.approved")
def aprobada(evento):
    print(evento.entity_id, evento.cdc)

@webhooks.on(Event.INVOICE_ERROR, Event.CREDIT_NOTE_ERROR)
def falla(evento):
    avisar(evento.error_message)

on_any

on_any() -> Callable[[Handler], Handler]

Registra un handler que recibe todos los eventos.

on_any_error

on_any_error() -> Callable[[Handler], Handler]

Registra un handler para los cinco eventos *.error.

Son cinco y no seis: receipt.error no existe, porque un recibo que falla queda en pending y se reintenta.

handlers_for

handlers_for(event_type: str) -> Iterable[Handler]

Los handlers que aplican a un tipo de evento.

signature_for

signature_for(body: bytes) -> str

La firma esperada para un cuerpo, con el formato del encabezado.

verify

verify(
    body: bytes | str, headers: Mapping[str, str]
) -> WebhookEvent

Verifica la firma y devuelve el evento.

Parameters:

Name Type Description Default
body bytes | str

el cuerpo crudo de la solicitud. Si se pasa el JSON reserializado, la firma no va a validar.

required
headers Mapping[str, str]

los encabezados. La búsqueda no distingue mayúsculas.

required

Raises:

Type Description
SignatureError

si falta la firma, no coincide, o el evento es demasiado viejo.

is_duplicate

is_duplicate(event: WebhookEvent) -> bool

Si esta entrega ya se procesó, y la marca como vista.

SmartDoc reintenta hasta 5 veces, y una entrega que el servidor procesó pero respondió tarde se vuelve a mandar. Sin esto, un webhook lento genera trabajo duplicado.

dispatch

dispatch(
    body: bytes | str, headers: Mapping[str, str]
) -> WebhookEvent

Verifica, deduplica y corre los handlers.

Si un handler levanta una excepción, se loguea y no se propaga: reintentar no arregla un bug de código, y SmartDoc solo hace 5 intentos en algo más de un minuto. Lo que corresponde ahí es responder 200 y arreglar el handler.

flask_view

flask_view() -> Callable[..., Any]

Una vista de Flask lista para montar.

::

app.add_url_rule(
    "/webhooks", view_func=webhooks.flask_view(), methods=["POST"]
)

fastapi_route

fastapi_route() -> Callable[..., Any]

Un handler de FastAPI listo para montar.

::

app.post("/webhooks")(webhooks.fastapi_route())

django_view

django_view() -> Callable[..., Any]

Una vista de Django lista para montar.

::

urlpatterns += [path("webhooks/", webhooks.django_view())]

serve

serve(
    port: int = 3000,
    host: str = "127.0.0.1",
    path: str = "/",
) -> None

Levanta un servidor mínimo, para desarrollo.

Bloquea hasta Ctrl-C. No usar en producción: es de un solo hilo y no tiene nada de lo que hace falta para aguantar tráfico real.

SmartDoc exige que la URL del endpoint sea https:// y alcanzable desde internet, así que localhost no sirve como endpoint: hay que exponer el puerto con un túnel (ngrok, cloudflared, smee.io) y registrar la URL pública que devuelva.

smartdoc.WebhookEvent dataclass

Una entrega de webhook, ya verificada y parseada.

Los campos del sobre son estables. Los de data no todos: SmartDoc garantiza un subconjunto —id, status, los datos del emisor, el timbrado, el establecimiento y el punto de expedición— y agrega otros propios de cada tipo de documento que pueden cambiar sin aviso.

Por eso la recomendación es tratar el evento como un disparador y, si hace falta el detalle completo y estable, traerlo con :meth:fetch.

status property

status: DocumentStatus | str

Estado del documento al momento del evento.

cdc property

cdc: str | None

CDC del documento. Siempre None en recibos, que no tienen.

subtype property

subtype: str

approved, cancelled, pending o error.

document_key property

document_key: tuple[str, int]

Identifica al documento sin ambigüedad: (entidad, id).

entity_id no es único por sí solo. Se numera por tipo de documento, así que el recibo 20 y la nota de débito 20 conviven. Guardar solo el número y buscar por él cruza documentos distintos.

Esta es la clave que corresponde usar contra la propia base::

Venta.objects.get(
    smartdoc_entity=evento.document_key[0],
    smartdoc_id=evento.document_key[1],
)

is_error property

is_error: bool

Si el documento quedó en error.

El motivo está en :attr:error_code y :attr:error_message.

is_approved property

is_approved: bool

Si el documento quedó válido.

Para un recibo, esto es cierto con el documento en generated: no pasa por la DNIT.

is_final property

is_final: bool

Si el documento ya no va a cambiar de estado por su cuenta.

error_code property

error_code: str | None

Código del error, o None si el documento no está en error.

Para ramificar conviene :attr:is_error.

error_message property

error_message: str | None

Descripción del error, o None si el documento no está en error.

fetch

fetch(client: Client) -> Document

Trae el documento completo desde la API.

Sirve cuando hace falta un campo que no está garantizado en data.

from_payload classmethod

from_payload(payload: dict[str, Any]) -> WebhookEvent

Construye el evento desde el cuerpo ya parseado.

Geografía

smartdoc.geo.find_city

find_city(
    name: str,
    *,
    department: str | None = None,
    district: str | None = None,
) -> City

Busca una ciudad por nombre exacto y devuelve una sola.

Ignora mayúsculas y tildes. Si el nombre es ambiguo se levanta ValidationError con todas las opciones y su código, en vez de elegir una: la ciudad queda impresa en el documento y adivinar sería imprimir una dirección equivocada.

La ambigüedad viene de dos lados. A veces son ciudades homónimas en departamentos distintos (hay tres CONCEPCION), y ahí desempata department. Otras veces SIFEN distingue el centro urbano del municipio dentro del mismo distrito —ENCARNACION y ENCARNACION (MUNICIPIO)—, y ahí no hay nada que desempate: hay que elegir por código con :func:city.

Para búsquedas parciales o exploratorias, usar :func:search_cities.

smartdoc.geo.search_cities

search_cities(term: str, *, limit: int = 20) -> list[City]

Ciudades cuyo nombre contiene term, ignorando mayúsculas y tildes.

smartdoc.geo.city

city(code: int) -> City

La ciudad con ese código.

smartdoc.geo.City dataclass

Ciudad, con su distrito y su departamento.

Es lo que se le pasa a Receptor: de acá salen los seis campos de dirección que exige el documento electrónico.

smartdoc.geo.District dataclass

Distrito, con su departamento.

smartdoc.geo.Department dataclass

Departamento del catálogo de la DNIT.

Catálogos

smartdoc.constants.documents

Catálogos propios de SmartDoc: valores de texto que espera la API v2.

A diferencia de los de :mod:smartdoc.constants.sifen, que son códigos numéricos de la DNIT, estos son las cadenas que SmartDoc guarda en su base. Son cerrados: mandar otra cosa no es "un código que el SDK todavía no conoce", es un error.

Conviene no confundir el valor con la etiqueta: en :class:SaleType, lo que viaja en el campo es "cash", mientras que "Contado" es el texto para mostrar en pantalla. Los constructores aceptan las dos formas y mandan siempre el valor.

Iva

Bases: Catalog

Tratamiento de IVA de un ítem (items[].ivaType).

Los dos tipos mixtos exigen además ivaBase, el porcentaje de la operación que está gravado.

rate property
rate: float

Tasa como fracción: 0.10, 0.05 o 0.0.

Para los mixtos es la tasa que se aplica sobre la parte gravada.

is_mixed property
is_mixed: bool

Si exige ivaBase.

Currency

Bases: Catalog

Moneda del documento (currency).

SIFEN admite las ~200 monedas ISO, pero SmartDoc valida contra estas tres y rechaza el resto, así que el catálogo es cerrado.

Con cualquier moneda distinta de :attr:PYG hay que mandar además pygExchangeRate.

SaleType

Bases: Catalog

Condición de venta de una factura (saleType).

Ojo con el valor: es "cash" / "credit", no "Contado" / "Crédito". Ver la nota al principio del módulo.

ClientType

Bases: Catalog

Naturaleza del receptor (clientType).

Los recibos aceptan solo :attr:PERSON, :attr:ENTITY y :attr:FOREIGN_ENTITY; el resto de los documentos acepta los seis.

:attr:FOREIGN_ENTITY es el único tipo que no exige dirección aunque clientHasAddress sea verdadero.

AssociatedDocumentType

Bases: Catalog

Tipo de documento asociado (associatedDocumentType).

Las notas de crédito y débito aceptan :attr:ELECTRONIC_INVOICE y :attr:PRINTED_INVOICE; los recibos aceptan además :attr:NONE.

Con :attr:ELECTRONIC_INVOICE hay que mandar associatedDocumentCdc. Con :attr:PRINTED_INVOICE, los datos del documento impreso: timbrado, establecimiento, punto de expedición, número y fecha.

DocumentType

Bases: Catalog

Los tipos de documento electrónico que emite SmartDoc.

Sirve para trabajar de forma genérica sobre un documento; cada uno tiene su recurso en el cliente (sd.invoices, sd.credit_notes, …).

ReceiptPaymentMethod

Bases: Catalog

Forma de pago de un recibo (paymentMethod).

ProviderType

Bases: Catalog

Naturaleza del proveedor en una autofactura (providerType).

La autofactura se emite cuando se compra a alguien que no puede emitir factura, así que el proveedor es siempre uno de estos dos.

ProviderDocumentType

Bases: Catalog

Documento de identidad del proveedor (providerDocumentType).

smartdoc.constants.sifen

Catálogos oficiales de SIFEN (códigos numéricos de la DNIT).

Están los catálogos completos de la DNIT, y solo aquellos que tienen un campo donde ir en el cuerpo de algún endpoint de la API v2.

Todos son abiertos: si la DNIT agrega un código, se puede pasar el entero crudo sin esperar una versión nueva del SDK.

TransactionType

Bases: IntCatalog

Tipo de transacción de una factura (transactionTypeCode).

La API valida que esté entre 1 y 13, que es justo el rango de este catálogo.

MeasureUnit

Bases: IntCatalog

Unidad de medida de un ítem (items[].measureUnit).

SmartDoc no ofrece un selector para esto: fija UNIT (77) en todos sus formularios. La API acepta cualquier código del catálogo.

RemissionReason

Bases: IntCatalog

Motivo de una nota de remisión (reasonCode).

RemissionResponsible

Bases: IntCatalog

Responsable de la emisión de la nota de remisión (responsibleCode).

TransportType

Bases: IntCatalog

Tipo de transporte (transportTypeCode).

TransportMode

Bases: IntCatalog

Modalidad de transporte (transportModeCode).

FreightResponsible

Bases: IntCatalog

Responsable del flete (transportResponsibleCode).

VehicleIdentificationType

Bases: IntCatalog

Tipo de identificación del vehículo (transportVehicleDocumentTypeCode).

PrintedDocumentType

Bases: IntCatalog

Tipo de documento impreso asociado (associatedDocumentPrintedType).

Se usa cuando el documento asociado no es electrónico.

CarrierDocumentType

Bases: IntCatalog

Tipo de documento del transportista (carrierDocumentTypeCode).

Es el catálogo de documentos de identidad del receptor que usa SIFEN.

smartdoc.constants.statuses

Estados de un documento electrónico.

Los seis tipos de documento comparten el mismo vocabulario de estados —19 en total, todos alcanzables por una factura— así que hay un solo :class:DocumentStatus. Lo que cambia por tipo es cuáles son alcanzables, y eso está en :data:REACHABLE.

El flujo habitual es::

pending → generated → uploaded_to_set → approved_by_set

Las agrupaciones (:data:SUCCESS, :data:ERROR, :data:TERMINAL) siguen la misma clasificación que usa SmartDoc para decidir qué webhook dispara cada estado, así que "esperar a que termine" y "esperar el evento" coinciden.

El recibo es la excepción. No se envía a la DNIT, así que nunca llega a approved_by_set: su estado final de éxito es generated, que para todos los demás documentos es un paso intermedio. Por eso conviene preguntar con :func:is_terminal y :func:is_success, que reciben el tipo de documento, en vez de comparar contra los conjuntos a mano.

DocumentStatus

Bases: Catalog

Estado de un documento electrónico.

is_success

is_success(
    status: str, document_type: DocumentType | None = None
) -> bool

Si el documento quedó válido.

Para un recibo, generated cuenta como éxito: es su estado final, porque no se envía a la DNIT. Para el resto, generated es intermedio.

is_error

is_error(status: str) -> bool

Si el documento quedó en un error del que no sale solo.

is_terminal

is_terminal(
    status: str, document_type: DocumentType | None = None
) -> bool

Si el documento ya no va a cambiar de estado por su cuenta.

Es lo que usa wait_until_final para saber cuándo dejar de consultar.

smartdoc.constants.events

Tipos de evento de webhook.

Son 23, con formato {entidad}.{subtipo}: seis entidades por cuatro subtipos, menos receipt.error, que no existe —si falla la generación de un recibo, el recibo queda en pending y se reintenta, no pasa a un estado de error—.

Un detalle que cambia cómo se escucha: receipt.approved no significa aprobado por la DNIT. El recibo no se envía a la DNIT, así que ese evento quiere decir "el recibo se generó y su KuDE ya está disponible", con el documento en estado generated. Esperar un approved_by_set para un recibo es esperar algo que nunca llega.

EntityType

Bases: Catalog

La entidad a la que pertenece un evento (campo entityType).

document_type property
document_type: DocumentType

El :class:DocumentType equivalente.

Event

Bases: Catalog

Tipo de evento de webhook.

entity property
entity: EntityType

La entidad del evento.

subtype property
subtype: str

El subtipo: approved, cancelled, pending o error.

smartdoc.constants.permissions

Permisos de una API Key.

Dos cosas que sorprenden y hacen perder tiempo:

  • No hay jerarquía. write_invoices no habilita nada que pida read_invoices. La verificación es por coincidencia exacta, así que una clave que crea facturas y después las consulta necesita los dos permisos marcados.
  • La clave pertenece al contribuyente, no al usuario. Los permisos del usuario que la creó no influyen, y la clave sigue funcionando aunque ese usuario cambie de permisos o se dé de baja.

Cuando falta un permiso la respuesta es 403 con código FORBIDDEN, y el SDK la convierte en :class:smartdoc.errors.PermissionError.

Permission

Bases: Catalog

Permiso de una API Key, tal como se marca en el panel de SmartDoc.

smartdoc.constants.countries

Códigos de país (clientCountryCode).

Son 249 códigos ISO-3, así que van como datos y no como enum: nadie quiere recorrer 249 miembros en el autocompletado. Lo que hace falta casi siempre es :data:PARAGUAY; para el resto están :func:code y :func:name.

El código se valida contra el catálogo, porque la API lo acepta como texto libre y un código inventado recién falla al enviarse a la DNIT.

name

name(country_code: str) -> str

Nombre en español del país con ese código ISO-3.

code

code(country_name: str) -> str

Código ISO-3 del país con ese nombre, ignorando mayúsculas y tildes.

is_valid

is_valid(country_code: str) -> bool

Si el código existe en el catálogo.

search

search(
    term: str, *, limit: int = 20
) -> list[tuple[str, str]]

Países cuyo nombre contiene term. Devuelve pares (código, nombre).

all_countries

all_countries() -> dict[str, str]

Todo el catálogo, como {código: nombre}.

validate

validate(country_code: str | None) -> str | None

Normaliza a mayúsculas y valida. None pasa sin tocar.

Errores

smartdoc.errors

Excepciones del SDK.

Este módulo no importa nada del resto del paquete, así que puede usarse desde cualquier lado sin ciclos.

La jerarquía separa dos orígenes:

  • Errores que devolvió SmartDoc (APIError y sus subclases), que traen el code y el message del cuerpo de la respuesta.
  • Errores que detecta el SDK antes de salir a la red (ValidationError, ConfigurationError, SignatureError), que existen justamente para no gastar una llamada en algo que ya se sabe que va a fallar.

SmartDocError

Bases: Exception

Base de todos los errores del SDK.

APIError

Bases: SmartDocError

Error devuelto por SmartDoc.

Atributos

code: el campo code del cuerpo (por ejemplo NOT_FOUND). message: el campo message. status_code: el código HTTP. raw: el cuerpo completo, por si trae algo que el SDK no modela.

AuthenticationError

Bases: APIError

401 — la API Key falta, es inválida o expiró.

PermissionError

Bases: APIError

403 — la API Key es válida pero no tiene el permiso necesario.

Los permisos de SmartDoc no son jerárquicos ni se heredan: write_invoices no habilita nada que pida read_invoices. Si esto salta, hay que marcar el permiso que falta en la API Key desde el panel.

NotFoundError

Bases: APIError

404 — el recurso no existe o no pertenece a este contribuyente.

ValidationError

Bases: APIError

422 de la API, o una validación que el SDK hizo localmente.

Cuando viene de la API, field_errors mapea campo → mensaje. Cuando la detecta el SDK, status_code es None y el mensaje explica cómo corregirlo.

RateLimitError

Bases: APIError

429 — se superó el límite de 5 solicitudes por segundo.

SmartDoc no envía Retry-After, así que el SDK reintenta con backoff exponencial propio. Si igual llega hasta acá, es que se agotaron los reintentos.

ConflictError

Bases: APIError

409, o una acción no permitida para el estado actual del documento.

ServerError

Bases: APIError

5xx — error interno de SmartDoc.

ConfigurationError

Bases: SmartDocError

El SDK no pudo resolver la configuración necesaria para emitir.

El caso típico es que el contribuyente tenga más de un establecimiento o punto de expedición y no se haya indicado cuál usar. El SDK nunca elige uno en silencio, porque el elegido queda impreso en el documento.

SignatureError

Bases: SmartDocError

La firma de un webhook no validó.

Las dos causas habituales son verificar contra el JSON reserializado en vez del cuerpo crudo, y usar el secreto de otro endpoint.

TimeoutError

Bases: SmartDocError

Se agotó la espera de wait_until_final sin llegar a un estado final.

smartdoc.constants.api_errors

Códigos de error que devuelve la API v2 en el campo code.

El SDK los traduce a las excepciones de :mod:smartdoc.errors, así que rara vez hace falta compararlos a mano. Están acá para los casos en que sí: distinguir un ACTION_NOT_ALLOWED de un UNKNOWN_ACTION, por ejemplo, cuando ambos llegan como :class:smartdoc.errors.ConflictError.

El catálogo es abierto: SmartDoc puede agregar códigos y el SDK no debería romperse por eso.

ErrorCode

Bases: Catalog

Código de error de la API v2.