Saltar al contenido principal

API de supresiones

La API de supresiones gestiona las listas, por espacio de trabajo, de direcciones de email y números de teléfono a los que Joryio no enviará mensajes. Una supresión es un bloqueo estricto: mientras un identificador esté suprimido en un canal, se omite cualquier mensaje a ese identificador en ese canal, sin importar qué campaña o journey intente alcanzarlo.

Esta API refleja la sección Audiencia → Listas de supresión del dashboard. Úsala para auditar quién está suprimido, migrar una lista existente de direcciones inactivas desde otra plataforma, cumplir una solicitud de consentimiento de tus propios sistemas o eliminar una supresión después de resolver un rebote permanente.

Todos los endpoints de esta página son relativos a la URL base: https://api-eu1.joryio.com/api. Consulta el resumen de la API.

Autenticación​

Cada solicitud se autentica con una clave de API que tenga el scope de entregabilidad correspondiente o con una sesión del dashboard (JWT). Los datos de supresión tienen ámbito de espacio de trabajo: una clave solo puede ver y editar las listas de su propio espacio de trabajo.

Authorization: Bearer jry_live_your_api_key_here
Content-Type: application/json

Scopes por endpoint​

El canal determina el scope necesario. Los endpoints de email necesitan email_suppression:*; los de SMS y WhatsApp necesitan sms_suppression:*.

EndpointCanal emailCanal SMS / WhatsApp
GET /suppressionsemail_suppression:readsms_suppression:read
GET /suppressions/{identifier}email_suppression:readsms_suppression:read
POST /suppressionsemail_suppression:writesms_suppression:write
POST /suppressions/hard-bounceemail_suppression:write- (email only)
POST /suppressions/importemail_suppression:writesms_suppression:write
DELETE /suppressions/{identifier}email_suppression:writesms_suppression:write

Conceptos básicos​

Lee esta sección antes de llamar a los endpoints de escritura: el resto de la API solo tiene sentido una vez que está clara la separación entre reason y source.

reason (por qué) frente a source (de dónde procede)​

Cada fila de supresión tiene dos campos independientes:

  • reason: por qué se suprime el identificador. Puede ser unsubscribe, hard_bounce, complaint o manual.
  • source: de dónde procede la supresión. Puede ser delivery, api, import o una fuente de consentimiento como unsubscribe_link.

Son conceptos independientes. Un mismo reason puede provenir de fuentes distintas: un hard_bounce observado por nuestra propia canalización de envío tiene source: "delivery", mientras que un hard_bounce que declares mediante esta API tiene source: "api". El motivo indica su significado de entregabilidad o consentimiento; la fuente te dice en qué medida confiar en él y si cuenta para tus métricas de reputación.

Motivos de consentimiento frente a motivos de entregabilidad​

Los cuatro motivos se dividen en dos familias que se comportan de forma diferente:

FamiliaMotivosSignificado¿Sobrevive a una nueva suscripción?
Consentimientounsubscribe, manualEl destinatario, o tú en su nombre, pidió no recibir comunicaciones.No - una nueva suscripción deja pasar solo a ese contacto (los demás contactos con la dirección siguen bloqueados); DELETE /suppressions la levanta.
Entregabilidadhard_bounce, complaintLa dirección o el número ya no es válido o nos marcó como spam.Sí: persiste incluso si el destinatario vuelve a suscribirse.

Una supresión de entregabilidad es un hecho técnico sobre la dirección, no una preferencia, así que una nueva suscripción del destinatario no la levanta, y tampoco la API, una importación ni el panel. Solo el soporte de Joryio levanta un rebote duro o una queja de spam (o el cambio del contacto a una nueva dirección de email).

No se puede inventar un rebote​

Las rutas de escritura habituales, POST /suppressions y POST /suppressions/import, restringen reason a manual o unsubscribe. No pueden crear una fila hard_bounce ni complaint. Un rebote es algo que la plataforma observa, no algo que un cliente pueda declarar sin más.

La única ruta que puede declarar un rebote es POST /suppressions/hard-bounce y, aun así, la fila lleva source: "api", para que nunca se confunda con un rebote observado por nosotros.

Solo los rebotes reales afectan a tu reputación​

La tasa de rebotes declarada de tu cuenta y las métricas de entregabilidad o reputación cuentan solo los rebotes con source: "delivery", es decir, los que nuestra propia canalización de envío observó en la capa SMTP. Una supresión declarada por API (source: "api") o importada (source: "import") bloquea el envío a ese identificador, pero no eleva tu tasa de rebotes declarada. Así puedes proteger la reputación de tu remitente cargando previamente direcciones que sabes que no sirven, sin contaminar la métrica que quieres proteger.

