Importar facturas a Quipu
Cada factura de compra que entra en BuildNexion se crea como gasto en Quipu. Un servidor en Python (FastAPI) recibe el webhook y llama a la API de Quipu.
Qué necesitas
- Una API key de BuildNexion con el permiso
write:webhooks. - El App ID y el App Secret de la API de Quipu.
- Un servidor con una URL pública HTTPS.
1. Registrar el webhook
bash
curl -X POST https://app.buildnexion.com/api/v1/webhooks \
-H "Authorization: Bearer $BUILDNEXION_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://tu-servidor.es/buildnexion/quipu",
"events": ["invoice.created"]
}'Guarda el secret de la respuesta para verificar la firma.
2. Token de Quipu
Quipu usa OAuth2 con credenciales de cliente; guarda el token mientras no caduque:
python
import os, time, requests
_token = {"valor": None, "caduca": 0}
def token_quipu() -> str:
if _token["valor"] and _token["caduca"] > time.time():
return _token["valor"]
res = requests.post(
"https://getquipu.com/oauth/token",
auth=(os.environ["QUIPU_APP_ID"], os.environ["QUIPU_APP_SECRET"]),
data={"grant_type": "client_credentials"},
)
res.raise_for_status()
cuerpo = res.json()
_token["valor"] = cuerpo["access_token"]
_token["caduca"] = time.time() + cuerpo["expires_in"] - 30
return _token["valor"]3. Recibir el aviso
python
import json, os
from fastapi import BackgroundTasks, FastAPI, HTTPException, Request
from firma import firma_valida # ver "Verificar la firma"
app = FastAPI()
@app.post("/buildnexion/quipu")
async def webhook(request: Request, tareas: BackgroundTasks):
cuerpo = await request.body()
cabecera = request.headers.get("x-buildnexion-signature", "")
if not firma_valida(cuerpo, cabecera, os.environ["BUILDNEXION_WEBHOOK_SECRET"]):
raise HTTPException(401, "firma no válida")
evento = json.loads(cuerpo)
if evento["event"] == "invoice.created":
tareas.add_task(crear_gasto, evento["data"], request.headers.get("x-buildnexion-delivery"))
return {"ok": True} # responde enseguida; el gasto se crea después4. Crear el gasto
invoice.created trae número, fecha, proveedor con su NIF, base, IVA y total (en euros), así que no hace falta pedir nada más a BuildNexion:
python
import requests
def crear_gasto(factura: dict, entrega: str):
if ya_procesada(entrega): # tu tabla de entregas procesadas
return
base = factura["base_imponible"] if factura["base_imponible"] is not None else factura["amount"]
payload = {
"data": {
"type": "expenses",
"attributes": {
"issue_date": factura["date"], # YYYY-MM-DD
"number": factura["number"],
"subtotal": f"{base:.2f}", # euros, como texto
"total": f"{factura['amount']:.2f}", # con IVA
"supplier_name": factura["supplier"]["name"],
"supplier_tin": factura["supplier"]["cif"],
},
}
}
res = requests.post(
"https://getquipu.com/api/expenses",
headers={
"Authorization": f"Bearer {token_quipu()}",
"Content-Type": "application/vnd.api+json",
"Accept": "application/vnd.api+json",
},
json=payload,
)
res.raise_for_status()
marcar_procesada(entrega)Los campos de Quipu son orientativos: compruébalos en su documentación. Como tu endpoint ya respondió 200, si Quipu falla BuildNexion no reintentará: guarda el error y reinténtalo tú.
Errores típicos
- Quipu responde 422: suele ser una factura duplicada para el mismo proveedor. Decide si la actualizas o la ignoras.
- 401 de Quipu: el token ha caducado; bórralo de la caché y pide otro.
- base_imponible a null: la lectura de la factura no la encontró. Usa el total o deja el gasto pendiente de revisar.