Ir al contenido

Perfil y privacidad

Con estas rutas tu app maneja lo que el número vinculado muestra de sí mismo y a quién:

  • el perfil propio: el nombre visible, la Info (el recado, recado en la API) y la foto;
  • la privacidad: quién ve la última vez, la foto o los estados, quién te agrega a grupos, las confirmaciones de lectura y el resto de opciones de WhatsApp;
  • el temporizador por defecto de los mensajes temporales en los chats nuevos;
  • los bloqueos: la lista de contactos bloqueados, bloquear y desbloquear.

Los cambios son comandos durables: el motor responde 202 y los ejecuta contra WhatsApp. Lo que pide el motor queda con confirmado: false hasta que WhatsApp dice lo mismo (un evento o la consulta que el motor hace después del cambio).

El perfil de otros (su nombre, su Info, su foto) está en Contactos.

  • Una instancia conectado. Las consultas con actualizar=true responden 409 no_conectada sin conexión; un comando sin conexión vuelve a la cola y se reintenta.
  • Una API key con estos scopes:
Operación Ruta Scope
Ver el perfil propio GET /v1/instances/{id}/profile profile:read y/o privacy:read
Ver la privacidad GET /v1/instances/{id}/privacy privacy:read
Ver los bloqueados GET /v1/instances/{id}/blocklist privacy:read
Cambiar el nombre PUT /v1/instances/{id}/profile/name profile:write
Cambiar la Info PUT /v1/instances/{id}/profile/about profile:write
Cambiar o quitar la foto PUT o DELETE /v1/instances/{id}/profile/picture profile:write
Cambiar una opción de privacidad PUT /v1/instances/{id}/privacy privacy:write
El temporizador por defecto PUT /v1/instances/{id}/privacy/disappearing privacy:write
Bloquear o desbloquear POST /v1/instances/{id}/blocklist privacy:write

GET …/profile muestra a cada key solo lo suyo: con profile:read, el nombre, la Info y la foto; con privacy:read, la privacidad, el temporizador y cuántos bloqueados hay. Sin ninguno de los dos, 403.

Los comandos piden una Idempotency-Key (de 1 a 200 caracteres). Para cambiar la foto, súbela antes con media:write (ver Multimedia).

Los ejemplos de Go y JavaScript usan una función auxiliar, llamar, que se define en el primer paso: el SDK de Go no trae métodos de perfil, así que es HTTP directo con el token del cliente.

GET /v1/instances/{id}/profile responde con lo que el motor sabe. Cada dato del perfil trae valor, desde (quién lo fijó: whatsapp si fue el teléfono, motor si fue un comando, o consulta), confirmado y momento. La foto se ve por su ID (vacío: sin foto). Con actualizar=true el motor consulta antes al servidor lo que la key puede leer.

Ventana de terminal
curl "https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/profile?actualizar=true" \
-H "Authorization: Bearer TU_API_KEY"
200 OK (con profile:read y privacy:read)
{
"nombre": { "valor": "Sucursal centro", "desde": "motor", "confirmado": true, "momento": "2026-10-24T15:25:00Z" },
"recado": { "valor": "Abierto de 9 a 18 h", "desde": "consulta", "confirmado": true, "momento": "2026-10-24T15:26:00Z" },
"foto": { "valor": "FOTO_ID", "desde": "motor", "confirmado": true, "momento": "2026-10-24T15:27:00Z" },
"privacidad": {
"groupadd": "contacts", "last": "contacts", "status": "contacts", "profile": "all",
"readreceipts": "all", "online": "all", "calladd": "all", "messages": "all",
"defense": "off", "stickers": "contacts"
},
"temporizador_por_defecto_seg": 0,
"bloqueados": 3,
"consultado_en": "2026-10-24T15:26:00Z"
}

GET …/privacy devuelve solo la parte de privacidad (con privacy:read).