La supresión sigue a la dirección o el número​

Una supresión se vincula a la dirección de email o el número de teléfono normalizado dentro del espacio de trabajo, no a un registro de contacto. Por eso, una dirección suprimida cubre a todos los contactos duplicados que la compartan. Suprime [email protected] una vez y todos los contactos con esa dirección quedan bloqueados en email en todo el espacio de trabajo.

Los números de teléfono se leen como los guardan los contactos: un número sin código de país (0501234567) se lee en el país predeterminado para números de teléfono del espacio de trabajo, y la supresión se guarda tanto con ese número (+972501234567) como con la forma antigua que pueden tener los registros anteriores, así que bloquea a todos los contactos con ese número. Un número que no es válido en absoluto también se guarda - nunca se descarta.

Un número ambiguo (válido como número nacional del país predeterminado y como número de otro país) se rechaza (400) al añadirlo o quitarlo, y se omite (contado en ambiguous) en una importación masiva, porque una de las lecturas puede ser el número de otra persona: envíalo con el código de país (+...).


Referencia de reason / source​

Valores de reason​

reasonFamiliaCreado por
unsubscribeConsentimientoEnlace de baja del destinatario, POST /suppressions, POST /suppressions/import
manualConsentimientoAcción de operador, POST /suppressions, POST /suppressions/import
hard_bounceEntregabilidadNuestra canalización de envío, POST /suppressions/hard-bounce
complaintEntregabilidadNuestra canalización de envío (bucles de comentarios)

Valores de source​

sourceSignificado¿Cuenta para la reputación?
deliveryObservado por nuestra propia canalización de envío/recepción (rebote real o queja).Sí
apiDeclarado mediante esta API REST.No
importCargado mediante POST /suppressions/import (migración masiva).No
unsubscribe_link (y otras fuentes de consentimiento)Cambio de consentimiento iniciado por el destinatario.No

Listar supresiones​

Devuelve una lista paginada de identificadores suprimidos de un canal.

Endpoint​

GET /suppressions

Parámetros de consulta​

ParámetroTipoPredeterminadoDescripción
channelcadenaemail, sms o whatsapp. Obligatorio.
reasoncadenaFiltro opcional: unsubscribe, hard_bounce, complaint o manual.
limitnúmero100Filas por página, máximo 1000.
offsetnúmero0Desplazamiento de paginación.

Solicitud de ejemplo​

curl -X GET "https://api-eu1.joryio.com/api/suppressions?channel=email&reason=hard_bounce&limit=50" \
-H "Authorization: Bearer jry_live_your_api_key"

Respuesta​

{
"items": [
{
"identifier": "[email protected]",
"channel": "email",
"identifierType": "email",
"reason": "hard_bounce",
"source": "delivery",
"scope": "channel",
"listId": null,
"createdAt": "2026-06-30T12:04:11.000Z"
},
{
"identifier": "[email protected]",
"channel": "email",
"identifierType": "email",
"reason": "unsubscribe",
"source": "unsubscribe_link",
"scope": "group",
"listId": "grp_newsletter",
"createdAt": "2026-07-02T09:20:00.000Z"
}
],
"total": 214,
"limit": 50,
"offset": 0
}

Comprobar un identificador​

Comprueba si un identificador concreto está suprimido en un canal.

Endpoint​

GET /suppressions/{identifier}

El parámetro de ruta {identifier} es la dirección de email o el número de teléfono codificado para URL.

Parámetros de consulta​

ParámetroTipoPredeterminadoDescripción
channelcadenaemail, sms o whatsapp. Obligatorio.

Solicitud de ejemplo​

curl -X GET "https://api-eu1.joryio.com/api/suppressions/[email protected]?channel=email" \
-H "Authorization: Bearer jry_live_your_api_key"

Respuesta: suprimido​

{
"suppressed": true,
"identifier": "[email protected]",
"reason": "hard_bounce",
"source": "delivery",
"scope": "channel",
"listId": null,
"createdAt": "2026-06-30T12:04:11.000Z"
}

Respuesta: no suprimido​

{
"suppressed": false
}

Añadir una supresión​

Añade una supresión por consentimiento o manual. Úsala para respetar una baja que te haya llegado por tus propios sistemas, como un ticket de soporte, una marca del CRM o un cambio en tu propio centro de preferencias.

Endpoint​

POST /suppressions

Cuerpo de la solicitud​

