Cómo integrarte
| API de aliados | Servidor MCP | |
|---|---|---|
| Para | Aseguradoras, flotas, concesionarios y plataformas (B2B) | Usuarios de Güpp desde Claude, ChatGPT u otro agente de IA |
| Autenticación | X-API-Key: gk_live_… (la entrega Güpp) | OAuth 2.1 con registro dinámico y PKCE; el usuario entra con su celular |
| Actúa como | Tu empresa: las órdenes se te facturan | El usuario que autorizó: cobra como en la app |
| Base | /api/partner/v1 | /mcp |
Para obtener una API key escríbenos a aliados@gupp.co. El equipo de Güpp crea tu cuenta de aliado, genera la llave con los permisos que necesitas y configura tu webhook.
API de aliados
API REST con JSON. Todas las rutas cuelgan de https://api-sandbox-3e2d10e3.up.railway.app/api/partner/v1. Los valores de dinero son pesos colombianos enteros (150000 = $150.000) y las fechas van en ISO 8601 con zona horaria.
Autenticación
Envía tu llave en el header X-API-Key en cada petición. Las llaves empiezan por gk_live_, se muestran una sola vez al crearlas y Güpp solo guarda su huella: si pierdes una, pide que la revoquen y te creen otra. Guárdala en un gestor de secretos, nunca en el código del cliente ni en una app móvil.
curl -s -H "X-API-Key: $GUPP_KEY" https://api-sandbox-3e2d10e3.up.railway.app/api/partner/v1/meCada llave tiene permisos (scopes). Pide solo los que uses:
| Permiso | Permite |
|---|---|
catalog:read | Ver servicios y aliados cercanos (CDA, talleres) |
quotes:write | Cotizar servicios |
orders:write | Crear y cancelar órdenes |
orders:read | Consultar órdenes y el costo de cancelarlas |
Endpoints
| Ruta | Permiso | Qué hace | |
|---|---|---|---|
| GET | /me | cualquiera | Aliado, nombre y permisos de la llave, límite y webhook configurado |
| GET | /services | catalog:read | Servicios disponibles con tarifas |
| GET | /places?lat&lng&kind | catalog:read | Aliados cercanos (CDA, talleres, lavaderos). radius_km opcional |
| POST | /quotes | quotes:write | Precio estimado, distancia, proveedores cerca y ETA |
| POST | /orders | orders:write | Pide el servicio (201 con la orden) |
| GET | /orders?status=active&external_ref= | orders:read | Tus órdenes, paginadas (limit, offset) |
| GET | /orders/{id} | orders:read | Detalle: estado, proveedor, ETA, PIN, precio e historial |
| GET | /orders/{id}/cancel-preview | orders:read | Cuánto costaría cancelar en este momento |
| POST | /orders/{id}/cancel | orders:write | Cancela la orden; exige accept_fee: true si tiene costo |
curl -s -X POST https://api-sandbox-3e2d10e3.up.railway.app/api/partner/v1/quotes \
-H "X-API-Key: $GUPP_KEY" -H "Content-Type: application/json" \
-d '{
"service_code": "grua",
"pickup_lat": 4.6533, "pickup_lng": -74.0836,
"dropoff_lat": 4.7001, "dropoff_lng": -74.0502,
"vehicle_kind": "carro"
}'Crear una orden
El cuerpo lleva los mismos campos de la cotización más los datos de la persona en sitio. El proveedor llamará a contact_phone, así que debe ser el celular de quien está con el vehículo.
| Campo | Tipo | Descripción |
|---|---|---|
service_code | texto, obligatorio | Código del servicio, p. ej. grua, paso_corriente, cambio_llanta |
pickup_lat, pickup_lng | número, obligatorio | Dónde está el vehículo |
dropoff_lat, dropoff_lng | número | Destino. Obligatorio en servicios con traslado (grúa) |
pickup_address, dropoff_address | texto | Direcciones legibles para el proveedor |
vehicle_kind | moto | carro | camioneta | pesado | Define la tarifa y el tipo de grúa |
vehicle_description | texto | Placa, marca y color, p. ej. “ABC123 · Kia Picanto gris” |
contact_name, contact_phone | texto, obligatorio | Persona en sitio |
external_ref | texto | Tu referencia (siniestro, ticket). Única entre tus órdenes activas: sirve de idempotencia |
problem, notes | texto | Qué pasó e indicaciones para llegar |
scheduled_at | fecha ISO | Para servicios con cita (lavado, revisión técnico-mecánica) |
curl -s -X POST https://api-sandbox-3e2d10e3.up.railway.app/api/partner/v1/orders \
-H "X-API-Key: $GUPP_KEY" -H "Content-Type: application/json" \
-d '{
"service_code": "grua",
"pickup_lat": 4.6533, "pickup_lng": -74.0836,
"pickup_address": "Av. 68 con Cll 26",
"dropoff_lat": 4.7001, "dropoff_lng": -74.0502,
"dropoff_address": "Taller aliado Cll 100",
"vehicle_kind": "carro",
"vehicle_description": "ABC123 · Kia Picanto gris",
"contact_name": "Ana Ruiz",
"contact_phone": "3157778899",
"external_ref": "SINIESTRO-2026-00123",
"problem": "choque",
"notes": "Vehículo en el carril derecho"
}'external_ref: si la orden ya existe y sigue activa, recibirás 409 en lugar de un duplicado. Consúltala con GET /orders?external_ref=….curl -s -H "X-API-Key: $GUPP_KEY" \
"https://api-sandbox-3e2d10e3.up.railway.app/api/partner/v1/orders?status=active&external_ref=SINIESTRO-2026-00123"Las órdenes de aliados se facturan a tu empresa (payment_method: "invoice") y puedes tener muchas activas a la vez. Cada orden trae tracking_url, un link público de seguimiento en vivo que puedes compartir con tu cliente.
Estados de la orden y PIN de inicio
| Estado | Significa |
|---|---|
searching | Buscando el proveedor más cercano |
assigned | Un proveedor aceptó. Ya hay proveedor, ETA y start_pin |
en_route | El proveedor va en camino |
arrived | El proveedor llegó al punto de recogida |
in_service | Servicio en curso (empezó tras validar el PIN) |
completed | Finalizado. final_price trae el valor definitivo |
cancelled | Cancelada por ti, por soporte o por el proveedor |
no_provider | No hubo proveedores disponibles a tiempo. Puedes volver a pedirla |
scheduled | Reserva con cita creada; Güpp la confirma |
confirmed | Reserva confirmada |
start_pin. Hazlo llegar a tu cliente (SMS, WhatsApp o tu app): el proveedor se lo pedirá para empezar el servicio. Así tu cliente confirma que es el proveedor correcto antes de entregar el vehículo.Cancelaciones
Cancelar es gratis mientras buscamos proveedor y durante los 2 minutos siguientes a la asignación. Después se cobra una compensación al proveedor por el desplazamiento: 20 % del valor estimado si va en camino, o 35 % si ya llegó, con un mínimo de $8.000 y un máximo de $30.000. Un servicio en curso, finalizado o ya cancelado no se puede cancelar (409). Consulta siempre el costo antes:
# 1. ¿Cuánto cuesta cancelar ahora?
curl -s -H "X-API-Key: $GUPP_KEY" https://api-sandbox-3e2d10e3.up.railway.app/api/partner/v1/orders/$ORDER_ID/cancel-preview
# → {"fee": 0, "reason": "Cancelación sin costo"}
# 2. Cancelar. Si fee > 0 hay que confirmar con "accept_fee": true (si no, responde 409)
curl -s -X POST https://api-sandbox-3e2d10e3.up.railway.app/api/partner/v1/orders/$ORDER_ID/cancel \
-H "X-API-Key: $GUPP_KEY" -H "Content-Type: application/json" \
-d '{"reason": "El cliente consiguió ayuda por su cuenta", "accept_fee": true}'Errores y límites
Los errores responden JSON con un campo detail en español. En validaciones (422), detail es una lista con el campo y el problema.
| Código | Cuándo | Qué hacer |
|---|---|---|
| 401 | Falta X-API-Key, o la llave es inválida, está revocada o venció | Revisa la llave; pide una nueva si la revocaron |
| 403 | La llave no tiene el permiso, o tu cuenta de aliado está desactivada | Pide el permiso o contacta a Güpp |
| 404 | La orden no existe o no es tuya | Verifica el id |
| 409 | external_ref repetido en una orden activa, o la orden ya no admite el cambio | Consulta el estado actual antes de reintentar |
| 422 | Cuerpo o parámetros inválidos | Corrige los campos indicados en detail |
| 429 | Superaste el límite de peticiones por minuto | Espera los segundos del header Retry-After y reintenta |
Cada llave tiene un límite de peticiones por minuto (120 por defecto, ajustable hasta 5.000). En vez de consultar una orden en bucle, usa los webhooks.
Webhooks
Güpp hace POST a la URL https que configuraste cada vez que se crea o cambia una de tus órdenes. El cuerpo es JSON con el evento, la fecha y la orden completa en data (el mismo formato de GET /orders/{id}).
Eventos y entregas
| Evento | Cuándo |
|---|---|
order.created | Se creó una orden tuya |
order.updated | Cambió el estado, el proveedor, el precio o el PIN de una orden |
ping | Prueba enviada desde la consola de Güpp |
POST /webhooks/gupp HTTP/1.1
Content-Type: application/json
User-Agent: Gupp-Webhooks/1.0
X-Gupp-Event: order.updated
X-Gupp-Delivery: 5b0c3c1e-8f0a-4d1e-9a51-0b7f2d6a9e44
X-Gupp-Signature: t=1794449005,v1=6f1c0a…e93b
{
"event": "order.updated",
"created_at": "2026-11-11T21:27:44.512Z",
"data": {
"id": "d0df529f-38be-4b59-87a4-8847bffc7b4c",
"code": "GP-Q2YRSD",
"status": "en_route",
"external_ref": "SINIESTRO-2026-00123",
"start_pin": "4821",
"eta_minutes": 7,
"provider": { "display_name": "Rescate Vial Norte", "vehicle_plate": "TTX902", … },
…
}
}- Responde con un código
2xxen menos de 10 segundos. Procesa en segundo plano si tu lógica tarda. - Si no respondes 2xx, se reintenta: 3 intentos en total (inmediato, a los 5 s y a los 30 s).
- Usa
X-Gupp-Deliverypara descartar duplicados, y el campostatusde la orden (no el orden de llegada) como fuente de verdad. - Todas las entregas, con su respuesta, quedan registradas en la consola de Güpp para soporte.
Verificar la firma
Cada entrega trae X-Gupp-Signature: t=<unix>,v1=<hex>, donde v1 es HMAC_SHA256(webhook_secret, `${t}.${cuerpo_crudo}`) en hexadecimal. Verifica sobre el cuerpo crudo (antes de parsear el JSON), compara en tiempo constante y rechaza firmas con más de 5 minutos para evitar repeticiones. El secreto te lo entrega Güpp y se puede rotar.
import crypto from "node:crypto";
import express from "express";
const SECRET = process.env.GUPP_WEBHOOK_SECRET;
const TOLERANCE_S = 5 * 60;
function verifyGupp(header, rawBody) {
const parts = Object.fromEntries(
(header ?? "").split(",").map((p) => p.split("=", 2)),
);
const t = Number(parts.t);
if (!t || !parts.v1) return false;
if (Math.abs(Date.now() / 1000 - t) > TOLERANCE_S) return false;
const expected = crypto
.createHmac("sha256", SECRET)
.update(`${t}.`)
.update(rawBody) // Buffer con el cuerpo tal cual llegó
.digest("hex");
const a = Buffer.from(expected, "hex");
const b = Buffer.from(parts.v1, "hex");
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
const app = express();
// Importante: el cuerpo crudo, no el JSON ya parseado.
app.post("/webhooks/gupp", express.raw({ type: "application/json" }), (req, res) => {
if (!verifyGupp(req.get("X-Gupp-Signature"), req.body)) {
return res.status(400).send("firma inválida");
}
const event = JSON.parse(req.body.toString("utf8"));
// Responde rápido y procesa en segundo plano (idempotente por X-Gupp-Delivery).
res.sendStatus(204);
queue.push(event);
});import hashlib
import hmac
import os
import time
from fastapi import FastAPI, Header, HTTPException, Request
SECRET = os.environ["GUPP_WEBHOOK_SECRET"].encode()
TOLERANCE_S = 5 * 60
app = FastAPI()
def verify_gupp(header: str | None, raw_body: bytes) -> bool:
try:
parts = dict(p.split("=", 1) for p in (header or "").split(","))
t = int(parts["t"])
received = parts["v1"]
except (KeyError, ValueError):
return False
if abs(time.time() - t) > TOLERANCE_S:
return False
expected = hmac.new(SECRET, f"{t}.".encode() + raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, received)
@app.post("/webhooks/gupp", status_code=204)
async def gupp_webhook(request: Request, x_gupp_signature: str | None = Header(None)):
raw = await request.body() # cuerpo crudo
if not verify_gupp(x_gupp_signature, raw):
raise HTTPException(400, "firma inválida")
event = await request.json()
# Encola y responde en menos de 10 s; deduplica por X-Gupp-Delivery.
enqueue(event)Servidor MCP
Güpp expone un servidor Model Context Protocol para que un agente de IA cotice y pida servicios, maneje el parqueo o consulte multas en nombre de un usuario de Güpp.
| Dato | Valor |
|---|---|
| URL | https://api-sandbox-3e2d10e3.up.railway.app/mcp |
| Transporte | Streamable HTTP |
| Autorización | OAuth 2.1: registro dinámico de clientes (RFC 7591) + código de autorización con PKCE S256 |
| Scope | gupp |
| Descubrimiento | /.well-known/oauth-protected-resource/mcp y /.well-known/oauth-authorization-server |
OAuth y consentimiento
- El cliente MCP descubre el servidor de autorización y se registra solo en
/register. - Abre
/authorizecon PKCE. El usuario llega a la pantalla de consentimiento de Güpp, escribe su celular y el código de verificación (OTP). - Ve qué app pide acceso y qué podrá hacer, y toca Autorizar.
- El cliente cambia el código en
/token: recibe un access token de 1 hora y un refresh token de 30 días (rotativo). Se pueden revocar en/revoke.
El agente actúa como el usuario: los servicios se cobran como en la app (billetera, tarjeta o efectivo). Güpp puede bloquear una app, lo que revoca todos sus tokens, o retirar el acceso de un usuario puntual.
Herramientas
| Herramienta | Qué hace |
|---|---|
list_services | Catálogo de servicios con tarifas |
my_vehicles | Vehículos del usuario y vencimientos de documentos |
quote_service | Cotiza un servicio entre dos puntos |
request_service | Pide el servicio (grúa, asistencia, lavado…) |
my_orders | Órdenes del usuario (activas por defecto) |
get_order | Estado, proveedor, ETA y PIN de una orden |
cancel_order | Cancela. Si hay tarifa, primero la informa y exige accept_fee=true |
find_parking | Zonas de parqueo en vía cercanas |
start_parking | Inicia un parqueo por minutos |
my_parking | Parqueos activos y tiempo restante |
stop_parking | Termina el parqueo y reembolsa los minutos no usados |
wallet_balance | Saldo de la billetera Güpp |
my_policies | Pólizas SOAT y todo riesgo |
check_fines | Consulta multas por cédula o placa |
Conectar en Claude
- Abre Configuración → Conectores.
- Toca “Agregar conector personalizado”.
- Pega
https://api-sandbox-3e2d10e3.up.railway.app/mcpy guarda. - Inicia sesión con tu celular y el código de verificación, revisa los permisos y toca Autorizar.
- Pídele a Claude, por ejemplo: “Cotízame una grúa desde la Calle 26 con 68 hasta mi taller”.