Cambio Ruta Cuerpo Tipo de comando
Nombre visible PUT …/profile/name {"nombre": "…"}, de 1 a 25 caracteres profile.name
Info PUT …/profile/about {"texto": "…"}; vacío la deja en blanco profile.about
Foto PUT …/profile/picture {"media_id": "…"} profile.picture
Quitar la foto DELETE …/profile/picture sin cuerpo profile.picture
  • El nombre sale por la sincronización de la app de WhatsApp; su eco lo confirma.
  • La Info que muestran los teléfonos es el text status de WhatsApp. El motor la escribe como WhatsApp Web, con la forma que elige el operador en Ajustes (perfil.forma_info, fijada al aceptar el comando). Con la forma de fábrica, 24h, la Info se ve 24 horas y después deja de verse, y admite hasta 50 caracteres. El result del comando dice por qué vía salió (via), con qué duración (duracion_seg) y qué pasó con el campo viejo (clasico: ok o fallo).
  • La foto es experimental: un JPEG subido antes a esta instancia, ya listo, de hasta 2 MB y de 192 a 2048 píxeles por lado. WhatsApp solo acepta 640 × 640, así que el motor la recorta al centro y la deja en esa medida.
Ventana de terminal
curl -X PUT https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/profile/name \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: perfil-nombre-centro" \
-d '{"nombre": "Sucursal centro"}'
curl -X PUT https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/profile/about \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: perfil-info-horario" \
-d '{"texto": "Abierto de 9 a 18 h"}'
curl -X PUT https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/profile/picture \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: perfil-foto-logo" \
-d '{"media_id": "8a1d2c3e-0000-4000-8000-0000000000e3"}'
GET /v1/commands/{id} · result de profile.about
{ "via": "mex", "duracion_seg": 86400, "clasico": "ok" }

3. Cambia la privacidad y el temporizador por defecto

Sección titulada «3. Cambia la privacidad y el temporizador por defecto»

PUT …/privacy cambia una opción por comando (privacy.set). El servidor devuelve la privacidad completa, que queda confirmada, y llega un privacy.changed.

opcion Qué controla Valores
groupadd Quién te agrega a grupos all, contacts, contact_blacklist, none
last Hora de última vez all, contacts, contact_blacklist, none
status Quién ve tus estados all, contacts, contact_blacklist, none
profile Quién ve tu foto all, contacts, contact_blacklist, none
readreceipts Confirmaciones de lectura all, none
online Quién ve que estás en línea all, match_last_seen
calladd Quién te llama all, known
messages Quién te escribe all, contacts
defense Protección avanzada on_standard, off
stickers Stickers contacts, contact_allowlist, none

Las listas de contact_blacklist y contact_allowlist se editan en el teléfono. Un valor que la opción no acepta es 422.

PUT …/privacy/disappearing fija el temporizador de los chats nuevos (privacy.disappearing) con segundos: 0 (desactivado), 86400 (24 horas), 604800 (7 días) o 7776000 (90 días). Cada chat sigue con el suyo (eso es el mensaje disappearing, ver Mensajes). El SDK no puede leer este valor: el motor muestra el último que fijó o que avisó WhatsApp.

Ventana de terminal
curl -X PUT https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/privacy \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: privacidad-grupos-contactos" \
-d '{"opcion": "groupadd", "valor": "contacts"}'
curl -X PUT https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/privacy/disappearing \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: temporizador-7-dias" \
-d '{"segundos": 604800}'

GET …/blocklist devuelve los bloqueados según lo último que dijo WhatsApp: cada uno con su contacto (peer), su telefono cuando se conoce, bloqueado, cambio_en y desde. Con actualizar=true el motor pide antes la lista al servidor.

POST …/blocklist bloquea o desbloquea un destino (telefono, jid o peer_ref) con bloquear: true o false (contact.block). El servidor devuelve la lista completa, que queda confirmada, y llega un blocklist.changed con el command_id. Un contacto del que el motor no conoce su LID puede fallar con sin_identidad.

Ventana de terminal
curl -X POST https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/blocklist \
-H "Authorization: Bearer TU_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: bloquear-spam-0042" \
-d '{"destino": {"peer_ref": "4b7d9e01-0000-4000-8000-0000000000b4"}, "bloquear": true}'
curl "https://motor.ejemplo.com/v1/instances/INSTANCIA_ID/blocklist?actualizar=true" \
-H "Authorization: Bearer TU_API_KEY"

Los comandos (profile.name, profile.about, profile.picture, privacy.set, privacy.disappearing y contact.block) nacen aceptado y terminan completado o fallido. El efecto llega con su command_id y desde: motor; un cambio hecho en el teléfono, con desde: whatsapp y sin command_id.