CampoTipoObligatorioDescripción
channelcadenaSíemail, sms o whatsapp.
identifiercadenaSíDirección de email o número de teléfono que se suprimirá.
reasoncadenaNomanual (predeterminado) o unsubscribe. Restringido: se rechaza cualquier otro valor.
scopecadenaNochannel (predeterminado): todas las listas del canal, o group con listId para una sola lista.
Por qué aquí no hay un ámbito de organización

Toda credencial en este endpoint lleva un único espacio de trabajo: una clave de API guarda un workspaceId y una sesión del dashboard actúa dentro de su espacio de trabajo actual. Una supresión a nivel de organización - la que silencia un identificador en todos los espacios de trabajo - no tiene aquí ningún llamante que represente a la organización, así que no puede crearse a través de esta API.

Antes era el valor PREDETERMINADO, lo que significaba que una integración de una sola marca silenciaba un identificador también para las marcas hermanas, sin pedirlo.

Las filas de nivel organización creadas antes de este cambio se siguen leyendo y respetando al enviar. Un DELETE limpia solo las filas de este espacio de trabajo - nunca una fila de nivel organización, porque levantaría el bloqueo también en todos los demás espacios de trabajo. Para levantar una fila antigua de nivel organización, contacta con el soporte de Joryio.

| listId | cadena | No | Obligatorio si scope es group: grupo/lista al que se aplica esta supresión. Debe ser una lista de este espacio de trabajo - un listId ausente, o de otro espacio de trabajo, devuelve 400. |

El source de una fila creada aquí siempre se registra como api. Este endpoint no puede crear un hard_bounce ni una complaint.

Solicitud de ejemplo​

curl -X POST https://api-eu1.joryio.com/api/suppressions \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"channel": "email",
"identifier": "[email protected]",
"reason": "unsubscribe",
"scope": "group",
"listId": "grp_newsletter"
}'

Respuesta​

{
"identifier": "[email protected]",
"channel": "email",
"identifierType": "email",
"reason": "unsubscribe",
"source": "api",
"scope": "group",
"listId": "grp_newsletter",
"createdAt": "2026-07-11T08:15:00.000Z"
}
reason: "unsubscribe" también actualiza los contactos

Con reason: "unsubscribe", todos los contactos del espacio de trabajo con ese email o teléfono también pasan a unsubscribed en el canal (o, con scope: "group", en esa lista), con una entrada de historial. Una supresión manual bloquea el envío sin cambiar el estado propio de los contactos.


Declarar un rebote permanente​

Registra un rebote permanente de una dirección de email. Es la única ruta de API que puede declarar una supresión de entregabilidad, y está separada y restringida de forma intencionada.

Por qué este endpoint está separado​

  • Un rebote es normalmente algo que Joryio observa al enviar, no algo que declare quien llama. Mantener esa declaración fuera de las rutas habituales de añadir/importar evita creaciones accidentales o descuidadas.
  • Como lo estás declarando tú y no observándolo nosotros, la fila lleva source: "api". Bloquea el envío igual que un rebote real, pero nunca se confunde con uno que hayamos visto y no cuenta para tu tasa de rebotes declarada ni tus métricas de reputación de remitente.
  • Es solo para email. No existe equivalente para teléfono: la falta de entrega de SMS/WhatsApp se modela de otra manera.

Endpoint​

POST /suppressions/hard-bounce

Cuerpo de la solicitud​

CampoTipoObligatorioDescripción
identifiercadenaSíDirección de email que tuvo el rebote permanente.

El canal es implícitamente email. La fila se registra con reason: "hard_bounce" y source: "api".

Solicitud de ejemplo​

curl -X POST https://api-eu1.joryio.com/api/suppressions/hard-bounce \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"identifier": "[email protected]"
}'

Respuesta​

{
"identifier": "[email protected]",
"channel": "email",
"identifierType": "email",
"reason": "hard_bounce",
"source": "api",
"scope": "channel",
"listId": null,
"createdAt": "2026-07-11T08:20:00.000Z"
}

Importación masiva​

Carga una lista de supresiones existente, por ejemplo al migrar desde otra plataforma.

Endpoint​

POST /suppressions/import

Cuerpo de la solicitud​

CampoTipoObligatorioDescripción
channelcadenaSíemail, sms o whatsapp.
entriesmatrizSíHasta 5000 objetos, cada uno { identifier, reason? }.

El reason de cada entrada se restringe a manual (predeterminado) o unsubscribe; cualquier otro valor se convierte en manual. Cada fila importada se registra con source: "import". Igual que el endpoint de añadir, la importación no puede crear un rebote ni una queja.

