Registrar una restricción de envío
/v1/mail-restrictionsPara desarrolladores
Descripción
Registra una oposición o un bloqueo temporal sobre una dirección de correo. Desde ese momento ningún remitente de la instancia le envía los correos que la restricción cubre, sin importar si el envío viene de la API, del relay SMTP o del panel.
Oposición (objection) | Bloqueo temporal (block) | |
|---|---|---|
| Para qué | El destinatario se opone a recibir correos | Detener todo envío mientras se resuelve una solicitud del destinatario (por ejemplo, una rectificación) |
| Qué envíos detiene | Los que indica scope: masivo, transaccional o ambos | Todos |
| Fecha de término | No acepta | Opcional, en expires_at; al llegar, el bloqueo se levanta automáticamente |
| Cómo se levanta | No se levanta: es permanente | Al llegar expires_at, o desde la Ficha de contacto del panel |
La API no levanta ninguna de las dos. Por eso, en un bloqueo indique siempre expires_at: sin fecha de término, el bloqueo solo se levanta desde el panel.
Cada llamada registra una dirección. No hay una forma por lotes de este endpoint.
Cada registro queda en el log de actividad de la instancia, a nombre de la API key que lo hizo.
Guía relacionada
- Ficha de contacto — Registre desde el panel la oposición o el bloqueo de un contacto.
Autenticación
Authorizationbearer tokenheaderobligatorio- API key con scope
mail:restrict. Formato:Bearer <API_KEY>. X-Instance-Slugstringheaderobligatorio- Identificador de su instancia. Ver Autenticación.
Una API key de Acceso completo incluye el scope mail:restrict. Una API key con Permisos específicos lo tiene solo si se le seleccionó Registrar oposiciones y bloqueos: ningún otro permiso lo incluye, tampoco mail:read.
Request
Query params:
tzstring- Zona horaria IANA (por ejemplo,
America/Santiago) con que se interpreta unexpires_atsin zona horaria. Sin valor se usa la zona de la instancia; si la instancia no tiene zona, UTC. Es la misma regla de los reportes.
Body:
recipientstringobligatorio- Email del destinatario, de 3 a 254 caracteres. Se guarda en minúsculas.
reasonstringobligatorioobjectionoblock.scopearray de string- Qué envíos detiene la oposición:
masivo,transaccionalo ambos. Obligatorio enobjection; no se acepta enblock. expires_atstring (date-time)- Fin del bloqueo, ISO 8601. Con zona horaria se usa tal cual; sin zona se interpreta en la zona de
tzo, en su defecto, en la de la instancia. Solo enblock. Debe ser futura y anterior al 19-01-2038.
Un campo que el body no declara se ignora.
Response
Location: /v1/mail-restrictions/{id}.La respuesta tiene la misma forma que cada elemento de GET /v1/mail-restrictions.
idintegersiempre presente- Identificador de la restricción.
recipientstringsiempre presente- Email del destinatario restringido.
reasonstring | null- Motivo, ver vocabulario.
scopearray de string | null- Qué envíos bloquea la restricción:
masivo,transaccionalo ambos.nullbloquea todo envío. Ver Alcance de una restricción. restricted_atstring (date-time) | null- Fecha y hora en que se aplicó la restricción.
expires_atstring (date-time) | null- Fin previsto de un bloqueo temporal (
reason: block). Al pasar esa fecha, la restricción se levanta automáticamente y pasa a exponerdeleted_at.nullen cualquier otro motivo, y en un bloqueo sin fecha de término. deleted_atstring (date-time) | null- Fecha y hora en que se levantó la restricción.
nullsi sigue vigente.
Errores
| Código | code | Cuándo |
|---|---|---|
| 403 | forbidden / ip_not_allowed | La API key no tiene el scope mail:restrict, o la IP del cliente no está en la lista permitida. |
| 409 | contact_objection_exists | Las oposiciones vigentes de la dirección ya cubren todos los tipos de envío pedidos. Una oposición que agrega un tipo que ninguna cubre sí se registra. |
| 409 | contact_block_exists | La dirección ya tiene un bloqueo temporal vigente: solo puede haber uno por dirección. |
| 422 | validation_error | El body no cumple el contrato. El detalle por campo viene en errors[] (ver errores por campo). |
Valores de errors[].code propios de este endpoint, todos en /expires_at:
code | Cuándo |
|---|---|
expires_at_not_future | La fecha de término no es futura. |
expires_at_nonexistent | La fecha de término, sin zona horaria, cae en una hora que esa zona se salta por el cambio de horario. |
expires_at_out_of_range | La fecha de término es posterior al 19-01-2038. |
expires_at_block_only | Se indicó una fecha de término en una oposición. |
Un scope ausente en objection, o presente en block, responde 422, y en errors[] el error llega con code: "value_error" y pointer: "" (la raíz del body).
Oposición y consentimiento
Una oposición y un rechazo de consentimiento (una regla deny en POST /v1/consent-events) no son lo mismo:
- La oposición detiene todo remitente de los tipos de envío que cubre, tenga o no la instancia el módulo de consentimiento y exija o no el remitente una finalidad.
- Un rechazo de consentimiento es por finalidad: detiene solo los envíos de los remitentes que exigen esa finalidad.
Si el destinatario pide no recibir más correos de la instancia, registre una oposición. Si retira su consentimiento para un fin concreto (por ejemplo, marketing), registre el rechazo de esa finalidad.
- Una oposición es permanente. Ni siquiera el panel la levanta, así que verifique la dirección antes de registrarla.
- Se ve en el listado. El registro aparece de inmediato en
GET /v1/mail-restrictions, conreasonobjectionoblock. - El enlace para volver a suscribirse no levanta una oposición ni un bloqueo: solo levanta las desuscripciones.
curl -X POST "https://$API_HOST/v1/mail-restrictions" \
-H "Authorization: Bearer $API_KEY" \
-H "X-Instance-Slug: $SLUG" \
-H "Content-Type: application/json" \
-d '{
"recipient": "ana@ejemplo.com",
"reason": "block",
"expires_at": "2026-11-20T12:00:00-03:00"
}'
{
"id": 42,
"recipient": "ana@ejemplo.com",
"reason": "block",
"scope": [
"masivo",
"transaccional"
],
"restricted_at": "2026-10-20T15:04:05Z",
"expires_at": "2026-11-20T15:00:00Z",
"deleted_at": null
}