# Actualizar Emisor Source: https://docs.mipos.co.cr/api-reference/cliente/actualizar-emisor Actualizar datos de un emisor registrado Actualiza los datos de un emisor previamente registrado. La estructura del body es idéntica a la de [Registrar Emisor](/api-reference/cliente/registrar-emisor). ## Request ``` POST https://{empresa}.mipos.co.cr/api/actualizar/emisor ``` ### Campos Todos los campos son los mismos que en [Registrar Emisor](/api-reference/cliente/registrar-emisor): Número de identificación del emisor a actualizar. Tipo de identificación: `1` Física, `2` Jurídica, `3` DIMEX, `4` NITE. Código de actividad económica. Número de sucursal. Nombre comercial o razón social. Número de teléfono. Correo electrónico. Dirección física. Ver [Registrar Emisor](/api-reference/cliente/registrar-emisor) para la estructura. Ambiente: `1` Producción, `2` Pruebas. Usuario de acceso a comprobantes electrónicos. Clave de acceso (base64). PIN del certificado digital. Certificado `.p12` en base64. ```bash cURL theme={null} curl -X POST https://{empresa}.mipos.co.cr/api/actualizar/emisor \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {token}" \ -d '{ "identificacion": 102340568, "tipoIdentificacion": 1, "codigoActividad": 4772.0, "sucursal": 1, "nombreComercial": "MI EMPRESA S.A.", "telefono": "88180000", "correoElectronico": "nuevo@ejemplo.com", "ubicacion": { "provincia": "1", "canton": "06", "distrito": "01", "barrio": "04", "direccion": "Nueva dirección" }, "ambiente": 1, "usuarioHacienda": "cpf-01-0234-0567@stag.comprobanteselectronicos.go.cr", "claveHacienda": "BASE64_ENCODED_KEY", "pinCertificado": "1234", "certificadoHacienda": "BASE64_ENCODED_P12_CERTIFICATE" }' ``` ```php PHP theme={null} $ch = curl_init('https://{empresa}.mipos.co.cr/api/actualizar/emisor'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token, ], CURLOPT_POSTFIELDS => json_encode([ 'identificacion' => 102340568, 'tipoIdentificacion' => 1, 'codigoActividad' => 4772.0, 'sucursal' => 1, 'nombreComercial' => 'MI EMPRESA S.A.', 'telefono' => '88180000', 'correoElectronico' => 'nuevo@ejemplo.com', 'ubicacion' => [ 'provincia' => '1', 'canton' => '06', 'distrito' => '01', 'barrio' => '04', 'direccion' => 'Nueva dirección', ], 'ambiente' => 1, 'usuarioHacienda' => 'cpf-01-0234-0567@stag.comprobanteselectronicos.go.cr', 'claveHacienda' => 'BASE64_ENCODED_KEY', 'pinCertificado' => '1234', 'certificadoHacienda' => $certificado, ]), ]); $response = curl_exec($ch); curl_close($ch); ``` ```javascript Node.js theme={null} const response = await fetch('https://{empresa}.mipos.co.cr/api/actualizar/emisor', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, body: JSON.stringify({ identificacion: 102340568, tipoIdentificacion: 1, codigoActividad: 4772.0, sucursal: 1, nombreComercial: 'MI EMPRESA S.A.', telefono: '88180000', correoElectronico: 'nuevo@ejemplo.com', ubicacion: { provincia: '1', canton: '06', distrito: '01', barrio: '04', direccion: 'Nueva dirección', }, ambiente: 1, usuarioHacienda: 'cpf-01-0234-0567@stag.comprobanteselectronicos.go.cr', claveHacienda: 'BASE64_ENCODED_KEY', pinCertificado: '1234', certificadoHacienda: certificado, }), }); const data = await response.json(); ``` ```python Python theme={null} import requests response = requests.post( 'https://{empresa}.mipos.co.cr/api/actualizar/emisor', headers={'Authorization': f'Bearer {token}'}, json={ 'identificacion': 102340568, 'tipoIdentificacion': 1, 'codigoActividad': 4772.0, 'sucursal': 1, 'nombreComercial': 'MI EMPRESA S.A.', 'telefono': '88180000', 'correoElectronico': 'nuevo@ejemplo.com', 'ubicacion': { 'provincia': '1', 'canton': '06', 'distrito': '01', 'barrio': '04', 'direccion': 'Nueva dirección', }, 'ambiente': 1, 'usuarioHacienda': 'cpf-01-0234-0567@stag.comprobanteselectronicos.go.cr', 'claveHacienda': 'BASE64_ENCODED_KEY', 'pinCertificado': '1234', 'certificadoHacienda': certificado, }, ) data = response.json() ``` ```json 200 — Emisor actualizado theme={null} { "status": "success", "message": "Emisor actualizado correctamente" } ``` ```json 401 — No autorizado theme={null} { "error": "unauthorized", "message": "Token inválido o expirado" } ``` # Consultar CABYS Source: https://docs.mipos.co.cr/api-reference/cliente/consultar-cabys Buscar códigos CABYS por nombre de producto o servicio Busca códigos CABYS (Catálogo de Bienes y Servicios) del Ministerio de Hacienda por nombre. Útil para obtener el código correcto al crear líneas de detalle en documentos electrónicos. ## Request ``` POST https://{empresa}.mipos.co.cr/api/hacienda/cabys ``` Nombre o término de búsqueda del producto o servicio (ej: `"AGUA"`, `"INTERNET"`). ```bash cURL theme={null} curl -X POST https://{empresa}.mipos.co.cr/api/hacienda/cabys \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {token}" \ -d '{ "name": "AGUA" }' ``` ```php PHP theme={null} $ch = curl_init('https://{empresa}.mipos.co.cr/api/hacienda/cabys'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token, ], CURLOPT_POSTFIELDS => json_encode([ 'name' => 'AGUA', ]), ]); $response = curl_exec($ch); curl_close($ch); $result = json_decode($response, true); ``` ```javascript Node.js theme={null} const response = await fetch('https://{empresa}.mipos.co.cr/api/hacienda/cabys', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, body: JSON.stringify({ name: 'AGUA' }), }); const data = await response.json(); ``` ```python Python theme={null} import requests response = requests.post( 'https://{empresa}.mipos.co.cr/api/hacienda/cabys', headers={'Authorization': f'Bearer {token}'}, json={'name': 'AGUA'}, ) data = response.json() ``` ```json 200 — Resultados encontrados theme={null} { "status": "success", "data": [ { "codigo": "0111001000000", "descripcion": "Agua mineral natural" }, { "codigo": "0111002000000", "descripcion": "Agua purificada" } ] } ``` ```json 401 — No autorizado theme={null} { "error": "unauthorized", "message": "Token inválido o expirado" } ``` # Consultar Emisor Source: https://docs.mipos.co.cr/api-reference/cliente/consultar-emisor Consultar información de un emisor registrado Consulta los datos de un emisor previamente registrado en el sistema. ## Request ``` POST https://{empresa}.mipos.co.cr/api/consultar/emisor ``` Número de identificación del emisor a consultar. ```bash cURL theme={null} curl -X POST https://{empresa}.mipos.co.cr/api/consultar/emisor \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {token}" \ -d '{ "identificacion": 3102955154 }' ``` ```php PHP theme={null} $ch = curl_init('https://{empresa}.mipos.co.cr/api/consultar/emisor'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token, ], CURLOPT_POSTFIELDS => json_encode([ 'identificacion' => 3102955154, ]), ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $result = json_decode($response, true); ``` ```javascript Node.js theme={null} const response = await fetch('https://{empresa}.mipos.co.cr/api/consultar/emisor', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, body: JSON.stringify({ identificacion: 3102955154, }), }); const data = await response.json(); ``` ```python Python theme={null} import requests response = requests.post( 'https://{empresa}.mipos.co.cr/api/consultar/emisor', headers={'Authorization': f'Bearer {token}'}, json={'identificacion': 3102955154}, ) data = response.json() ``` ```json 200 — Emisor encontrado theme={null} { "status": "success", "data": { "identificacion": 3102955154, "tipoIdentificacion": 2, "nombreComercial": "MI EMPRESA S.A.", "correoElectronico": "empresa@ejemplo.com", "telefono": "88180000", "ambiente": 1 } } ``` ```json 404 — Emisor no encontrado theme={null} { "error": "not_found", "message": "Emisor no encontrado" } ``` # Consultar Exoneración Source: https://docs.mipos.co.cr/api-reference/cliente/consultar-exoneracion Validar un documento de exoneración en Hacienda Consulta y valida un documento de exoneración en el Ministerio de Hacienda. Útil para verificar que un número de exoneración es válido antes de incluirlo en una factura. ## Request ``` POST https://{empresa}.mipos.co.cr/api/hacienda/exoneracion ``` Número del documento de exoneración a consultar (ej: `"AL-00080875-23"`). ```bash cURL theme={null} curl -X POST https://{empresa}.mipos.co.cr/api/hacienda/exoneracion \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {token}" \ -d '{ "exoneracion": "AL-00080875-23" }' ``` ```php PHP theme={null} $ch = curl_init('https://{empresa}.mipos.co.cr/api/hacienda/exoneracion'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token, ], CURLOPT_POSTFIELDS => json_encode([ 'exoneracion' => 'AL-00080875-23', ]), ]); $response = curl_exec($ch); curl_close($ch); $result = json_decode($response, true); ``` ```javascript Node.js theme={null} const response = await fetch('https://{empresa}.mipos.co.cr/api/hacienda/exoneracion', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, body: JSON.stringify({ exoneracion: 'AL-00080875-23' }), }); const data = await response.json(); ``` ```python Python theme={null} import requests response = requests.post( 'https://{empresa}.mipos.co.cr/api/hacienda/exoneracion', headers={'Authorization': f'Bearer {token}'}, json={'exoneracion': 'AL-00080875-23'}, ) data = response.json() ``` ```json 200 — Exoneración válida theme={null} { "status": "success", "data": { "numeroDocumento": "AL-00080875-23", "estado": "vigente", "porcentajeExoneracion": 13, "fechaVencimiento": "2025-12-31T00:00:00" } } ``` ```json 404 — No encontrada theme={null} { "error": "not_found", "message": "Documento de exoneración no encontrado" } ``` # Consultar Identificación Source: https://docs.mipos.co.cr/api-reference/cliente/consultar-identificacion Consultar datos de un contribuyente por número de identificación Consulta los datos de un contribuyente en el Ministerio de Hacienda a partir de su número de identificación (cédula física, jurídica, DIMEX o NITE). ## Request ``` POST https://{empresa}.mipos.co.cr/api/hacienda/identificacion ``` Número de identificación del contribuyente a consultar. ```bash cURL theme={null} curl -X POST https://{empresa}.mipos.co.cr/api/hacienda/identificacion \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {token}" \ -d '{ "identificacion": "208370410" }' ``` ```php PHP theme={null} $ch = curl_init('https://{empresa}.mipos.co.cr/api/hacienda/identificacion'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token, ], CURLOPT_POSTFIELDS => json_encode([ 'identificacion' => '208370410', ]), ]); $response = curl_exec($ch); curl_close($ch); $result = json_decode($response, true); ``` ```javascript Node.js theme={null} const response = await fetch('https://{empresa}.mipos.co.cr/api/hacienda/identificacion', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, body: JSON.stringify({ identificacion: '208370410' }), }); const data = await response.json(); ``` ```python Python theme={null} import requests response = requests.post( 'https://{empresa}.mipos.co.cr/api/hacienda/identificacion', headers={'Authorization': f'Bearer {token}'}, json={'identificacion': '208370410'}, ) data = response.json() ``` ```json 200 — Contribuyente encontrado theme={null} { "status": "success", "data": { "nombre": "JUAN PEREZ GARCIA", "tipoIdentificacion": "01", "identificacion": "208370410" } } ``` ```json 404 — No encontrado theme={null} { "error": "not_found", "message": "Identificación no encontrada" } ``` # Descargar Factura Source: https://docs.mipos.co.cr/api-reference/cliente/descargar-archivo Descargar el archivo de una factura por clave de Hacienda Descarga el archivo XML o PDF de un comprobante electrónico previamente enviado, utilizando la clave numérica de Hacienda de 50 dígitos. ## Request ``` POST https://{empresa}.mipos.co.cr/api/consultar/archivo ``` Clave numérica de 50 dígitos asignada por Hacienda al comprobante electrónico. ```bash cURL theme={null} curl -X POST https://{empresa}.mipos.co.cr/api/consultar/archivo \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {token}" \ -d '{ "claveHacienda": "50628032600310295515400200070010000000001146404019" }' ``` ```php PHP theme={null} $ch = curl_init('https://{empresa}.mipos.co.cr/api/consultar/archivo'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token, ], CURLOPT_POSTFIELDS => json_encode([ 'claveHacienda' => '50628032600310295515400200070010000000001146404019', ]), ]); $response = curl_exec($ch); curl_close($ch); $result = json_decode($response, true); ``` ```javascript Node.js theme={null} const response = await fetch('https://{empresa}.mipos.co.cr/api/consultar/archivo', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, body: JSON.stringify({ claveHacienda: '50628032600310295515400200070010000000001146404019', }), }); const data = await response.json(); ``` ```python Python theme={null} import requests response = requests.post( 'https://{empresa}.mipos.co.cr/api/consultar/archivo', headers={'Authorization': f'Bearer {token}'}, json={ 'claveHacienda': '50628032600310295515400200070010000000001146404019', }, ) data = response.json() ``` ```json 200 — Archivo encontrado theme={null} { "status": "success", "data": { "claveHacienda": "50628032600310295515400200070010000000001146404019", "fileType": "XML", "encodedFile": "PD94bWwgdmVyc2lvbj0iMS4wIi..." } } ``` ```json 404 — No encontrado theme={null} { "error": "not_found", "message": "Archivo no encontrado para la clave proporcionada" } ``` # Factura con Exoneración Source: https://docs.mipos.co.cr/api-reference/cliente/enviar-exoneracion Emitir una factura electrónica con exoneración de impuestos Envía una factura electrónica (tipoDoc `"1"`) con exoneración de impuestos. La exoneración se aplica dentro del objeto `impuesto` de cada línea del detalle de servicio. Existen dos modalidades de exoneración: **autorizaciones** (emitidas por entidades autorizadas) y **gobierno** (instituciones públicas con artículo e inciso de ley). ## Request ``` POST https://{empresa}.mipos.co.cr/api/enviar/items ``` ### Campos principales Los campos principales son idénticos a la [Factura Electrónica](/api-reference/cliente/enviar-factura): `emisor`, `numeroReferencia`, `codigoActividad`, `tipoDoc` (`"1"`), `receptor`, `folioType`, `fechaEmision`, `resumenFactura`, `otrosCargos`. ### Exoneración en el impuesto La diferencia está dentro de cada elemento del array `impuesto` en `detalleServicio`, que incluye un objeto `exoneracion`: Datos del documento de exoneración. Tipo de documento de exoneración: * `"01"` — Compras autorizadas * `"02"` — Ventas exentas a diplomáticos * `"03"` — Autorizado por ley especial * `"04"` — Exoneraciones DGTH * `"05"` — Zonas Francas * `"99"` — Otros Número del documento de exoneración (ej: `"AL-00080875-23"`). Código o nombre de la institución que emite la exoneración. * `"01"` — Instituciones del Estado * `"04"` — DGTH (Dirección General de Tributación y Hacienda) Fecha de emisión del documento de exoneración en formato ISO 8601. Porcentaje de exoneración (ej: `13` para exoneración total del IVA). Nombre completo de la institución (requerido para tipo `"01"` gobierno). Artículo de ley que fundamenta la exoneración (para instituciones gobierno). Inciso del artículo de ley (para instituciones gobierno). ## Ejemplo: Exoneración estándar (DGTH) ```bash cURL theme={null} curl -X POST https://{empresa}.mipos.co.cr/api/enviar/items \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {token}" \ -d '{ "emisor": "3102955154", "numeroReferencia": 18, "codigoActividad": "6201.0", "tipoDoc": "1", "receptor": { "nombre": "JUAN PEREZ GARCIA", "telefono": "50684444", "identificacion": { "tipo": "01", "numero": "504300485" }, "correoElectronico": "cliente@ejemplo.com" }, "folioType": "INVOICE", "fechaEmision": "2024-08-23T10:30:00-06:00", "resumenFactura": { "codigoTipoMoneda": { "codigoMoneda": "CRC" }, "mediosPago": [ { "tipoMedioPago":"01", "totalMedioPago":"20000.00" }, { "tipoMedioPago":"06", "totalMedioPago":"5160.90" } ], "totalOtrosCargos": "387.09" }, "detalleServicio": [ { "codigo": "8421000000000", "detalle": "INTERNET RESIDENCIAL 200 MBPS", "cantidad": 1.0, "impuesto": [ { "monto": "2875.53", "codigo": "01", "tarifa": "13", "codigoTarifa": "08", "exoneracion": { "numeroDocumento": "AL-00080875-23", "fechaEmision": "2023-10-11T00:00:00", "nombreInstitucion": "04", "porcentajeExoneracion": 13, "tipoDocumento": "04" } } ], "subTotal": "22119.47", "descuento": "0.00", "montoTotal": "22119.47", "numeroLinea": 1, "impuestoNeto": 2875.53, "unidadMedida": "Os", "precioUnitario": "22119.47", "codigoComercial": [ { "tipo": "04", "codigo": "I_INT" } ], "montoTotalLinea": 24995.0, "unidadMedidaComercial": "Os" } ] }' ``` ```php PHP theme={null} $ch = curl_init('https://{empresa}.mipos.co.cr/api/enviar/items'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token, ], CURLOPT_POSTFIELDS => json_encode([ 'emisor' => '3102955154', 'numeroReferencia' => 18, 'codigoActividad' => '6201.0', 'tipoDoc' => '1', 'receptor' => [ 'nombre' => 'JUAN PEREZ GARCIA', 'telefono' => '50684444', 'identificacion' => ['tipo' => '01', 'numero' => '504300485'], 'correoElectronico' => 'cliente@ejemplo.com', ], 'folioType' => 'INVOICE', 'fechaEmision' => date('c'), 'resumenFactura' => [ 'codigoTipoMoneda' => ['codigoMoneda' => 'CRC'], 'totalOtrosCargos' => '387.09', ], 'detalleServicio' => [ [ 'codigo' => '8421000000000', 'detalle' => 'INTERNET RESIDENCIAL 200 MBPS', 'cantidad' => 1.0, 'precioUnitario' => '22119.47', 'subTotal' => '22119.47', 'montoTotal' => '22119.47', 'descuento' => '0.00', 'numeroLinea' => 1, 'unidadMedida' => 'Os', 'impuestoNeto' => 2875.53, 'montoTotalLinea' => 24995.0, 'impuesto' => [ [ 'codigo' => '01', 'codigoTarifa' => '08', 'tarifa' => '13', 'monto' => '2875.53', 'exoneracion' => [ 'numeroDocumento' => 'AL-00080875-23', 'fechaEmision' => '2023-10-11T00:00:00', 'nombreInstitucion' => '04', 'porcentajeExoneracion' => 13, 'tipoDocumento' => '04', ], ], ], ], ], ]), ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); ``` ```javascript Node.js theme={null} const response = await fetch("https://{empresa}.mipos.co.cr/api/enviar/items", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ emisor: "3102955154", numeroReferencia: 18, codigoActividad: "6201.0", tipoDoc: "1", receptor: { nombre: "JUAN PEREZ GARCIA", telefono: "50684444", identificacion: { tipo: "01", numero: "504300485" }, correoElectronico: "cliente@ejemplo.com", }, folioType: "INVOICE", fechaEmision: new Date().toISOString(), resumenFactura: { codigoTipoMoneda: { codigoMoneda: "CRC" }, totalOtrosCargos: "387.09", }, detalleServicio: [ { codigo: "8421000000000", detalle: "INTERNET RESIDENCIAL 200 MBPS", cantidad: 1.0, precioUnitario: "22119.47", subTotal: "22119.47", montoTotal: "22119.47", descuento: "0.00", numeroLinea: 1, unidadMedida: "Os", impuestoNeto: 2875.53, montoTotalLinea: 24995.0, impuesto: [ { codigo: "01", codigoTarifa: "08", tarifa: "13", monto: "2875.53", exoneracion: { numeroDocumento: "AL-00080875-23", fechaEmision: "2023-10-11T00:00:00", nombreInstitucion: "04", porcentajeExoneracion: 13, tipoDocumento: "04", }, }, ], }, ], }), }); const data = await response.json(); ``` ```python Python theme={null} import requests from datetime import datetime response = requests.post( 'https://{empresa}.mipos.co.cr/api/enviar/items', headers={'Authorization': f'Bearer {token}'}, json={ 'emisor': '3102955154', 'numeroReferencia': 18, 'codigoActividad': '6201.0', 'tipoDoc': '1', 'receptor': { 'nombre': 'JUAN PEREZ GARCIA', 'telefono': '50684444', 'identificacion': {'tipo': '01', 'numero': '504300485'}, 'correoElectronico': 'cliente@ejemplo.com', }, 'folioType': 'INVOICE', 'fechaEmision': datetime.now().isoformat(), 'resumenFactura': { 'codigoTipoMoneda': {'codigoMoneda': 'CRC'}, 'totalOtrosCargos': '387.09', }, 'detalleServicio': [ { 'codigo': '8421000000000', 'detalle': 'INTERNET RESIDENCIAL 200 MBPS', 'cantidad': 1.0, 'precioUnitario': '22119.47', 'subTotal': '22119.47', 'montoTotal': '22119.47', 'descuento': '0.00', 'numeroLinea': 1, 'unidadMedida': 'Os', 'impuestoNeto': 2875.53, 'montoTotalLinea': 24995.0, 'impuesto': [ { 'codigo': '01', 'codigoTarifa': '08', 'tarifa': '13', 'monto': '2875.53', 'exoneracion': { 'numeroDocumento': 'AL-00080875-23', 'fechaEmision': '2023-10-11T00:00:00', 'nombreInstitucion': '04', 'porcentajeExoneracion': 13, 'tipoDocumento': '04', }, }, ], }, ], }, ) data = response.json() ``` ## Ejemplo: Exoneración gobierno (con artículo e inciso) Para instituciones del Estado, se agregan los campos `nombreInstitucionOtros`, `articulo` e `inciso`: ```json theme={null} "exoneracion": { "numeroDocumento": "7293", "fechaEmision": "1992-04-03T00:00:00", "nombreInstitucion": "01", "nombreInstitucionOtros": "CAJA COSTARRICENSE DEL SEGURO SOCIAL", "porcentajeExoneracion": 13, "tipoDocumento": "03", "articulo": "2", "inciso": 1 } ``` ```json 200 — Documento enviado theme={null} { "status": "success", "message": "Documento enviado correctamente", "clave": "50628032600310295515400100001010000000018146404019" } ``` ```json 401 — No autorizado theme={null} { "error": "unauthorized", "message": "Token inválido o expirado" } ``` # Factura Electrónica Source: https://docs.mipos.co.cr/api-reference/cliente/enviar-factura Emitir una factura electrónica con receptor identificado Envía una factura electrónica (tipoDoc `"1"`) al Ministerio de Hacienda a través del API. Requiere un receptor con identificación válida (cédula física, jurídica, DIMEX o NITE). ## Request ``` POST https://{empresa}.mipos.co.cr/api/enviar/items ``` ### Campos principales Número de identificación del emisor registrado. Número consecutivo de referencia del documento. Código de actividad económica del emisor (ej: `"6201.0"`). Tipo de documento. Para factura electrónica: `"1"`. Tipo de folio. Siempre `"INVOICE"`. Fecha y hora de emisión en formato ISO 8601. ### Receptor Datos del receptor del documento. Nombre completo o razón social del receptor. Número de teléfono del receptor. Tipo de identificación: `"01"` Física, `"02"` Jurídica, `"03"` DIMEX, `"04"` NITE. Número de identificación. Correo electrónico del receptor. ### Detalle del servicio Líneas de detalle del documento. Número consecutivo de la línea (inicia en 1). Código CABYS del producto o servicio. Descripción del producto o servicio. Cantidad del producto o servicio. Unidad de medida (ej: `"Unid"`, `"Os"`, `"Sp"`). Unidad de medida comercial. Precio unitario del producto. Subtotal de la línea (cantidad x precio unitario). Monto de descuento aplicado. `"0.00"` si no hay descuento. Monto total de la línea antes de impuestos. Monto neto de impuesto de la línea. Monto total de la línea incluyendo impuestos. Códigos comerciales del producto. Tipo de código comercial (ej: `"04"`). Código del producto (ej: `"I_INT"`). Impuestos aplicados a la línea. Código de impuesto. `"01"` = IVA. Código de tarifa. `"08"` = tarifa general 13%. Porcentaje de la tarifa (ej: `"13"`). Monto del impuesto calculado. ### Otros cargos (opcional) Cargos adicionales al documento. Tipo de documento del cargo (ej: `"99"` para otros). Descripción del cargo (ej: `"911"`, `"CR"`). Monto del cargo. Porcentaje del cargo. Nombre del tercero asociado al cargo. ### Resumen de factura Resumen totales del documento. Código de moneda ISO 4217 (ej: `"CRC"`, `"USD"`). Código de medio de pago. * `"01"` Efectivo * `"02"` Tarjeta * `"03"` Cheque * `"04"` Transferencia o deposito * `"05"` Recudado por terceros * `"06"` SINPE MOVIL * `"07"` Plataforma Digital * `"99"` Otros (se debe indicar el medio de pago en la representacion gráfica PDF) Total asignado a este medio de pago. Total de otros cargos. ```bash cURL theme={null} curl -X POST https://{empresa}.mipos.co.cr/api/enviar/items \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {token}" \ -d '{ "emisor": "3102955154", "numeroReferencia": 10, "codigoActividad": "6201.0", "tipoDoc": "1", "receptor": { "nombre": "JUAN PEREZ GARCIA", "telefono": "50688881234", "identificacion": { "tipo": "01", "numero": "504300485" }, "correoElectronico": "cliente@ejemplo.com" }, "folioType": "INVOICE", "otrosCargos": [ { "detalle": "911", "montoCargo": "165.90", "porcentaje": "0.75", "nombreTercero": "911", "tipoDocumento": "99" } ], "fechaEmision": "2024-08-23T10:30:00-06:00", "resumenFactura": { "codigoTipoMoneda": { "codigoMoneda": "CRC" }, "mediosPago": [ { "tipoMedioPago":"01", "totalMedioPago":"20000.00" }, { "tipoMedioPago":"06", "totalMedioPago":"5160.90" } ], "totalOtrosCargos": "165.90" }, "detalleServicio": [ { "codigo": "8421000000000", "detalle": "INTERNET RESIDENCIAL 200 MBPS", "cantidad": 1.0, "impuesto": [ { "monto": "2875.53", "codigo": "01", "tarifa": "13", "codigoTarifa": "08" } ], "subTotal": "22119.47", "descuento": "0.00", "montoTotal": "22119.47", "numeroLinea": 1, "impuestoNeto": 2875.53, "unidadMedida": "Os", "precioUnitario": "22119.47", "codigoComercial": [ { "tipo": "04", "codigo": "I_INT" } ], "montoTotalLinea": 24995.0, "unidadMedidaComercial": "Os" } ] }' ``` ```php PHP theme={null} $ch = curl_init('https://{empresa}.mipos.co.cr/api/enviar/items'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token, ], CURLOPT_POSTFIELDS => json_encode([ 'emisor' => '3102955154', 'numeroReferencia' => 10, 'codigoActividad' => '6201.0', 'tipoDoc' => '1', 'receptor' => [ 'nombre' => 'JUAN PEREZ GARCIA', 'telefono' => '50688881234', 'identificacion' => [ 'tipo' => '01', 'numero' => '504300485', ], 'correoElectronico' => 'cliente@ejemplo.com', ], 'folioType' => 'INVOICE', 'fechaEmision' => date('c'), 'resumenFactura' => [ 'codigoTipoMoneda' => ['codigoMoneda' => 'CRC'], ], 'detalleServicio' => [ [ 'codigo' => '8421000000000', 'detalle' => 'INTERNET RESIDENCIAL 200 MBPS', 'cantidad' => 1.0, 'precioUnitario' => '22119.47', 'subTotal' => '22119.47', 'montoTotal' => '22119.47', 'descuento' => '0.00', 'numeroLinea' => 1, 'unidadMedida' => 'Os', 'impuestoNeto' => 2875.53, 'montoTotalLinea' => 24995.0, 'impuesto' => [ [ 'codigo' => '01', 'codigoTarifa'=> '08', 'tarifa' => '13', 'monto' => '2875.53', ], ], ], ], ]), ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $result = json_decode($response, true); ``` ```javascript Node.js theme={null} const response = await fetch("https://{empresa}.mipos.co.cr/api/enviar/items", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ emisor: "3102955154", numeroReferencia: 10, codigoActividad: "6201.0", tipoDoc: "1", receptor: { nombre: "JUAN PEREZ GARCIA", telefono: "50688881234", identificacion: { tipo: "01", numero: "504300485" }, correoElectronico: "cliente@ejemplo.com", }, folioType: "INVOICE", fechaEmision: new Date().toISOString(), resumenFactura: { codigoTipoMoneda: { codigoMoneda: "CRC" }, }, detalleServicio: [ { codigo: "8421000000000", detalle: "INTERNET RESIDENCIAL 200 MBPS", cantidad: 1.0, precioUnitario: "22119.47", subTotal: "22119.47", montoTotal: "22119.47", descuento: "0.00", numeroLinea: 1, unidadMedida: "Os", impuestoNeto: 2875.53, montoTotalLinea: 24995.0, impuesto: [ { codigo: "01", codigoTarifa: "08", tarifa: "13", monto: "2875.53" }, ], }, ], }), }); const data = await response.json(); ``` ```python Python theme={null} import requests from datetime import datetime response = requests.post( 'https://{empresa}.mipos.co.cr/api/enviar/items', headers={'Authorization': f'Bearer {token}'}, json={ 'emisor': '3102955154', 'numeroReferencia': 10, 'codigoActividad': '6201.0', 'tipoDoc': '1', 'receptor': { 'nombre': 'JUAN PEREZ GARCIA', 'telefono': '50688881234', 'identificacion': {'tipo': '01', 'numero': '504300485'}, 'correoElectronico': 'cliente@ejemplo.com', }, 'folioType': 'INVOICE', 'fechaEmision': datetime.now().isoformat(), 'resumenFactura': { 'codigoTipoMoneda': {'codigoMoneda': 'CRC'}, }, 'detalleServicio': [ { 'codigo': '8421000000000', 'detalle': 'INTERNET RESIDENCIAL 200 MBPS', 'cantidad': 1.0, 'precioUnitario': '22119.47', 'subTotal': '22119.47', 'montoTotal': '22119.47', 'descuento': '0.00', 'numeroLinea': 1, 'unidadMedida': 'Os', 'impuestoNeto': 2875.53, 'montoTotalLinea': 24995.0, 'impuesto': [ {'codigo': '01', 'codigoTarifa': '08', 'tarifa': '13', 'monto': '2875.53'}, ], }, ], }, ) data = response.json() ``` ```json 200 — Documento enviado theme={null} { "status": "success", "message": "Documento enviado correctamente", "clave": "50628032600310295515400100001010000000010146404019" } ``` ```json 400 — Error de validación theme={null} { "error": "bad_request", "message": "Campos requeridos faltantes" } ``` ```json 401 — No autorizado theme={null} { "error": "unauthorized", "message": "Token inválido o expirado" } ``` # Nota de Crédito Source: https://docs.mipos.co.cr/api-reference/cliente/enviar-nota-credito Emitir una nota de crédito electrónica Envía una nota de crédito electrónica (tipoDoc `"3"`) al Ministerio de Hacienda. Se usa para anular o ajustar parcialmente una factura emitida previamente. La nota de crédito requiere un receptor identificado, igual que la factura electrónica. ## Request ``` POST https://{empresa}.mipos.co.cr/api/enviar/items ``` ### Campos principales Número de identificación del emisor registrado. Número consecutivo de referencia del documento. Código de actividad económica del emisor. Tipo de documento. Para nota de crédito: `"3"`. Tipo de folio. Siempre `"INVOICE"`. Fecha y hora de emisión en formato ISO 8601. ### Receptor Datos del receptor. Ver [Factura Electrónica](/api-reference/cliente/enviar-factura) para la estructura completa del receptor. Nombre completo o razón social del receptor. Número de teléfono. Tipo de identificación: `"01"` Física, `"02"` Jurídica, `"03"` DIMEX, `"04"` NITE. Número de identificación. Correo electrónico del receptor. Líneas de detalle del documento. Ver [Factura Electrónica](/api-reference/cliente/enviar-factura) para la estructura completa. Cargos adicionales. Ver [Factura Electrónica](/api-reference/cliente/enviar-factura) para la estructura. Resumen con moneda y totales. Ver [Factura Electrónica](/api-reference/cliente/enviar-factura) para la estructura. ```bash cURL theme={null} curl -X POST https://{empresa}.mipos.co.cr/api/enviar/items \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {token}" \ -d '{ "emisor": "3102955154", "numeroReferencia": 10, "codigoActividad": "6110.0", "tipoDoc": "3", "receptor": { "nombre": "JUAN PEREZ GARCIA", "telefono": "50684444", "identificacion": { "tipo": "01", "numero": "504300485" }, "correoElectronico": "cliente@ejemplo.com" }, "folioType": "INVOICE", "fechaEmision": "2024-08-23T10:30:00-06:00", "resumenFactura": { "codigoTipoMoneda": { "codigoMoneda": "CRC" }, "mediosPago": [ { "tipoMedioPago":"01", "totalMedioPago":"20000.00" }, { "tipoMedioPago":"06", "totalMedioPago":"5160.90" } ], "totalOtrosCargos": "387.09" }, "otrosCargos": [ { "detalle": "911", "montoCargo": "165.90", "porcentaje": "0.75", "nombreTercero": "911", "tipoDocumento": "99" }, { "detalle": "CR", "montoCargo": "221.19", "porcentaje": "1.00", "nombreTercero": "CR", "tipoDocumento": "99" } ], "detalleServicio": [ { "codigo": "8421000000000", "detalle": "INTERNET RESIDENCIAL 200 MBPS", "cantidad": 1.0, "impuesto": [ { "monto": "2875.53", "codigo": "01", "tarifa": "13", "codigoTarifa": "08" } ], "subTotal": "22119.47", "descuento": "0.00", "montoTotal": "22119.47", "numeroLinea": 1, "impuestoNeto": 2875.53, "unidadMedida": "Os", "precioUnitario": "22119.47", "codigoComercial": [ { "tipo": "04", "codigo": "I_INT" } ], "montoTotalLinea": 24995.0, "unidadMedidaComercial": "Os" } ] }' ``` ```php PHP theme={null} $ch = curl_init('https://{empresa}.mipos.co.cr/api/enviar/items'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token, ], CURLOPT_POSTFIELDS => json_encode([ 'emisor' => '3102955154', 'numeroReferencia' => 10, 'codigoActividad' => '6110.0', 'tipoDoc' => '3', 'receptor' => [ 'nombre' => 'JUAN PEREZ GARCIA', 'telefono' => '50684444', 'identificacion' => ['tipo' => '01', 'numero' => '504300485'], 'correoElectronico' => 'cliente@ejemplo.com', ], 'folioType' => 'INVOICE', 'fechaEmision' => date('c'), 'resumenFactura' => [ 'codigoTipoMoneda' => ['codigoMoneda' => 'CRC'], 'totalOtrosCargos' => '387.09', ], 'detalleServicio' => [ [ 'codigo' => '8421000000000', 'detalle' => 'INTERNET RESIDENCIAL 200 MBPS', 'cantidad' => 1.0, 'precioUnitario' => '22119.47', 'subTotal' => '22119.47', 'montoTotal' => '22119.47', 'descuento' => '0.00', 'numeroLinea' => 1, 'unidadMedida' => 'Os', 'impuestoNeto' => 2875.53, 'montoTotalLinea' => 24995.0, 'impuesto' => [ ['codigo' => '01', 'codigoTarifa' => '08', 'tarifa' => '13', 'monto' => '2875.53'], ], ], ], ]), ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); ``` ```javascript Node.js theme={null} const response = await fetch("https://{empresa}.mipos.co.cr/api/enviar/items", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ emisor: "3102955154", numeroReferencia: 10, codigoActividad: "6110.0", tipoDoc: "3", receptor: { nombre: "JUAN PEREZ GARCIA", telefono: "50684444", identificacion: { tipo: "01", numero: "504300485" }, correoElectronico: "cliente@ejemplo.com", }, folioType: "INVOICE", fechaEmision: new Date().toISOString(), resumenFactura: { codigoTipoMoneda: { codigoMoneda: "CRC" }, totalOtrosCargos: "387.09", }, detalleServicio: [ { codigo: "8421000000000", detalle: "INTERNET RESIDENCIAL 200 MBPS", cantidad: 1.0, precioUnitario: "22119.47", subTotal: "22119.47", montoTotal: "22119.47", descuento: "0.00", numeroLinea: 1, unidadMedida: "Os", impuestoNeto: 2875.53, montoTotalLinea: 24995.0, impuesto: [ { codigo: "01", codigoTarifa: "08", tarifa: "13", monto: "2875.53" }, ], }, ], }), }); const data = await response.json(); ``` ```python Python theme={null} import requests from datetime import datetime response = requests.post( 'https://{empresa}.mipos.co.cr/api/enviar/items', headers={'Authorization': f'Bearer {token}'}, json={ 'emisor': '3102955154', 'numeroReferencia': 10, 'codigoActividad': '6110.0', 'tipoDoc': '3', 'receptor': { 'nombre': 'JUAN PEREZ GARCIA', 'telefono': '50684444', 'identificacion': {'tipo': '01', 'numero': '504300485'}, 'correoElectronico': 'cliente@ejemplo.com', }, 'folioType': 'INVOICE', 'fechaEmision': datetime.now().isoformat(), 'resumenFactura': { 'codigoTipoMoneda': {'codigoMoneda': 'CRC'}, 'totalOtrosCargos': '387.09', }, 'detalleServicio': [ { 'codigo': '8421000000000', 'detalle': 'INTERNET RESIDENCIAL 200 MBPS', 'cantidad': 1.0, 'precioUnitario': '22119.47', 'subTotal': '22119.47', 'montoTotal': '22119.47', 'descuento': '0.00', 'numeroLinea': 1, 'unidadMedida': 'Os', 'impuestoNeto': 2875.53, 'montoTotalLinea': 24995.0, 'impuesto': [ {'codigo': '01', 'codigoTarifa': '08', 'tarifa': '13', 'monto': '2875.53'}, ], }, ], }, ) data = response.json() ``` ```json 200 — Documento enviado theme={null} { "status": "success", "message": "Documento enviado correctamente", "clave": "50628032600310295515400300001010000000010146404019" } ``` ```json 401 — No autorizado theme={null} { "error": "unauthorized", "message": "Token inválido o expirado" } ``` # Tiquete Electrónico Source: https://docs.mipos.co.cr/api-reference/cliente/enviar-tiquete Emitir un tiquete electrónico sin receptor identificado Envía un tiquete electrónico (tipoDoc `"4"`) al Ministerio de Hacienda. Se usa para ventas al consumidor final sin necesidad de identificar al receptor. El campo `receptor` debe enviarse como string vacío `""` para tiquetes sin receptor. ## Request ``` POST https://{empresa}.mipos.co.cr/api/enviar/items ``` ### Campos principales Número de identificación del emisor registrado. Número consecutivo de referencia del documento. Código de actividad económica del emisor. Tipo de documento. Para tiquete electrónico: `"4"`. String vacío `""` para tiquetes sin receptor. Tipo de folio. Siempre `"INVOICE"`. Fecha y hora de emisión en formato ISO 8601. Líneas de detalle del documento. Ver [Factura Electrónica](/api-reference/cliente/enviar-factura) para la estructura completa. Cargos adicionales. Ver [Factura Electrónica](/api-reference/cliente/enviar-factura) para la estructura. Resumen con moneda y totales. Ver [Factura Electrónica](/api-reference/cliente/enviar-factura) para la estructura. ```bash cURL theme={null} curl -X POST https://{empresa}.mipos.co.cr/api/enviar/items \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {token}" \ -d '{ "emisor": "3102955154", "numeroReferencia": 22, "codigoActividad": "6201.0", "tipoDoc": "4", "receptor": "", "folioType": "INVOICE", "fechaEmision": "2024-08-23T10:30:00-06:00", "resumenFactura": { "codigoTipoMoneda": { "codigoMoneda": "CRC" }, "mediosPago": [ { "tipoMedioPago":"01", "totalMedioPago":"20000.00" }, { "tipoMedioPago":"06", "totalMedioPago":"5160.90" } ], "totalOtrosCargos": "387.09" }, "otrosCargos": [ { "detalle": "911", "montoCargo": "165.90", "porcentaje": "0.75", "nombreTercero": "911", "tipoDocumento": "99" }, { "detalle": "CR", "montoCargo": "221.19", "porcentaje": "1.00", "nombreTercero": "CR", "tipoDocumento": "99" } ], "detalleServicio": [ { "codigo": "8421000000000", "detalle": "INTERNET RESIDENCIAL 200 MBPS", "cantidad": 1.0, "impuesto": [ { "monto": "2875.53", "codigo": "01", "tarifa": "13", "codigoTarifa": "08" } ], "subTotal": "22119.47", "descuento": "0.00", "montoTotal": "22119.47", "numeroLinea": 1, "impuestoNeto": 2875.53, "unidadMedida": "Os", "precioUnitario": "22119.47", "codigoComercial": [ { "tipo": "04", "codigo": "I_INT" } ], "montoTotalLinea": 24995.0, "unidadMedidaComercial": "Os" } ] }' ``` ```php PHP theme={null} $ch = curl_init('https://{empresa}.mipos.co.cr/api/enviar/items'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token, ], CURLOPT_POSTFIELDS => json_encode([ 'emisor' => '3102955154', 'numeroReferencia' => 22, 'codigoActividad' => '6201.0', 'tipoDoc' => '4', 'receptor' => '', 'folioType' => 'INVOICE', 'fechaEmision' => date('c'), 'resumenFactura' => [ 'codigoTipoMoneda' => ['codigoMoneda' => 'CRC'], 'totalOtrosCargos' => '387.09', ], 'detalleServicio' => [ [ 'codigo' => '8421000000000', 'detalle' => 'INTERNET RESIDENCIAL 200 MBPS', 'cantidad' => 1.0, 'precioUnitario' => '22119.47', 'subTotal' => '22119.47', 'montoTotal' => '22119.47', 'descuento' => '0.00', 'numeroLinea' => 1, 'unidadMedida' => 'Os', 'impuestoNeto' => 2875.53, 'montoTotalLinea' => 24995.0, 'impuesto' => [ ['codigo' => '01', 'codigoTarifa' => '08', 'tarifa' => '13', 'monto' => '2875.53'], ], ], ], ]), ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); ``` ```javascript Node.js theme={null} const response = await fetch("https://{empresa}.mipos.co.cr/api/enviar/items", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ emisor: "3102955154", numeroReferencia: 22, codigoActividad: "6201.0", tipoDoc: "4", receptor: "", folioType: "INVOICE", fechaEmision: new Date().toISOString(), resumenFactura: { codigoTipoMoneda: { codigoMoneda: "CRC" }, totalOtrosCargos: "387.09", }, detalleServicio: [ { codigo: "8421000000000", detalle: "INTERNET RESIDENCIAL 200 MBPS", cantidad: 1.0, precioUnitario: "22119.47", subTotal: "22119.47", montoTotal: "22119.47", descuento: "0.00", numeroLinea: 1, unidadMedida: "Os", impuestoNeto: 2875.53, montoTotalLinea: 24995.0, impuesto: [ { codigo: "01", codigoTarifa: "08", tarifa: "13", monto: "2875.53" }, ], }, ], }), }); const data = await response.json(); ``` ```python Python theme={null} import requests from datetime import datetime response = requests.post( 'https://{empresa}.mipos.co.cr/api/enviar/items', headers={'Authorization': f'Bearer {token}'}, json={ 'emisor': '3102955154', 'numeroReferencia': 22, 'codigoActividad': '6201.0', 'tipoDoc': '4', 'receptor': '', 'folioType': 'INVOICE', 'fechaEmision': datetime.now().isoformat(), 'resumenFactura': { 'codigoTipoMoneda': {'codigoMoneda': 'CRC'}, 'totalOtrosCargos': '387.09', }, 'detalleServicio': [ { 'codigo': '8421000000000', 'detalle': 'INTERNET RESIDENCIAL 200 MBPS', 'cantidad': 1.0, 'precioUnitario': '22119.47', 'subTotal': '22119.47', 'montoTotal': '22119.47', 'descuento': '0.00', 'numeroLinea': 1, 'unidadMedida': 'Os', 'impuestoNeto': 2875.53, 'montoTotalLinea': 24995.0, 'impuesto': [ {'codigo': '01', 'codigoTarifa': '08', 'tarifa': '13', 'monto': '2875.53'}, ], }, ], }, ) data = response.json() ``` ```json 200 — Documento enviado theme={null} { "status": "success", "message": "Documento enviado correctamente", "clave": "50628032600310295515400400001010000000022146404019" } ``` ```json 401 — No autorizado theme={null} { "error": "unauthorized", "message": "Token inválido o expirado" } ``` # Tiquete Extranjero Source: https://docs.mipos.co.cr/api-reference/cliente/enviar-tiquete-extranjero Emitir un tiquete electrónico para receptor con identificación extranjera Envía un tiquete electrónico (tipoDoc `"4"`) para un receptor con identificación extranjera (tipo `"05"`). La diferencia con el tiquete regular es que este incluye un receptor con tipo de identificación `"05"` (extranjero), que acepta números de pasaporte u otros documentos internacionales. ## Request ``` POST https://{empresa}.mipos.co.cr/api/enviar/items ``` ### Campos principales Número de identificación del emisor registrado. Número consecutivo de referencia del documento. Código de actividad económica del emisor. Tipo de documento. Para tiquete: `"4"`. Tipo de folio. Siempre `"INVOICE"`. Fecha y hora de emisión en formato ISO 8601. ### Receptor extranjero Datos del receptor extranjero. Nombre completo del receptor. Número de teléfono. Tipo de identificación. Para extranjeros: `"05"`. Número de pasaporte o documento extranjero. Correo electrónico del receptor. Líneas de detalle del documento. Ver [Factura Electrónica](/api-reference/cliente/enviar-factura) para la estructura completa. Cargos adicionales. Ver [Factura Electrónica](/api-reference/cliente/enviar-factura) para la estructura. Resumen con moneda y totales. Ver [Factura Electrónica](/api-reference/cliente/enviar-factura) para la estructura. ```bash cURL theme={null} curl -X POST https://{empresa}.mipos.co.cr/api/enviar/items \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {token}" \ -d '{ "emisor": "3102955154", "numeroReferencia": 20, "codigoActividad": "6201.0", "tipoDoc": "4", "receptor": { "nombre": "JOHN DOE SMITH", "telefono": "50684444", "identificacion": { "tipo": "05", "numero": "BC9872774" }, "correoElectronico": "john@example.com" }, "folioType": "INVOICE", "fechaEmision": "2024-08-23T10:30:00-06:00", "resumenFactura": { "codigoTipoMoneda": { "codigoMoneda": "CRC" }, "mediosPago": [ { "tipoMedioPago":"01", "totalMedioPago":"20000.00" }, { "tipoMedioPago":"06", "totalMedioPago":"5160.90" } ], "totalOtrosCargos": "387.09" }, "otrosCargos": [ { "detalle": "911", "montoCargo": "165.90", "porcentaje": "0.75", "nombreTercero": "911", "tipoDocumento": "99" }, { "detalle": "CR", "montoCargo": "221.19", "porcentaje": "1.00", "nombreTercero": "CR", "tipoDocumento": "99" } ], "detalleServicio": [ { "codigo": "8421000000000", "detalle": "INTERNET RESIDENCIAL 200 MBPS", "cantidad": 1.0, "impuesto": [ { "monto": "2875.53", "codigo": "01", "tarifa": "13", "codigoTarifa": "08" } ], "subTotal": "22119.47", "descuento": "0.00", "montoTotal": "22119.47", "numeroLinea": 1, "impuestoNeto": 2875.53, "unidadMedida": "Os", "precioUnitario": "22119.47", "codigoComercial": [ { "tipo": "04", "codigo": "I_INT" } ], "montoTotalLinea": 24995.0, "unidadMedidaComercial": "Os" } ] }' ``` ```php PHP theme={null} $ch = curl_init('https://{empresa}.mipos.co.cr/api/enviar/items'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token, ], CURLOPT_POSTFIELDS => json_encode([ 'emisor' => '3102955154', 'numeroReferencia' => 20, 'codigoActividad' => '6201.0', 'tipoDoc' => '4', 'receptor' => [ 'nombre' => 'JOHN DOE SMITH', 'telefono' => '50684444', 'identificacion' => ['tipo' => '05', 'numero' => 'BC9872774'], 'correoElectronico' => 'john@example.com', ], 'folioType' => 'INVOICE', 'fechaEmision' => date('c'), 'resumenFactura' => [ 'codigoTipoMoneda' => ['codigoMoneda' => 'CRC'], 'totalOtrosCargos' => '387.09', ], 'detalleServicio' => [ [ 'codigo' => '8421000000000', 'detalle' => 'INTERNET RESIDENCIAL 200 MBPS', 'cantidad' => 1.0, 'precioUnitario' => '22119.47', 'subTotal' => '22119.47', 'montoTotal' => '22119.47', 'descuento' => '0.00', 'numeroLinea' => 1, 'unidadMedida' => 'Os', 'impuestoNeto' => 2875.53, 'montoTotalLinea' => 24995.0, 'impuesto' => [ ['codigo' => '01', 'codigoTarifa' => '08', 'tarifa' => '13', 'monto' => '2875.53'], ], ], ], ]), ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); ``` ```javascript Node.js theme={null} const response = await fetch("https://{empresa}.mipos.co.cr/api/enviar/items", { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ emisor: "3102955154", numeroReferencia: 20, codigoActividad: "6201.0", tipoDoc: "4", receptor: { nombre: "JOHN DOE SMITH", telefono: "50684444", identificacion: { tipo: "05", numero: "BC9872774" }, correoElectronico: "john@example.com", }, folioType: "INVOICE", fechaEmision: new Date().toISOString(), resumenFactura: { codigoTipoMoneda: { codigoMoneda: "CRC" }, totalOtrosCargos: "387.09", }, detalleServicio: [ { codigo: "8421000000000", detalle: "INTERNET RESIDENCIAL 200 MBPS", cantidad: 1.0, precioUnitario: "22119.47", subTotal: "22119.47", montoTotal: "22119.47", descuento: "0.00", numeroLinea: 1, unidadMedida: "Os", impuestoNeto: 2875.53, montoTotalLinea: 24995.0, impuesto: [ { codigo: "01", codigoTarifa: "08", tarifa: "13", monto: "2875.53" }, ], }, ], }), }); const data = await response.json(); ``` ```python Python theme={null} import requests from datetime import datetime response = requests.post( 'https://{empresa}.mipos.co.cr/api/enviar/items', headers={'Authorization': f'Bearer {token}'}, json={ 'emisor': '3102955154', 'numeroReferencia': 20, 'codigoActividad': '6201.0', 'tipoDoc': '4', 'receptor': { 'nombre': 'JOHN DOE SMITH', 'telefono': '50684444', 'identificacion': {'tipo': '05', 'numero': 'BC9872774'}, 'correoElectronico': 'john@example.com', }, 'folioType': 'INVOICE', 'fechaEmision': datetime.now().isoformat(), 'resumenFactura': { 'codigoTipoMoneda': {'codigoMoneda': 'CRC'}, 'totalOtrosCargos': '387.09', }, 'detalleServicio': [ { 'codigo': '8421000000000', 'detalle': 'INTERNET RESIDENCIAL 200 MBPS', 'cantidad': 1.0, 'precioUnitario': '22119.47', 'subTotal': '22119.47', 'montoTotal': '22119.47', 'descuento': '0.00', 'numeroLinea': 1, 'unidadMedida': 'Os', 'impuestoNeto': 2875.53, 'montoTotalLinea': 24995.0, 'impuesto': [ {'codigo': '01', 'codigoTarifa': '08', 'tarifa': '13', 'monto': '2875.53'}, ], }, ], }, ) data = response.json() ``` ```json 200 — Documento enviado theme={null} { "status": "success", "message": "Documento enviado correctamente", "clave": "50628032600310295515400400001010000000020146404019" } ``` ```json 401 — No autorizado theme={null} { "error": "unauthorized", "message": "Token inválido o expirado" } ``` # API Cliente Source: https://docs.mipos.co.cr/api-reference/cliente/overview Endpoints para facturación electrónica, gestión de emisores y consultas a Hacienda ## Base URL Cada cliente tiene su propia URL de API. Reemplaza `{empresa}` con tu identificador: ``` https://{empresa}.mipos.co.cr ``` Todos los endpoints requieren autenticación. Incluye el header `Authorization: Bearer ` obtenido desde el [Auth Server](/api-reference/login). ## Headers requeridos | Header | Valor | Requerido | | --------------- | ------------------ | --------- | | `Content-Type` | `application/json` | Siempre | | `Authorization` | `Bearer ` | Siempre | ## Documentos Electrónicos Emitir factura electrónica con receptor identificado. Emitir tiquete sin receptor (venta al público). Emitir tiquete para receptor con identificación extranjera. Emitir nota de crédito electrónica. Emitir factura con exoneración de impuestos. ## Gestión de Emisor Registrar un nuevo emisor con su certificado de Hacienda. Actualizar datos de un emisor existente. Consultar información de un emisor registrado. ## Consultas a Hacienda Buscar códigos CABYS por nombre de producto/servicio. Consultar datos de un contribuyente por cédula. Validar un documento de exoneración. ## Archivos y Webhooks Descargar el archivo de una factura por clave de Hacienda. Respuesta XML que Hacienda envía a tu webhook. Callback con el estado del comprobante electrónico. ## Tipos de documento | Código | Tipo | Descripción | | ------ | ------------------- | ----------------------------------------------- | | `"1"` | Factura Electrónica | Documento tributario con receptor identificado | | `"3"` | Nota de Crédito | Documento para anular o ajustar una factura | | `"4"` | Tiquete Electrónico | Documento simplificado sin receptor obligatorio | # Registrar Emisor Source: https://docs.mipos.co.cr/api-reference/cliente/registrar-emisor Registrar un nuevo emisor con su certificado de Hacienda Registra un nuevo emisor en el sistema con sus credenciales del Ministerio de Hacienda y certificado digital (archivo `.p12` codificado en base64). El campo `certificadoHacienda` debe ser el archivo `.p12` del emisor codificado en base64. Este certificado es emitido por el BCCR (Banco Central de Costa Rica). ## Request ``` POST https://{empresa}.mipos.co.cr/api/enviar/emisor ``` ### Datos del emisor Número de identificación del emisor (cédula física o jurídica). Tipo de identificación: * `1` — Cédula Física * `2` — Cédula Jurídica * `3` — DIMEX * `4` — NITE Código de actividad económica registrado en Hacienda (ej: `4772.0`). Número de sucursal. Usar `1` para la sede principal. Nombre comercial o razón social del emisor. Número de teléfono del emisor. Correo electrónico del emisor. ### Ubicación Dirección física del emisor. Código de provincia (1-7). Código de cantón (2 dígitos). Código de distrito (2 dígitos). Código de barrio (2 dígitos). Dirección detallada en texto libre. ### Credenciales de Hacienda Ambiente de Hacienda: * `1` — Producción * `2` — Pruebas (Sandbox) Usuario de acceso al sistema de comprobantes electrónicos de Hacienda. Clave de acceso al sistema de comprobantes electrónicos (codificada en base64). PIN del certificado digital `.p12`. Certificado digital `.p12` codificado en base64. ```bash cURL theme={null} curl -X POST https://{empresa}.mipos.co.cr/api/enviar/emisor \ -H "Content-Type: application/json" \ -H "Authorization: Bearer {token}" \ -d '{ "identificacion": 102340568, "tipoIdentificacion": 1, "codigoActividad": 4772.0, "sucursal": 1, "nombreComercial": "MI EMPRESA S.A.", "telefono": "88180000", "correoElectronico": "empresa@ejemplo.com", "ubicacion": { "provincia": "1", "canton": "06", "distrito": "01", "barrio": "04", "direccion": "350 Oeste del Puente Cañas" }, "ambiente": 1, "usuarioHacienda": "cpf-01-0234-0567@stag.comprobanteselectronicos.go.cr", "claveHacienda": "BASE64_ENCODED_KEY", "pinCertificado": "1234", "certificadoHacienda": "BASE64_ENCODED_P12_CERTIFICATE" }' ``` ```php PHP theme={null} // Leer el certificado .p12 y codificar en base64 $certificado = base64_encode(file_get_contents('/ruta/certificado.p12')); $ch = curl_init('https://{empresa}.mipos.co.cr/api/enviar/emisor'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Content-Type: application/json', 'Authorization: Bearer ' . $token, ], CURLOPT_POSTFIELDS => json_encode([ 'identificacion' => 102340568, 'tipoIdentificacion' => 1, 'codigoActividad' => 4772.0, 'sucursal' => 1, 'nombreComercial' => 'MI EMPRESA S.A.', 'telefono' => '88180000', 'correoElectronico' => 'empresa@ejemplo.com', 'ubicacion' => [ 'provincia' => '1', 'canton' => '06', 'distrito' => '01', 'barrio' => '04', 'direccion' => '350 Oeste del Puente Cañas', ], 'ambiente' => 1, 'usuarioHacienda' => 'cpf-01-0234-0567@stag.comprobanteselectronicos.go.cr', 'claveHacienda' => 'BASE64_ENCODED_KEY', 'pinCertificado' => '1234', 'certificadoHacienda' => $certificado, ]), ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); ``` ```javascript Node.js theme={null} import { readFileSync } from 'fs'; // Leer el certificado .p12 y codificar en base64 const certificado = readFileSync('/ruta/certificado.p12').toString('base64'); const response = await fetch('https://{empresa}.mipos.co.cr/api/enviar/emisor', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}`, }, body: JSON.stringify({ identificacion: 102340568, tipoIdentificacion: 1, codigoActividad: 4772.0, sucursal: 1, nombreComercial: 'MI EMPRESA S.A.', telefono: '88180000', correoElectronico: 'empresa@ejemplo.com', ubicacion: { provincia: '1', canton: '06', distrito: '01', barrio: '04', direccion: '350 Oeste del Puente Cañas', }, ambiente: 1, usuarioHacienda: 'cpf-01-0234-0567@stag.comprobanteselectronicos.go.cr', claveHacienda: 'BASE64_ENCODED_KEY', pinCertificado: '1234', certificadoHacienda: certificado, }), }); const data = await response.json(); ``` ```python Python theme={null} import requests import base64 # Leer el certificado .p12 y codificar en base64 with open('/ruta/certificado.p12', 'rb') as f: certificado = base64.b64encode(f.read()).decode() response = requests.post( 'https://{empresa}.mipos.co.cr/api/enviar/emisor', headers={'Authorization': f'Bearer {token}'}, json={ 'identificacion': 102340568, 'tipoIdentificacion': 1, 'codigoActividad': 4772.0, 'sucursal': 1, 'nombreComercial': 'MI EMPRESA S.A.', 'telefono': '88180000', 'correoElectronico': 'empresa@ejemplo.com', 'ubicacion': { 'provincia': '1', 'canton': '06', 'distrito': '01', 'barrio': '04', 'direccion': '350 Oeste del Puente Cañas', }, 'ambiente': 1, 'usuarioHacienda': 'cpf-01-0234-0567@stag.comprobanteselectronicos.go.cr', 'claveHacienda': 'BASE64_ENCODED_KEY', 'pinCertificado': '1234', 'certificadoHacienda': certificado, }, ) data = response.json() ``` ```json 200 — Emisor registrado theme={null} { "status": "success", "message": "Emisor registrado correctamente" } ``` ```json 400 — Error de validación theme={null} { "error": "bad_request", "message": "Campos requeridos faltantes" } ``` ```json 401 — No autorizado theme={null} { "error": "unauthorized", "message": "Token inválido o expirado" } ``` # Callback Source: https://docs.mipos.co.cr/api-reference/cliente/webhooks/callback Callback con el estado del comprobante electrónico El sistema envía un callback HTTP POST a tu endpoint configurado cuando Hacienda devuelve el estado de un comprobante electrónico. Este callback incluye la clave del documento, la fecha, el estado y la respuesta XML. Configura tu URL de callback al registrar el emisor. El callback se envía automáticamente cuando Hacienda responde. ## Payload recibido ``` POST https://{tu-callback-url}/callback.php ``` Clave numérica de 50 dígitos del comprobante electrónico. Fecha y hora del procesamiento en formato ISO 8601 con zona horaria (ej: `"2024-10-01T15:51:22-06:00"`). Estado del comprobante: * `"aceptado"` — Comprobante aceptado por Hacienda * `"rechazado"` — Comprobante rechazado por Hacienda * `"aceptado parcialmente"` — Aceptado con observaciones Respuesta XML de Hacienda codificada en base64. Contiene el `MensajeHacienda` firmado. ## Ejemplo del payload ```json theme={null} { "clave": "50601102400300404520200200070010000042681104691472", "fecha": "2024-10-01T15:51:22-06:00", "ind-estado": "aceptado", "respuesta-xml": "PD94bWwgdmVyc2lvbj0iMS4wIi..." } ``` ## Procesar el callback ```php PHP theme={null} $payload = json_decode(file_get_contents('php://input'), true); $clave = $payload['clave']; $estado = $payload['ind-estado']; $fecha = $payload['fecha']; $xml = base64_decode($payload['respuesta-xml']); // Procesar según estado switch ($estado) { case 'aceptado': // Marcar factura como aceptada actualizarEstado($clave, 'aceptada'); break; case 'rechazado': // Notificar rechazo y revisar errores $doc = simplexml_load_string($xml); $detalle = (string) $doc->DetalleMensaje; notificarRechazo($clave, $detalle); break; } http_response_code(200); ``` ```javascript Node.js theme={null} app.post('/callback', (req, res) => { const { clave, fecha, 'ind-estado': estado, 'respuesta-xml': xmlBase64 } = req.body; const xml = Buffer.from(xmlBase64, 'base64').toString('utf-8'); switch (estado) { case 'aceptado': // Marcar factura como aceptada console.log(`Factura ${clave} aceptada el ${fecha}`); break; case 'rechazado': // Notificar rechazo console.log(`Factura ${clave} rechazada:`, xml); break; } res.sendStatus(200); }); ``` ```python Python theme={null} from flask import Flask, request import base64 app = Flask(__name__) @app.route('/callback', methods=['POST']) def callback(): payload = request.json clave = payload['clave'] estado = payload['ind-estado'] fecha = payload['fecha'] xml = base64.b64decode(payload['respuesta-xml']).decode('utf-8') if estado == 'aceptado': # Marcar factura como aceptada print(f'Factura {clave} aceptada el {fecha}') elif estado == 'rechazado': # Notificar rechazo print(f'Factura {clave} rechazada: {xml}') return '', 200 ``` # XML Return (Webhook) Source: https://docs.mipos.co.cr/api-reference/cliente/webhooks/xml-return Respuesta XML de Hacienda enviada a tu servidor Cuando Hacienda procesa un comprobante electrónico, envía la respuesta XML a la URL de webhook configurada en tu sistema. Este payload es enviado por Hacienda, no por el API de MiPOS. Tu servidor debe estar preparado para recibir este POST en la URL de webhook que configuraste. La respuesta contiene el XML firmado por Hacienda codificado en base64. ## Payload recibido ``` POST https://{tu-webhook-url} ``` Identificador del folio del documento procesado. Tipo de folio. Generalmente `"INVOICE"`. Array con los archivos de respuesta. Tipo de archivo. Generalmente `"XML"`. Contenido del archivo XML codificado en base64. Al decodificarlo se obtiene el `MensajeHacienda` con el estado del comprobante. ## Ejemplo del payload ```json theme={null} { "folioId": "051744", "folioType": "INVOICE", "folioDetails": [ { "fileType": "XML", "encodedFile": "PD94bWwgdmVyc2lvbj0iMS4wIi..." } ] } ``` ## Decodificar la respuesta XML El campo `encodedFile` contiene un XML `MensajeHacienda` codificado en base64. Al decodificarlo incluye: | Campo | Descripción | | -------------------- | -------------------------------------------------------------------- | | `Clave` | Clave numérica de 50 dígitos del comprobante | | `NombreEmisor` | Nombre del emisor | | `NombreReceptor` | Nombre del receptor | | `Mensaje` | Estado: `1` = Aceptado, `2` = Aceptado parcialmente, `3` = Rechazado | | `DetalleMensaje` | Detalle del resultado del procesamiento | | `MontoTotalImpuesto` | Total de impuestos | | `TotalFactura` | Total de la factura | ```php PHP theme={null} $payload = json_decode(file_get_contents('php://input'), true); foreach ($payload['folioDetails'] as $detail) { $xml = base64_decode($detail['encodedFile']); $doc = simplexml_load_string($xml); $clave = (string) $doc->Clave; $estado = (string) $doc->Mensaje; // 1=aceptado, 3=rechazado $detalle = (string) $doc->DetalleMensaje; } ``` ```javascript Node.js theme={null} app.post('/webhook', (req, res) => { const { folioId, folioDetails } = req.body; for (const detail of folioDetails) { const xml = Buffer.from(detail.encodedFile, 'base64').toString('utf-8'); // Parsear XML para obtener estado console.log(`Folio ${folioId}:`, xml); } res.sendStatus(200); }); ``` ```python Python theme={null} from flask import Flask, request import base64 app = Flask(__name__) @app.route('/webhook', methods=['POST']) def webhook(): payload = request.json for detail in payload['folioDetails']: xml = base64.b64decode(detail['encodedFile']).decode('utf-8') # Parsear XML para obtener estado print(f"Folio {payload['folioId']}: {xml}") return '', 200 ``` # Errores Source: https://docs.mipos.co.cr/api-reference/errors Referencia completa de códigos de error del Auth Server Todas las respuestas de error siguen la misma estructura: ```json theme={null} { "error": "codigo_de_error", "message": "Descripción legible del error" } ``` ## Códigos de error **Causa:** Falta el campo `base` o `password` en el body del request. ```json theme={null} { "error": "bad_request", "message": "Los campos base y password son requeridos" } ``` **Solución:** Asegúrate de enviar ambos campos en el body JSON. **Causa:** El cliente no existe o la contraseña es incorrecta. ```json theme={null} { "error": "invalid_credentials", "message": "Base o contraseña incorrectos" } ``` **Nota:** Por seguridad, el mensaje es idéntico en ambos casos para no revelar si un cliente existe. **Causa:** La ruta solicitada no existe. ```json theme={null} { "error": "not_found", "message": "Endpoint no existe" } ``` **Solución:** Verifica que la URL sea `/login` o `/.well-known/jwks.json`. **Causa:** Se usó un método HTTP incorrecto. ```json theme={null} { "error": "method_not_allowed", "message": "Método no permitido" } ``` **Solución:** * `/login` solo acepta `POST` * `/.well-known/jwks.json` solo acepta `GET` **Causa:** Error interno del servidor. ```json theme={null} { "error": "server_error", "message": "Error interno" } ``` **Nota:** Los detalles del error nunca se exponen en la respuesta por seguridad. Revisar los logs del servidor. ## Manejo de errores recomendado ```php PHP theme={null} $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); $result = json_decode($response, true); match ($httpCode) { 200 => procesarToken($result['token']), 400 => throw new \InvalidArgumentException($result['message']), 401 => throw new \RuntimeException('Credenciales inválidas'), default => throw new \RuntimeException("Error inesperado: {$httpCode}"), }; ``` ```javascript Node.js theme={null} const res = await fetch('https://auth.mipos.co.cr/login', { ... }); const data = await res.json(); if (!res.ok) { switch (res.status) { case 400: throw new Error(`Solicitud inválida: ${data.message}`); case 401: throw new Error('Credenciales inválidas'); default: throw new Error(`Error ${res.status}: ${data.message}`); } } ``` ```python Python theme={null} response = requests.post('https://auth.mipos.co.cr/login', json=payload) if response.status_code != 200: error = response.json() raise Exception(f"{error['error']}: {error['message']}") ``` # GET /.well-known/jwks.json Source: https://docs.mipos.co.cr/api-reference/jwks GET https://auth.mipos.co.cr/.well-known/jwks.json Obtener la clave pública RSA en formato JWKS Retorna la clave pública del Auth Server en formato estándar [JSON Web Key Set (RFC 7517)](https://datatracker.ietf.org/doc/html/rfc7517). Las APIs cliente pueden usar este endpoint para verificar la firma de los JWT sin necesidad de una copia local de `public.pem`. La respuesta incluye el header `Cache-Control: public, max-age=3600`. Puedes cachear la respuesta por hasta 1 hora. ## Response Array de claves públicas activas. Tipo de clave. Siempre `"RSA"`. Uso de la clave. Siempre `"sig"` (firma). Algoritmo. Siempre `"RS256"`. Key ID. Identifica qué clave se usó para firmar. Coincide con el header `kid` del JWT. Módulo RSA codificado en base64url. Exponente RSA codificado en base64url. Normalmente `"AQAB"`. ```bash cURL theme={null} curl -s https://auth.mipos.co.cr/.well-known/jwks.json | python3 -m json.tool ``` ```php PHP theme={null} $jwks = json_decode( file_get_contents('https://auth.mipos.co.cr/.well-known/jwks.json'), true ); // $jwks['keys'][0]['n'] = módulo RSA // $jwks['keys'][0]['kid'] = key ID ``` ```javascript Node.js theme={null} const res = await fetch('https://auth.mipos.co.cr/.well-known/jwks.json'); const jwks = await res.json(); // jwks.keys[0].kid = key ID activo ``` ```python Python theme={null} import requests jwks = requests.get('https://auth.mipos.co.cr/.well-known/jwks.json').json() # jwks['keys'][0]['kid'] = key ID activo ``` ```json 200 — JWKS theme={null} { "keys": [ { "kty": "RSA", "use": "sig", "alg": "RS256", "kid": "key-2024-01", "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw", "e": "AQAB" } ] } ``` # POST /login Source: https://docs.mipos.co.cr/api-reference/login POST https://auth.mipos.co.cr/login Autenticar un cliente y obtener un JWT Valida las credenciales del cliente contra la base de datos y emite un JWT firmado con RS256. Por seguridad, el mensaje de error es idéntico tanto si el cliente no existe como si la contraseña es incorrecta. ## Request Identificador único del cliente en la base de datos. Contraseña del cliente. ## Response JWT firmado con RS256. Incluye los claims `iss`, `aud`, `iat`, `exp`, `jti`, `sub` y `base`. Tiempo de vida del token en segundos. Por defecto `28800` (8 horas). Siempre `"Bearer"`. ```bash cURL theme={null} curl -X POST https://auth.mipos.co.cr/login \ -H "Content-Type: application/json" \ -d '{ "base": "mi_empresa", "password": "mi_password_seguro" }' ``` ```php PHP theme={null} $ch = curl_init('https://auth.mipos.co.cr/login'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode([ 'base' => 'mi_empresa', 'password' => 'mi_password_seguro', ]), ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); $result = json_decode($response, true); if ($httpCode === 200) { $token = $result['token']; // Guardar $token para usarlo en requests posteriores } ``` ```javascript Node.js theme={null} const response = await fetch('https://auth.mipos.co.cr/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ base: 'mi_empresa', password: 'mi_password_seguro', }), }); if (response.ok) { const { token, expires_in } = await response.json(); // Guardar token para requests posteriores } ``` ```python Python theme={null} import requests response = requests.post('https://auth.mipos.co.cr/login', json={ 'base': 'mi_empresa', 'password': 'mi_password_seguro', }) if response.status_code == 200: data = response.json() token = data['token'] ``` ```json 200 — Éxito theme={null} { "token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6ImtleS0yMDI0LTAxIn0.eyJpc3MiOiJodHRwczovL2F1dGgubWlwb3MuY28uY3IiLCJhdWQiOiJodHRwczovL2F1dGgubWlwb3MuY28uY3IiLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MTcwMDAyODgwMCwianRpIjoiYTFiMmMzZDRlNWY2YTFiMmMzZDRlNWY2YTFiMmMzZDQiLCJzdWIiOiJtaV9lbXByZXNhIiwiYmFzZSI6Im1pX2VtcHJlc2EifQ.firma_rsa256", "expires_in": 28800, "token_type": "Bearer" } ``` ```json 400 — Campos faltantes theme={null} { "error": "bad_request", "message": "Los campos base y password son requeridos" } ``` ```json 401 — Credenciales inválidas theme={null} { "error": "invalid_credentials", "message": "Base o contraseña incorrectos" } ``` # Visión general Source: https://docs.mipos.co.cr/api-reference/overview Resumen de los endpoints disponibles en el Auth Server ## Base URL ``` https://auth.mipos.co.cr ``` ## Endpoints Autenticar con `base` y `password` para obtener un JWT firmado. Obtener la clave pública RSA en formato JWKS para verificar tokens. ## Formato de respuesta Todas las respuestas son `application/json`. **Exitosas:** ```json theme={null} { "token": "eyJhbGciOi...", "expires_in": 28800, "token_type": "Bearer" } ``` **Errores:** ```json theme={null} { "error": "codigo_de_error", "message": "Descripción legible" } ``` Ver la [referencia de errores](/api-reference/errors) para todos los códigos posibles. ## API Cliente Una vez autenticado, usa el token JWT para acceder a los endpoints de facturación electrónica: Documentos electrónicos, gestión de emisores, consultas a Hacienda y webhooks. ## Headers requeridos | Header | Valor | Requerido en | | --------------- | ------------------ | -------------------------------- | | `Content-Type` | `application/json` | `POST /login` | | `Authorization` | `Bearer ` | APIs cliente (no en Auth Server) | # Autenticación Source: https://docs.mipos.co.cr/authentication Cómo obtener y usar tokens JWT del Auth Server ## Obtener un token Envía tus credenciales al endpoint de login: ```bash cURL theme={null} curl -X POST https://auth.mipos.co.cr/login \ -H "Content-Type: application/json" \ -d '{"base":"mi_empresa","password":"mi_password"}' ``` ```php PHP theme={null} $ch = curl_init('https://auth.mipos.co.cr/login'); curl_setopt_array($ch, [ CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode([ 'base' => 'mi_empresa', 'password' => 'mi_password', ]), ]); $result = json_decode(curl_exec($ch), true); $token = $result['token']; ``` ```javascript Node.js theme={null} const res = await fetch('https://auth.mipos.co.cr/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ base: 'mi_empresa', password: 'mi_password' }), }); const { token } = await res.json(); ``` ```python Python theme={null} import requests res = requests.post('https://auth.mipos.co.cr/login', json={ 'base': 'mi_empresa', 'password': 'mi_password', }) token = res.json()['token'] ``` ## Usar el token Incluye el token en el header `Authorization` de cada request a las APIs cliente: ``` Authorization: Bearer eyJhbGciOiJSUzI1NiIs... ``` El token expira después de **8 horas** (28800 segundos). Cuando expire, debes hacer login nuevamente. No hay endpoint de refresh. ## Verificar el token (APIs cliente) Las APIs que reciben el token deben verificar su firma. Hay dos formas: Copia el archivo `public.pem` del Auth Server a tu API cliente y verifica localmente: ```php PHP theme={null} use Firebase\JWT\JWT; use Firebase\JWT\Key; $publicKey = file_get_contents('/ruta/segura/public.pem'); $token = str_replace('Bearer ', '', $_SERVER['HTTP_AUTHORIZATION'] ?? ''); try { $decoded = JWT::decode($token, new Key($publicKey, 'RS256')); $base = $decoded->base; // Identificador del cliente } catch (\Exception $e) { http_response_code(401); exit(json_encode(['error' => 'Token inválido'])); } ``` ```javascript Node.js theme={null} const jwt = require('jsonwebtoken'); const fs = require('fs'); const publicKey = fs.readFileSync('/ruta/segura/public.pem', 'utf8'); function auth(req, res, next) { const token = (req.headers.authorization || '').replace('Bearer ', ''); try { req.cliente = jwt.verify(token, publicKey, { algorithms: ['RS256'] }); next(); } catch { res.status(401).json({ error: 'Token inválido' }); } } ``` ```python Python theme={null} import jwt public_key = open('/ruta/segura/public.pem').read() try: decoded = jwt.decode(token, public_key, algorithms=['RS256']) base = decoded['base'] except jwt.InvalidTokenError: # Token inválido o expirado pass ``` Consulta el endpoint JWKS para obtener la clave pública dinámicamente. Útil cuando se rotan claves. ``` GET https://auth.mipos.co.cr/.well-known/jwks.json ``` La respuesta contiene la clave pública con su `kid`. Las librerías JWT pueden consumir el JWKS directamente para resolver la clave correcta. # Introducción Source: https://docs.mipos.co.cr/introduction API de autenticación centralizada para el ecosistema MiPOS Esta API es el único punto de autenticación. Las APIs cliente nunca acceden a la base de datos del Auth Server — solo verifican la firma del JWT. ## ¿Cómo funciona? Tu aplicación envía `base` y `password` al endpoint `/login`. El Auth Server valida las credenciales y retorna un token JWT firmado con RS256. Envía el token en el header `Authorization: Bearer ` a cualquier API del ecosistema MiPOS. La API cliente verifica la firma del JWT usando la clave pública. No necesita consultar ninguna base de datos. ## Arquitectura ``` ┌──────────────┐ POST /login ┌───────────────────┐ │ │ ──────────────────▶ │ │ │ Tu App │ │ Auth Server │ │ │ ◀────────────────── │ auth.mipos.co.cr │ └──────────────┘ { token } └───────────────────┘ │ │ │ Authorization: Bearer │ Clave privada RSA ▼ │ (nunca sale del servidor) ┌──────────────┐ │ │ API Cliente │ ◀── Clave pública ───────────┘ │ (tu API) │ (public.pem o JWKS) └──────────────┘ ``` ## Datos del token Cada JWT contiene esta información en su payload: Emisor del token — `https://auth.mipos.co.cr` Timestamp Unix de expiración. Por defecto, 8 horas después de la emisión. Identificador del cliente autenticado. Nombre del cliente en la base de datos. Mismo valor que `sub`. Usar este campo para filtrar datos en cada API. ID único del token (32 caracteres hex). Puede usarse para revocación si se implementa una lista negra. # Playground Source: https://docs.mipos.co.cr/playground/index Prueba los endpoints del Auth Server de forma interactiva Usa este playground para probar los endpoints del Auth Server directamente desde el navegador. *** ## Login Autenticarse y obtener un JWT. Identificador del cliente Contraseña del cliente ```bash Probar con cURL theme={null} curl -X POST https://auth.mipos.co.cr/login \ -H "Content-Type: application/json" \ -d '{"base":"TU_BASE","password":"TU_PASSWORD"}' ``` **Mintlify Playground integrado:** Si estás viendo esta documentación en Mintlify, usa el panel lateral derecho de las páginas [POST /login](/api-reference/login) y [GET /jwks](/api-reference/jwks) para ejecutar requests directamente. Solo llena los campos y presiona **Send**. *** ## JWKS Obtener la clave pública. No requiere autenticación. ```bash Probar con cURL theme={null} curl -s https://auth.mipos.co.cr/.well-known/jwks.json | python3 -m json.tool ``` *** ## Decodificar un JWT Una vez que tengas un token, puedes inspeccionar su contenido (sin verificar la firma): ```bash theme={null} # Reemplaza TOKEN con tu JWT echo "TOKEN" | cut -d. -f2 | base64 -d 2>/dev/null | python3 -m json.tool ``` ```javascript theme={null} function decodificarJWT(token) { const payload = token.split('.')[1]; return JSON.parse(atob(payload.replace(/-/g, '+').replace(/_/g, '/'))); } // Pegar tu token aquí const decoded = decodificarJWT('eyJhbGciOi...'); console.table(decoded); ``` ```php theme={null} function decodificarJWT(string $token): array { $parts = explode('.', $token); return json_decode(base64_decode(strtr($parts[1], '-_', '+/')), true); } print_r(decodificarJWT('eyJhbGciOi...')); ``` ```python theme={null} import base64, json def decodificar_jwt(token: str) -> dict: payload = token.split('.')[1] payload += '=' * (4 - len(payload) % 4) # padding return json.loads(base64.urlsafe_b64decode(payload)) print(decodificar_jwt('eyJhbGciOi...')) ``` ### Resultado esperado ```json theme={null} { "iss": "https://auth.mipos.co.cr", "aud": "https://auth.mipos.co.cr", "iat": 1700000000, "exp": 1700028800, "jti": "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4", "sub": "mi_empresa", "base": "mi_empresa" } ``` | Campo | Qué contiene | | ------ | --------------------------------------------- | | `iss` | URL del Auth Server que emitió el token | | `aud` | Audiencia del token | | `iat` | Cuándo se emitió (Unix timestamp) | | `exp` | Cuándo expira (Unix timestamp) | | `jti` | ID único del token — útil para revocación | | `sub` | Identificador del cliente | | `base` | Nombre del cliente en la BD (mismo que `sub`) | *** ## Flujo completo de prueba Ir a [POST /login](/api-reference/login) → llenar `base` y `password` → **Send** De la respuesta, copiar el valor del campo `token`. Usar cualquiera de los métodos anteriores para ver el payload del JWT. Enviar el token como `Authorization: Bearer ` en requests a tus APIs. **No decodifiques tokens en herramientas online de terceros en producción.** Aunque el payload no es secreto, evita exponer tokens activos en sitios como jwt.io. Usa los métodos locales de arriba. # Node.js Source: https://docs.mipos.co.cr/quickstart/nodejs Integrar el Auth Server en tu aplicación Node.js en 5 minutos ## 1. Instalar dependencia ```bash theme={null} npm install jsonwebtoken ``` ## 2. Obtener token ```javascript theme={null} async function obtenerToken(base, password) { const response = await fetch('https://auth.mipos.co.cr/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ base, password }), }); if (!response.ok) { const error = await response.json(); throw new Error(error.message || 'Error de autenticación'); } const { token, expires_in } = await response.json(); return { token, expires_in }; } // Uso const { token } = await obtenerToken('mi_empresa', 'mi_password'); ``` ## 3. Middleware de verificación (Express) ```javascript theme={null} const jwt = require('jsonwebtoken'); const fs = require('fs'); const publicKey = fs.readFileSync('/ruta/segura/public.pem', 'utf8'); function authMiddleware(req, res, next) { const authHeader = req.headers.authorization || ''; const token = authHeader.replace('Bearer ', ''); if (!token) { return res.status(401).json({ error: 'Token no proporcionado' }); } try { req.cliente = jwt.verify(token, publicKey, { algorithms: ['RS256'] }); next(); } catch (err) { res.status(401).json({ error: 'Token inválido o expirado' }); } } // Uso en Express app.get('/ventas', authMiddleware, (req, res) => { const base = req.cliente.base; // Identificador del cliente // ... lógica del endpoint }); ``` ## 4. Usar el token en requests ```javascript theme={null} const { token } = await obtenerToken('mi_empresa', 'mi_password'); const ventas = await fetch('https://api.mipos.co.cr/ventas', { headers: { 'Authorization': `Bearer ${token}` }, }).then(r => r.json()); ``` **Listo.** Tu aplicación Node.js puede autenticarse y verificar tokens del ecosistema MiPOS. # PHP Source: https://docs.mipos.co.cr/quickstart/php Integrar el Auth Server en tu aplicación PHP en 5 minutos ## 1. Instalar dependencia ```bash theme={null} composer require firebase/php-jwt ``` ## 2. Obtener token ```php theme={null} true, CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => ['Content-Type: application/json'], CURLOPT_POSTFIELDS => json_encode([ 'base' => $base, 'password' => $password, ]), ]); $response = curl_exec($ch); $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch); if ($httpCode !== 200) { $error = json_decode($response, true); throw new \RuntimeException($error['message'] ?? 'Error de autenticación'); } $result = json_decode($response, true); return $result['token']; } // Uso $token = obtenerToken('mi_empresa', 'mi_password'); ``` ## 3. Verificar token en tu API ```php theme={null} 'Token no proporcionado'])); } try { return JWT::decode($token, new Key($publicKey, 'RS256')); } catch (\Exception $e) { http_response_code(401); exit(json_encode(['error' => 'Token inválido o expirado'])); } } // En tu endpoint protegido: $cliente = verificarToken(); $base = $cliente->base; // Identificador del cliente ``` ## 4. Usar en requests a otras APIs ```php theme={null} $token = obtenerToken('mi_empresa', 'mi_password'); $ch = curl_init('https://api.mipos.co.cr/ventas'); curl_setopt_array($ch, [ CURLOPT_RETURNTRANSFER => true, CURLOPT_HTTPHEADER => [ 'Authorization: Bearer ' . $token, 'Content-Type: application/json', ], ]); $ventas = json_decode(curl_exec($ch), true); ``` **Listo.** Tu aplicación PHP puede autenticarse y verificar tokens del ecosistema MiPOS. # Python Source: https://docs.mipos.co.cr/quickstart/python Integrar el Auth Server en tu aplicación Python en 5 minutos ## 1. Instalar dependencias ```bash theme={null} pip install requests PyJWT cryptography ``` ## 2. Obtener token ```python theme={null} import requests def obtener_token(base: str, password: str) -> dict: response = requests.post('https://auth.mipos.co.cr/login', json={ 'base': base, 'password': password, }) if response.status_code != 200: error = response.json() raise Exception(error.get('message', 'Error de autenticación')) return response.json() # Uso data = obtener_token('mi_empresa', 'mi_password') token = data['token'] expires_in = data['expires_in'] ``` ## 3. Verificar token ```python theme={null} import jwt def verificar_token(token: str, public_key_path: str = '/ruta/segura/public.pem') -> dict: with open(public_key_path) as f: public_key = f.read() try: return jwt.decode(token, public_key, algorithms=['RS256']) except jwt.ExpiredSignatureError: raise Exception('Token expirado') except jwt.InvalidTokenError: raise Exception('Token inválido') # Uso payload = verificar_token(token) base = payload['base'] # Identificador del cliente ``` ## 4. Middleware para Flask ```python theme={null} from functools import wraps from flask import request, jsonify import jwt PUBLIC_KEY = open('/ruta/segura/public.pem').read() def requiere_auth(f): @wraps(f) def decorated(*args, **kwargs): auth_header = request.headers.get('Authorization', '') token = auth_header.replace('Bearer ', '') if not token: return jsonify({'error': 'Token no proporcionado'}), 401 try: request.cliente = jwt.decode(token, PUBLIC_KEY, algorithms=['RS256']) except jwt.InvalidTokenError: return jsonify({'error': 'Token inválido o expirado'}), 401 return f(*args, **kwargs) return decorated # Uso @app.route('/ventas') @requiere_auth def ventas(): base = request.cliente['base'] # ... lógica del endpoint ``` ## 5. Middleware para FastAPI ```python theme={null} from fastapi import Depends, HTTPException from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials import jwt PUBLIC_KEY = open('/ruta/segura/public.pem').read() security = HTTPBearer() async def verificar_auth(credentials: HTTPAuthorizationCredentials = Depends(security)): try: return jwt.decode(credentials.credentials, PUBLIC_KEY, algorithms=['RS256']) except jwt.InvalidTokenError: raise HTTPException(status_code=401, detail='Token inválido o expirado') # Uso @app.get('/ventas') async def ventas(cliente: dict = Depends(verificar_auth)): base = cliente['base'] # ... lógica del endpoint ``` **Listo.** Tu aplicación Python puede autenticarse y verificar tokens del ecosistema MiPOS.