Solicitud de ejemplo​

curl -X POST https://api-eu1.joryio.com/api/suppressions/import \
-H "Authorization: Bearer jry_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"channel": "email",
"entries": [
{ "identifier": "[email protected]" },
{ "identifier": "[email protected]", "reason": "unsubscribe" },
{ "identifier": "[email protected]" }
]
}'

Respuesta​

{
"channel": "email",
"received": 3,
"imported": 3,
"skipped": 0,
"source": "import"
}

Eliminar una supresión (anular supresión)​

Elimina una supresión para que Joryio pueda volver a enviar al identificador.

Endpoint​

DELETE /suppressions/{identifier}

Parámetros de consulta​

ParámetroTipoPredeterminadoDescripción
channelcadenaemail, sms o whatsapp. Obligatorio.
aviso

Esto elimina las filas de supresión de consentimiento (unsubscribe, manual) del identificador en el canal, solo en este espacio de trabajo. Nunca elimina una baja por palabra STOP ni una fila hard_bounce / complaint; esas se informan en kept. Los contactos cuyo propio estado pasó a unsubscribed siguen de baja; vuelve a suscribir un contacto mediante la Subscriptions API.

Solicitud de ejemplo​

curl -X DELETE "https://api-eu1.joryio.com/api/suppressions/[email protected]?channel=email" \
-H "Authorization: Bearer jry_live_your_api_key"

Respuesta​

{
"identifier": "[email protected]",
"removed": 1,
"kept": { "deliverability": 1 }
}

removed es el número de filas de supresión eliminadas; un identificador puede tener a la vez una fila de consentimiento de ámbito de grupo y una fila global de entregabilidad.

kept (presente solo cuando se conservó algo) cuenta las filas que esta API no puede eliminar: stop_keyword (eliminarla levantaría el número para todos los contactos que lo comparten - vuelve a suscribir al contacto concreto mediante la API de suscripciones, o la persona envía START) y deliverability (un rebote duro o una queja de spam - solo el soporte de Joryio lo levanta).

legacy_twin (solo teléfono) cuenta las entradas con la forma local antigua del número (+10612345678 para +31612345678) que se conservaron: solo se levantan cuando el espacio de trabajo tiene un país predeterminado para números de teléfono que las lee exactamente como este número, porque los mismos dígitos locales pueden ser el número de otro país. Un START sigue la misma regla y nunca levanta una entrada así en una baja de toda la organización.


Migrar una lista de supresiones existente​

Cuando pases a Joryio desde otra plataforma de email o SMS, lleva tu lista de supresiones desde el primer día para que tu primer envío no vuelva a contactar direcciones que ya sabes que no sirven o que se dieron de baja.

  1. Importa la lista completa mediante POST /suppressions/import. Las entradas llegan como manual - o unsubscribe si las etiquetas así - con source: "import". Bloquean el envío y protegen la reputación de tu remitente desde el primer envío, sin aumentar tu tasa de rebotes declarada, porque las filas importadas nunca cuentan para la reputación.
  2. Solo si necesitas específicamente que esas direcciones se informen como rebotes, por ejemplo para mantener continua la analítica de rebotes durante la migración, decláralas de una en una con POST /suppressions/hard-bounce. Seguirán llevando source: "api", por lo que bloquean el envío y aparecen como rebotes en la lista de supresiones sin contarse como rebotes observados por nosotros.

Para la mayoría de las migraciones, solo el paso 1 es lo correcto: detiene los envíos y mantiene limpias tus métricas de reputación.


Respuestas de error​

Todos los errores comparten la estructura estándar: no existe un vocabulario separado de códigos de error legibles por máquina; usa el estado HTTP junto con el campo message. Consulta respuesta de error en el resumen de la API. Los errores de validación (400) añaden una matriz errors con un mensaje por cada campo que falla:

{
"statusCode": 400,
"message": "Bad Request Exception",
"timestamp": "2026-07-12T09:00:00.000Z",
"path": "/suppressions",
"errors": [
"reason must be one of the following values: manual, unsubscribe"
]
}

Importante: esta API aplica scopes específicos de cada canal. Una clave de API que solo tiene el scope de SMS recibe un 403 si accede a supresiones de email, y viceversa:

{
"statusCode": 403,
"message": "API key missing required scope 'email_suppression:write' for channel 'email'",
"timestamp": "2026-07-12T09:00:00.000Z",
"path": "/suppressions"
}

Siguientes pasos​