Migrar desde MaytAPI¶
MaytAPI es un SaaS WhatsApp popular en Brasil y LATAM (precios $40-200/mes/phone). Su modelo: products (tu cuenta) que contienen phones (1 phone = 1 número), con webhooks configurables por producto.
Mapeo conceptual¶
| MaytAPI | qrsgen |
|---|---|
product_id |
owner_tag (string para correlación tenant) |
phone_id (dentro del product) |
Instancia name |
Token por product (api_token) |
Bearer global QRSGEN_API_TOKEN |
| Webhook URL por product | events_webhook_url per-instance |
POST /sendMessage en api.maytapi.com |
POST /api/instances/:name/webhook |
| Webhook payload MaytAPI | WebhookPayload Channel::Api-compatible |
Panel dashboard.maytapi.com |
API REST /api/instances/* |
API surface relevante¶
MaytAPI usa un esquema product-based:
api.maytapi.com/api/<PRODUCT_ID>/...
├── listPhones
├── /<PHONE_ID>/
│ ├── sendMessage
│ ├── status
│ ├── screen ← QR
│ └── ...
└── webhook
Auth: header x-maytapi-key: <API_TOKEN> (no Bearer).
Endpoints útiles para la migración¶
| Endpoint | Para qué |
|---|---|
GET /api/{product_id}/listPhones |
Listar todos los phones del product |
GET /api/{product_id}/{phone_id}/status |
Estado de un phone concreto |
GET /api/{product_id}/webhook |
Webhook URL configurada |
Estructura del response de /listPhones (resumida)¶
{
"success": true,
"data": [
{
"id": 12345,
"number": "34600000000",
"name": "support-line",
"type": "whatsapp",
"status": "active",
"multi_device": true
}
]
}
Los campos que importan para qrsgen:
- id o name → name de instancia (usa name si lo tienes, más
legible; si no, phone-12345).
- number → informativo (qrsgen lo descubre tras pairing).
MaytAPI NO expone las claves criptográficas ni historial completo via API. Mismo limit que cualquier SaaS — re-pairing obligatorio.
Receta¶
1. Inventory¶
MAYT_PRODUCT_ID="12345"
MAYT_TOKEN="..."
curl -sS -H "x-maytapi-key: $MAYT_TOKEN" \
"https://api.maytapi.com/api/$MAYT_PRODUCT_ID/listPhones" \
| jq '.data[] | {
id: .id,
name: (.name // "phone-\(.id)"),
number: .number,
status: .status
}' > /tmp/maytapi-phones.json
2. Webhook URL configurada en MaytAPI (para reutilizarla)¶
WEBHOOK=$(curl -sS -H "x-maytapi-key: $MAYT_TOKEN" \
"https://api.maytapi.com/api/$MAYT_PRODUCT_ID/webhook" | jq -r '.webhook // empty')
echo "Webhook actual: $WEBHOOK"
MaytAPI tiene una sola webhook URL por producto (no per-phone), mientras qrsgen lo tiene per-instance. Esto te permite simplificar (misma URL para todas) o aprovechar la oportunidad para separar por instancia.
3. Generar plan JSON para qrsgen¶
import json, os
with open("/tmp/maytapi-phones.json") as f:
raw = f.read().strip()
phones = json.loads(f"[{raw.replace('}\n{', '},{')}]") if raw else []
webhook_default = os.environ.get("NEW_WEBHOOK_URL", "https://my-app.com/qrsgen-events")
plan = {
"instances": [
{
"name": p["name"],
"events_webhook_url": webhook_default,
"owner_tag": "migrated-from-maytapi",
}
for p in phones if p.get("name")
]
}
with open("/tmp/qrsgen-plan.json", "w") as f:
json.dump(plan, f, indent=2)
4. Provisionar en qrsgen¶
QRSGEN_URL=http://qrsgen:3100 QRSGEN_TOKEN="$TOK" \
python3 tools/migrate/bulk-provision.py /tmp/qrsgen-plan.json
5. Re-pairing manual¶
Igual que cualquier migración: usuarios re-escanean. MaytAPI no exporta sesiones a otros clientes.
Tip: MaytAPI te muestra el QR via
GET /api/{product_id}/{phone_id}/screen — útil para confirmar que
no lo tienes (porque ya estaba pareado en MaytAPI) y por tanto sí
necesitas re-pairing en qrsgen.
6. Switch del webhook downstream¶
Cambias la URL que apunta a MaytAPI por la de qrsgen:
- webhook_url = https://api.maytapi.com/api/12345/<PHONE_ID>/sendMessage
+ webhook_url = http://qrsgen:3100/api/instances/<INSTANCE_NAME>/webhook
Si tu downstream procesaba payloads MaytAPI nativos, ver Diferencias de payload más abajo.
7. Pausar / cancelar en MaytAPI¶
# Disconnect (no borra, solo desactiva)
curl -X POST -H "x-maytapi-key: $MAYT_TOKEN" \
"https://api.maytapi.com/api/$MAYT_PRODUCT_ID/$PHONE_ID/logout"
MaytAPI cobra por phones activos — desactivar reduce el coste inmediatamente. El borrado definitivo se hace desde el panel.
Diferencias de payload (webhook entrante)¶
MaytAPI te envía algo como:
{
"type": "message",
"user": {"id": "34600000000@c.us", "name": "Pepe"},
"conversation": "34600000000@c.us",
"message": {
"type": "text",
"text": "Hola",
"id": "true_34600000000@c.us_3EB0..."
},
"phoneId": 12345,
"productId": "..."
}
qrsgen emite (Channel::Api):
{
"event": "message_created",
"id": <int>,
"message_type": "incoming",
"content": "Hola",
"source_id": "true_34600000000@c.us_3EB0...",
"conversation": {
"id": <int>,
"inbox_id": 87,
"meta": {"sender": {"phone_number": "+34600000000", "identifier": "34600000000@s.whatsapp.net"}}
}
}
JID gotcha (importante): MaytAPI usa el formato legacy @c.us.
qrsgen / whatsmeow normaliza siempre a @s.whatsapp.net. Si tu
downstream tiene @c.us hardcoded, hay que sustituirlo.
Coste MaytAPI vs qrsgen self-host¶
| Plan MaytAPI | Precio | Equivalente qrsgen self-host |
|---|---|---|
| Basic (1 phone) | $40/mes | VPS €10/mes — 4× más barato |
| Pro (5 phones) | $99/mes | VPS €15/mes — 6× más barato |
| Business (20 phones) | $249/mes | VPS €25/mes — 10× más barato |
Antes de migrar: calcula tu TCO incluyendo tiempo de ops.
Glosario¶
MaytAPI: SaaS WhatsApp con tracción especial en Brasil y LATAM. Precio $40-249/mes según número de phones.
Product: terminología MaytAPI para "tu cuenta" — agrupa
varios phones. Equivalente conceptual al owner_tag de qrsgen
(aunque con un único product por cliente).
Phone: terminología MaytAPI para "una sesión WhatsApp" (= un
número). Equivalente a instance en qrsgen.
x-maytapi-key: header de auth que MaytAPI usa en lugar del Bearer estándar.
Multi-device (campo de MaytAPI): indica si el phone está en modo Multi-Device. qrsgen / whatsmeow solo soporta Multi-Device — el modo legacy ya no se vende.
JID legacy @c.us: formato antiguo de WhatsApp para identificar
números. MaytAPI lo expone porque Baileys (su backend) lo conserva
internamente. qrsgen normaliza a @s.whatsapp.net (estándar
Multi-Device).