Cambió el perfil propio. campo es nombre, recado o foto; valor es el nombre, la Info o el ID de la foto, y una foto quitada trae quitada: true. desde es whatsapp, motor o consulta. Está también en el Catálogo de eventos.

{ "campo": "recado", "valor": "Abierto de 9 a 18 h", "desde": "motor", "momento": "2026-10-24T15:26:00Z" }

Cambió la privacidad. privacidad trae todas las opciones con su valor nuevo y cambiadas, solo las que cambiaron. Un cambio del temporizador por defecto trae cambiadas: ["temporizador_por_defecto"] y temporizador_por_defecto_seg.

{
"privacidad": {
"groupadd": "contacts", "last": "contacts", "status": "contacts", "profile": "all",
"readreceipts": "all", "online": "all", "calladd": "all", "messages": "all",
"defense": "off", "stickers": "contacts"
},
"cambiadas": ["groupadd"],
"desde": "motor"
}

Cambiaron los bloqueos. cambios trae cada contacto que cambió, con su estado nuevo.

{
"cambios": [
{
"contacto": { "peer_ref": "…", "jid": "CONTACTO@lid", "clase": "usuario", "aliases": ["CONTACTO@s.whatsapp.net"] },
"telefono": "+NUMERO_DEL_CONTACTO",
"bloqueado": true,
"cambio_en": "2026-10-24T15:40:00Z",
"desde": "motor"
}
],
"desde": "motor"
}
HTTP error_code Cuándo
403 — A tu key le falta el scope (profile:read ni privacy:read en GET …/profile; profile:write, privacy:read o privacy:write en las demás).
404 — El media_id de la foto no existe en esta instancia.
409 no_conectada Una consulta con actualizar=true sin conexión.
409 — La misma Idempotency-Key con otros datos.
422 — Un nombre fuera de 1 a 25 caracteres; una Info de más de 50 (139 solo con la forma clasico); un valor que la opción de privacidad no acepta; un temporizador que no es 0, 86400, 604800 ni 7776000.
422 foto_invalida La foto no es un JPEG listo de esta instancia, pesa más de 2 MB o no mide de 192 a 2048 píxeles por lado.
503 multimedia_deshabilitada Cambiar la foto sin almacenamiento de multimedia configurado.

Los error_code de un comando que falla:

error_code Qué pasó
foto_invalida El servidor rechazó la imagen.
medio_no_disponible La foto ya no existe o no se pudo leer: no salió nada.
sin_identidad Bloquear un contacto del que el motor no conoce su LID.
rechazado_por_whatsapp WhatsApp rechazó el cambio; server_code trae su código.
operacion_fallida El cambio no se pudo hacer; el motor lo reintenta antes de darlo por fallido.
  • La Info cambiada desde el teléfono no avisa a la propia instancia (falla F34, caso A45, 22/10/2026, abierta y de gravedad baja): la instancia no recibe profile.updated, aunque las otras instancias que tienen el número como contacto sí reciben contact.about. Un cambio de nombre sí llega. Consulta el perfil con GET …/profile?actualizar=true cuando necesites el valor vigente. Ver Info propia cambiada desde el teléfono.
  • Borrar la Info deja el campo viejo (caso A45, 21/10/2026): con texto: "" la Info queda en blanco (via: mex, duracion_seg: 0), pero el campo viejo del servidor conserva el texto anterior (clasico: fallo) y una consulta posterior puede devolverlo.
  • La foto solo en 640 × 640 (caso A46, falla F26, corregida el 21/10/2026): el servidor rechazaba con foto_invalida cualquier otra medida; desde la corrección el motor la deja en 640 × 640 antes de mandarla. Ver Otros límites observados.
  • Privacidad y temporizador (casos A47 y A50, 20 y 21/10/2026): cada cambio desde el motor y desde el teléfono llegó como privacy.changed con solo la opción que cambió; el temporizador por defecto cambiado en el teléfono llegó con temporizador_por_defecto_seg (21/10, con la corrección R10). La opción «Stickers» no existe en el cliente normal de Android probado: solo se pudo cambiar desde el motor.
  • Bloqueos (caso A48, falla F28, corregida el 21/10/2026): bloquear y desbloquear desde el motor llega como blocklist.changed con desde: motor y su command_id; desde el teléfono, con desde: whatsapp. Mientras está bloqueado, el contacto no puede escribirle al número.