Factura Fácil API
API para crear comprobantes electrónicos de forma asíncrona. Enviás un POST, recibís 202 + job_id, y el resultado llega por webhook.
ExternalId para evitar duplicados y reintentar con force.URL base
Ejemplos:
- https://www.facil.com.ar/api
Autenticación
Todos los requests requieren:
Authorization: Bearer <TOKEN>
Content-Type: application/json
El token se obtiene con POST /api/auth/token usando api_key y api_secret.
El JWT dura 24 horas.
curl -X POST "https://www.facil.com.ar/api/auth/token" \
-H "Content-Type: application/json" \
-d '{
"api_key": "YOUR_API_KEY",
"api_secret": "YOUR_API_SECRET"
}'
{
"access_token": "JWT_TOKEN",
"token_type": "Bearer",
"expires_in": 86400
}
| HTTP | error | Detalle |
|---|---|---|
| 400 | MISSING_CREDENTIALS |
Faltan api_key o api_secret. |
| 401 | INVALID_CREDENTIALS |
Credenciales invalidas o cliente inactivo. |
| 403 | IP_NOT_ALLOWED |
La IP no esta en allowlist. |
| 405 | METHOD_NOT_ALLOWED |
Metodo no permitido (solo POST). |
| 500 | SERVER_MISCONFIG_JWT_KEY |
Servidor sin clave de firma JWT configurada. |
Flujo
- Obtenés un JWT (Bearer token).
- Enviás
POST /api/crearFacturacon el payload. - Recibís
202 Acceptedconjob_id. - (Opcional) consultás estado por
GET /api/facturas/estado/{jobId}. - (Opcional) descargás el comprobante en PDF por
GET /api/facturas/pdf/{jobId}cuando el job está endone. - Cuando finaliza, Factura Fácil envía el resultado a tu
callback_url.
POST /api/crearFactura
AsíncronoCrea un job de facturación y lo encola para ser procesado por el worker.
{
"ok": true,
"job_id": "12",
"status": "queued",
"attempt": 1,
"forced": 0,
"external_id": "TEST-API-002"
}
Payload
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
ExternalId |
string | Si | Id externo para idempotencia (ej: ORD-123). |
force |
bool | No | Si true, permite reintentar aunque ya exista done/error. |
user_login |
int | Sí | ID del usuario registrado en Facil a nombre de quien se factura. Debe estar habilitado en Facil. |
ExternalIdCliente |
string | No | ID externo del receptor de la factura. Si no viene, se guarda vacío. |
Fpago |
int | Sí | ID de la Forma de pago. (ver en tabla FormasPago). |
DocNro |
string | Sí | Documento/CUIT/DNI del receptor. |
CondicionIVAReceptor |
int | Si en caso que options[Padron] sea false o no se envie | ID del tipo de Condicion del receptor (ver en tabla CondicionIVAReceptor). |
DocTipo |
int | Si | ID del tipo de documento (ver en tabla DocTipo). |
NombreCliente |
string | Sí | Nombre/Razón social del receptor. |
DireccionCliente |
string | Sí | Domicilio del receptor. |
idProvincia |
int | Sí | ID del Código de provincia (ver en tabla Provincias). |
Email |
string | No | Email del receptor. |
Puntovta |
string/int | Sí | Punto de venta. Tiene que estar dado de alta en Facil. |
Concepto |
int | Sí | 1 productos, 2 servicios, 3 ambos. |
Fecha |
string | Sí | Fecha factura YYYY-MM-DD. |
Cbte |
int | No | ID de comprobante (ver tabla Comprobantes). Ej: 1. |
Moneda |
string | No | Default: PES. |
Cambio |
number | No | Tipo de cambio en caso que Moneda sea distinto de PES, Default: 1. |
productos |
array | Sí | Productos a facturar. (ver apartado Productos). |
options |
object | No | Opciones (ver apartado Options). |
callback_url |
string | No | Webhook para recibir el resultado. |
{
"options": {
"Padron": true,
"SaveProd": true,
"SaveMemo": true
}
}
options = @(Padron = true; SaveProd = true; SaveMemo = true)
| Campo | Tipo | Req. | Descripcion |
|---|---|---|---|
Padron |
bool | No | Si true, busca datos del padron en ARCA. |
SaveProd |
bool | No | Si true, guarda los productos en la base de Facil. |
SaveMemo |
bool | No | Si true, agrega "ExternalId numero: " en el campo observaciones dentro de la factura. |
| Campo | Tipo | Req. | Descripción |
|---|---|---|---|
Codigo |
string | Sí | SKU o identificador externo. |
Nombre |
string | Sí | Nombre del producto/servicio. |
Unidad |
int | Sí | ID Unidad de medida (ver tabla Unidades). |
Importe |
number | Sí | Precio final con IVA. |
IVA |
number | Sí | Porcentaje de IVA (ej: 21). |
Cantidad |
number | Sí | Valor decimal de cantidades de unidades. |
Tablas de referencia
Estos catalogos se obtienen de las tablas internas del sistema y pueden variar segun configuracion.
| ID | Descripcion |
|---|---|
| 0 | Contado |
| 1 | Cta. Cte. |
| 2 | Tarjeta Débito |
| 3 | Tarjeta Crédito |
| 4 | Cheque |
| 5 | Transferencia |
| 6 | Otro |
| Codigo | Descripcion |
|---|---|
| 80 | CUIT |
| 86 | CUIL |
| 87 | CDI |
| 89 | LE |
| 90 | LC |
| 91 | CI Extranjera |
| 92 | en trámite |
| 93 | Acta Nacimiento |
| 95 | CI Bs. As. RNP |
| 96 | DNI |
| 94 | Pasaporte |
| 99 | Doc (CONSUMIDOR FINAL) |
| ID | Descripcion |
|---|---|
| 9 | Cliente del Exterior |
| 5 | Consumidor Final |
| 10 | IVA Liberado – Ley N° 19.640 |
| 15 | IVA No Alcanzado |
| 1 | IVA Responsable Inscripto |
| 4 | IVA Sujeto Exento |
| 16 | Monotributo Trabajador Independiente Promovid |
| 8 | Proveedor del Exterior |
| 6 | Responsable Monotributo |
| 7 | Sujeto No Categorizado |
| Codigo | Provincia |
|---|---|
| 1 | Buenos Aires |
| 0 | Capital Federal |
| 2 | Catamarca |
| 16 | Chaco |
| 17 | Chubut |
| 4 | Corrientes |
| 3 | Córdoba |
| 5 | Entre Ríos |
| 18 | Formosa |
| 6 | Jujuy |
| 21 | La Pampa |
| 8 | La Rioja |
| 7 | Mendoza |
| 19 | Misiones |
| 20 | Neuquén |
| 22 | Río Negro |
| 9 | Salta |
| 10 | San Juan |
| 11 | San Luis |
| 23 | Santa Cruz |
| 12 | Santa Fe |
| 13 | Santiago del Estero |
| 24 | Tierra del Fuego |
| 14 | Tucumán |
| Codigo | Descripcion |
|---|---|
| PES | Pesos Argentinos |
| DOL | Dolar Estadounidense |
| 002 | Dolar Libre EEUU |
| 007 | Florines Holandeses |
| 010 | Pesos Mejicanos |
| 011 | Pesos Uruguayos |
| 014 | Coronas Danesas |
| 015 | Coronas Noruegas |
| 016 | Coronas Suecas |
| 018 | Dolar Canadiense |
| 019 | Yens |
| 021 | Libra Esterlina |
| 023 | Bolívar Venezolano |
| 024 | Corona Checa |
| 025 | Dinar Yugoslavo |
| 026 | Dolar Australiano |
| 027 | Dracma Griego |
| 028 | Florín (Antillas Holandesas) |
| 029 | Güaraní |
| 031 | Peso Boliviano |
| 032 | Peso Colombiano |
| 033 | Peso Chileno |
| 034 | Rand Sudafricano |
| 036 | Sucre Ecuatoriano |
| 051 | Dolar de Hong Kong |
| 052 | Dolar de Singapur |
| 053 | Dolar de Jamaica |
| 054 | Dolar de Taiwan |
| 055 | Quetzal Guatemalteco |
| 056 | Forint (Hungría) |
| 057 | Baht (Tailandia) |
| 059 | Dinar Kuwaiti |
| 012 | Real |
| 030 | Shekel (Israel) |
| 035 | Nuevo Sol Peruano |
| 060 | Euro |
| 040 | Lei Rumano |
| 042 | Peso Dominicano |
| 043 | Balboas Panameñas |
| 044 | Córdoba Nicaragüense |
| 045 | Dirham Marroquí |
| 046 | Libra Egipcia |
| 047 | Riyal Saudita |
| 061 | Zloty Polaco |
| 062 | Rupia Hindú |
| 063 | Lempira Hondureña |
| 064 | Yuan (Rep. Pop. China) |
| 009 | Franco Suizo |
| 041 | Derechos Especiales de Giro |
| 049 | Gramos de Oro Fino |
| ID | Descripcion |
|---|---|
| 7 | unidades |
| 1 | kilogramos |
| 2 | metros |
| 3 | metros cuadrados |
| 4 | metros cubicos |
| 5 | litros |
| 6 | 1000 kWh |
| 8 | pares |
| 9 | docenas |
| 10 | quilates |
| 11 | millares |
| 14 | gramos |
| 15 | milimetros |
| 16 | mm cubicos |
| 17 | kilometros |
| 18 | hectolitros |
| 20 | centimetros |
| 27 | cm cubicos |
| 29 | toneladas |
| 30 | dam cubicos |
| 31 | hm cubicos |
| 32 | km cubicos |
| 33 | microgramos |
| 34 | nanogramos |
| 35 | picogramos |
| 41 | miligramos |
| 47 | mililitros |
| 48 | curie |
| 49 | milicurie |
| 50 | microcurie |
| 51 | uiacthor |
| 52 | muiacthor |
| 53 | kg base |
| 54 | gruesa |
| 61 | kg bruto |
| 62 | uiactant |
| 63 | muiactant |
| 64 | uiactig |
| 65 | muiactig |
| 66 | kg activo |
| 67 | gramo activo |
| 68 | gramo base |
| 96 | packs |
| ID | Descripcion |
|---|---|
| 1 | Factura A |
| 2 | Nota de Debito A |
| 3 | Nota de Credito A |
| 4 | Recibo A |
| 5 | Notas de Venta al contado A |
| 6 | Factura B |
| 7 | Nota de Debito B |
| 8 | Nota de Credito B |
| 9 | Recibo B |
| 10 | Notas de Venta al contado B |
| 11 | Factura C |
| 12 | Nota de Debito C |
| 13 | Nota de Credito C |
| 15 | Recibo C |
| 34 | Cbtes. A del Anexo I, Apartado A,inc.f),R.G.Nro. 1415 |
| 35 | Cbtes. B del Anexo I,Apartado A,inc. f),R.G. Nro. 1415 |
| 39 | Otros comprobantes A que cumplan con R.G.Nro. 1415 |
| 40 | Otros comprobantes B que cumplan con R.G.Nro. 1415 |
| 49 | Comprobante de Compra de Bienes Usados a Consumidor Final |
| 60 | Cta de Vta y Liquido prod. A |
| 61 | Cta de Vta y Liquido prod. B |
| 63 | Liquidacion A |
| 64 | Liquidacion B |
| 51 | Factura M |
| 52 | Nota de Débito M |
| 53 | Nota de Crédito M |
| 54 | Recibo M |
| 19 | Facturas de Exportación |
| 20 | Nota de Débito por Operaciones con el Exterior |
| 21 | Nota de Crédito por Operaciones con el Exterior |
| 88 | Remito Electrónico |
| 89 | Resumen de Datos |
| 202 | Nota de Débito electrónica MiPyMEs (FCE) A |
| 203 | Nota de Crédito electrónica MiPyMEs (FCE) A |
| 206 | Factura de Crédito electrónica MiPyMEs (FCE) B |
| 207 | Nota de Débito electrónica MiPyMEs (FCE) B |
| 208 | Nota de Crédito electrónica MiPyMEs (FCE) B |
| 211 | Factura de Crédito electrónica MiPyMEs (FCE) C |
| 212 | Nota de Débito electrónica MiPyMEs (FCE) C |
| 213 | Nota de Crédito electrónica MiPyMEs (FCE) C |
| 201 | Factura de Crédito electrónica MiPyMEs (FCE) A |
Ejemplos
{
"force": true,
"ExternalId": "TEST-API-025",
"user_login": 2,
"ExternalIdCliente": "CLI-EXT-99",
"Fpago": 1,
"DocTipo": 80,
"DocNro": "27256766316",
"NombreCliente": "Juan Perez",
"DireccionCliente": "Av Siempreviva 123",
"idProvincia": 2,
"Email": "juan@correo.com",
"Puntovta": 2,
"Concepto": 1,
"Fecha": "2026-01-12",
"Cbte": 1,
"Moneda": "PES",
"Cambio": 1,
"options": {
"Padron": true,
"SaveProd": true,
"SaveMemo": true
},
"productos": [
{ "Codigo": "SKU-001", "Nombre": "Producto1", "Unidad": 7, "Importe": 1210, "IVA": 21, "Cantidad": 1 },
{ "Codigo": "SKU-002", "Nombre": "Producto2", "Unidad": 7, "Importe": 2000, "IVA": 21, "Cantidad": 1 }
],
"callback_url": "https://webhook.site/xxxx"
}
curl -X POST "http://localhost:8080/api/crearFactura" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{
"force": true,
"ExternalId": "TEST-API-025",
"user_login": 2,
"ExternalIdCliente": "CLI-EXT-99",
"Fpago": 1,
"DocTipo": 80,
"DocNro": "27256766316",
"NombreCliente": "Juan Perez",
"DireccionCliente": "Av Siempreviva 123",
"idProvincia": 2,
"Email": "juan@correo.com",
"Puntovta": 2,
"Concepto": 1,
"Fecha": "2026-01-12",
"Cbte": 1,
"Moneda": "PES",
"Cambio": 1,
"options": {
"Padron": true,
"SaveProd": true,
"SaveMemo": true
},
"productos": [
{ "Codigo": "SKU-001", "Nombre": "Producto1", "Unidad": 7, "Importe": 1210, "IVA": 21, "Cantidad": 1 },
{ "Codigo": "SKU-002", "Nombre": "Producto2", "Unidad": 7, "Importe": 2000, "IVA": 21, "Cantidad": 1 }
],
"callback_url": "https://webhook.site/xxxx"
}'
<?php
$token = 'YOUR_JWT_TOKEN';
$url = 'http://www.facil.com.ar/api/crearFactura';
$payload = [
"force" => true,
"ExternalId" => "TEST-API-025",
"user_login" => 2,
"ExternalIdCliente" => "CLI-EXT-99",
"Fpago" => 1,
"DocTipo" => 80,
"DocNro" => "27256766316",
"NombreCliente" => "Juan Perez",
"DireccionCliente" => "Av Siempreviva 123",
"idProvincia" => 2,
"Email" => "juan@correo.com",
"Puntovta" => 2,
"Concepto" => 1,
"Fecha" => "2026-01-12",
"Cbte" => 1,
"Moneda" => "PES",
"Cambio" => 1,
"options" => [
"Padron" => true,
"SaveProd" => true,
"SaveMemo" => true
],
"productos" => [
["Codigo"=>"SKU-001","Nombre"=>"Producto1","Unidad"=>7,"Importe"=>1210,"IVA"=>21,"Cantidad"=>1],
["Codigo"=>"SKU-002","Nombre"=>"Producto2","Unidad"=>7,"Importe"=>2000,"IVA"=>21,"Cantidad"=>1],
],
"callback_url" => "https://webhook.site/xxxx"
];
$json = json_encode($payload, JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES);
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $json,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'Authorization: Bearer '.$token
]
]);
$res = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "HTTP $http\n";
echo $res;
import requests
token = "YOUR_JWT_TOKEN"
url = "http://www.facil.com.ar/api/crearFactura"
payload = {
"force": True,
"ExternalId": "TEST-API-025",
"user_login": 2,
"ExternalIdCliente": "CLI-EXT-99",
"Fpago": 1,
"DocTipo": 80,
"DocNro": "27256766316",
"NombreCliente": "Juan Perez",
"DireccionCliente": "Av Siempreviva 123",
"idProvincia": 2,
"Email": "juan@correo.com",
"Puntovta": 2,
"Concepto": 1,
"Fecha": "2026-01-12",
"Cbte": 1,
"Moneda": "PES",
"Cambio": 1,
"options": {
"Padron": True,
"SaveProd": True,
"SaveMemo": True
},
"productos": [
{"Codigo":"SKU-001","Nombre":"Producto1","Unidad":7,"Importe":1210,"IVA":21,"Cantidad":1},
{"Codigo":"SKU-002","Nombre":"Producto2","Unidad":7,"Importe":2000,"IVA":21,"Cantidad":1}
],
"callback_url": "https://webhook.site/xxxx"
}
r = requests.post(
url,
json=payload,
headers={
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
},
timeout=20
)
print(r.status_code)
print(r.text)
GET /api/facturas/estado/{jobId}
Polling opcional
Devuelve el estado de un job. El job debe pertenecer al client_id del token.
curl -X GET "http://www.facil.com.ar/api/facturas/estado/12" \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
<?php
$url = 'http://www.facil.com.ar/api/facturas/estado/12';
$token = 'YOUR_JWT_TOKEN';
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 20,
]);
$res = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "HTTP: $http\n";
echo $res;
import requests
url = "http://www.facil.com.ar/api/facturas/estado/12"
token = "YOUR_JWT_TOKEN"
r = requests.get(
url,
headers={"Authorization": f"Bearer {token}"},
timeout=20,
)
print(r.status_code)
print(r.text)
{
"ok": true,
"job_id": "12",
"external_id": "TEST-API-002",
"status": "done",
"error_msg": null,
"result": {
"FacturaNumero": "0002-00000123",
"CAE": "72345678901234",
"FchVtoCAE": "20260201"
},
"created_at": "2026-01-13 14:21:33",
"updated_at": "2026-01-13 14:22:10"
}
GET /api/facturas/pdf/{jobId}
Descarga PDF
Devuelve el PDF del comprobante emitido para ese job. El job debe pertenecer al client_id del token y estar en estado done.
curl -L -X GET "http://www.facil.com.ar/api/facturas/pdf/12" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
--output comprobante_12.pdf
<?php
$url = 'http://www.facil.com.ar/api/facturas/pdf/12';
$token = 'YOUR_JWT_TOKEN';
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $token],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_TIMEOUT => 30,
]);
$pdf = curl_exec($ch);
$http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($http === 200 && $pdf !== false) {
file_put_contents('comprobante_12.pdf', $pdf);
echo "PDF guardado\n";
} else {
echo "Error HTTP $http\n";
}
import requests
url = "http://www.facil.com.ar/api/facturas/pdf/12"
token = "YOUR_JWT_TOKEN"
r = requests.get(
url,
headers={"Authorization": f"Bearer {token}"},
timeout=30,
)
if r.status_code == 200:
with open("comprobante_12.pdf", "wb") as f:
f.write(r.content)
print("PDF guardado")
else:
print("Error:", r.status_code, r.text)
404 JOB_NOT_FOUND, 409 JOB_NOT_DONE, 404 PDF_KEY_NOT_FOUND, 502 PDF_FETCH_FAILED.
Webhooks (callback_url) + Firma
Cuando el job finaliza, Factura Fácil envía un POST al callback_url con el resultado.
Content-Type: application/jsonX-Facil-Webhook-Version: 1X-Facil-Job-Id,X-Facil-External-Id,X-Facil-AttemptX-Facil-Signature: sha256=<hmac>(si haywebhook_secret)
<?php
$secret = 'TU_WEBHOOK_SECRET';
$raw = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_FACIL_SIGNATURE'] ?? '';
$expected = 'sha256=' . hash_hmac('sha256', $raw, $secret);
if (!hash_equals($expected, $sig)) {
http_response_code(401);
echo "INVALID_SIGNATURE\n";
exit;
}
http_response_code(200);
echo "OK\n";
from flask import Flask, request, abort
import hmac, hashlib
app = Flask(__name__)
SECRET = b"TU_WEBHOOK_SECRET"
@app.post("/webhook/facil")
def facil_webhook():
raw = request.get_data()
sig = request.headers.get("X-Facil-Signature","")
expected = "sha256=" + hmac.new(SECRET, raw, hashlib.sha256).hexdigest()
if not hmac.compare_digest(expected, sig):
abort(401)
print("OK:", request.json)
return "OK", 200
Este ejemplo genera la firma con Python y hace el POST al endpoint de tu webhook.
BODY='{"webhook_version":1,"event":"invoice.job.updated","job_id":"12","status":"done"}'
SECRET='TU_WEBHOOK_SECRET'
SIG=$(python - <<'PY'
import hmac,hashlib,os
body=os.environ["BODY"].encode()
secret=os.environ["SECRET"].encode()
print("sha256="+hmac.new(secret, body, hashlib.sha256).hexdigest())
PY
)
curl -X POST "https://tucliente.com/webhook/facil" \
-H "Content-Type: application/json" \
-H "X-Facil-Signature: '"$SIG"'" \
-d "$BODY"
Versionado
X-Facil-Webhook-Version: 1payload.webhook_version = 1
Errores
{
"error": "VALIDATION_ERROR",
"fields": {
"IdCliente": "required",
"Email": "invalid_email"
}
}
{ "error": "IDCLIENTE_NOT_ALLOWED" }
force, la API puede responder 409 para evitar duplicados o indicar reintento requerido.