Iniciar sesión Crear cuenta
Referencia de la API

Recibidos y buzones de entrada

Buzones de entrada para recibir correos electrónicos y los endpoints para leer, reenviar y organizar los mensajes recibidos.

Recibe correos electrónicos en Postal: crea buzones de entrada en un dominio tuyo (con recepción activa) o en una dirección generada en in.postal.so, lee los mensajes por la API y recibe un aviso al instante mediante el webhook email.received. Consulta cómo apuntar el MX en Dominios y DNS. Postal acepta mensajes de hasta 25 MB. Los endpoints de esta página exigen una clave de acceso total, y el plan define cuántos buzones de entrada, y qué funciones, tiene el equipo (consulta Límites del plan).

#Listar buzones de entrada

GET/inboxes

Clave de acceso total

Lista los buzones de entrada del equipo con el conteo de mensajes no leídos. Acepta limit, after y before.

Parámetros de consulta

  • limitinteger
    De 1 a 100. Predeterminado: 20.
  • afterstring
    Cursor de la página siguiente.
  • beforestring
    Cursor de la página anterior.
GET /inboxes

cURL

curl https://api.postal.so/inboxes \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "inbox",
      "id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
      "paused_by_plan": false,
      "name": "Suporte",
      "address": "[email protected]",
      "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
      "receiving_address": "[email protected]",
      "catch_all": false,
      "forward_to": ["[email protected]"],
      "can_forward": true,
      "unread_count": 3,
      "last_received_at": "2026-10-24T12:41:09.000000Z",
      "created_at": "2026-10-02T18:20:00.000000Z",
      "reply_name": "Ada do Suporte",
      "view": "list",
      "agent": {
        "enabled": false,
        "instructions": null,
        "actions": [],
        "tone": {
          "formality": 0.3,
          "length": 0.3
        }
      }
    }
  ]
}

#Crear un buzón de entrada

POST/inboxes

Clave de acceso total

Con domain_id, el buzón recibe en local_part@seu-dominio (usa * para recibir cualquier dirección del dominio, un catch-all). Sin domain_id, Postal genera una dirección en in.postal.so, como [email protected]. Los buzones en tu dominio están disponibles en los planes de pago.

Cuerpo de la solicitud

  • namestringobligatorio
    Nombre del buzón, hasta 255 caracteres.
  • domain_idstring
    Dominio tuyo con recepción activa (capabilities.receiving = "enabled").
  • local_partstring
    Parte anterior a la @, hasta 64 caracteres: letras, números, ., +, - y _, o *. Obligatorio con domain_id. Debe empezar por letra o número (o ser solo *).
  • forward_tostring[]
    Hasta 5 direcciones a las que se reenvía automáticamente cada mensaje (no vale para catch-all ni para in.postal.so).

Errores

  • 422validation_error
    Campo no válido, dominio sin recepción activa o dirección ya en uso.
  • 403plan_limit_reached
    El plan no permite más buzones de entrada ni buzones en tu dominio.
POST /inboxes

cURL

curl -X POST https://api.postal.so/inboxes \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Suporte",
    "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
    "local_part": "suporte",
    "forward_to": ["[email protected]"]
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Suporte',
    domain_id: '0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f',
    local_part: 'suporte',
    forward_to: ['[email protected]'],
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "name": "Suporte",
        "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
        "local_part": "suporte",
        "forward_to": ["[email protected]"],
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'name' => 'Suporte',
        'domain_id' => '0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f',
        'local_part' => 'suporte',
        'forward_to' => ['[email protected]'],
    ],
]);

$data = json_decode($response->getBody(), true);
201Respuesta
{
  "object": "inbox",
  "id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "paused_by_plan": false,
  "name": "Suporte",
  "address": "[email protected]",
  "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
  "receiving_address": "[email protected]",
  "catch_all": false,
  "forward_to": ["[email protected]"],
  "can_forward": true,
  "unread_count": 0,
  "last_received_at": null,
  "created_at": "2026-10-02T18:20:00.000000Z",
  "reply_name": "Ada do Suporte",
  "view": "list",
  "agent": {
    "enabled": false,
    "instructions": null,
    "actions": [],
    "tone": {
      "formality": 0.3,
      "length": 0.3
    }
  }
}

#Obtener un buzón de entrada

GET/inboxes/{id}

Clave de acceso total

receiving_address es una dirección en in.postal.so que también entrega en el buzón: reenvía correos electrónicos de cualquier proveedor a ella, sin apuntar el MX de tu dominio (en los buzones en in.postal.so es la propia address). Además de los datos del buzón, trae folders con cuántas conversaciones hay en cada carpeta (inbox, sent, drafts, archive, spam y trash) y cuántas están sin leer.

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
GET /inboxes/{id}

cURL

curl https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "inbox",
  "id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "paused_by_plan": false,
  "name": "Suporte",
  "address": "[email protected]",
  "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
  "receiving_address": "[email protected]",
  "catch_all": false,
  "forward_to": ["[email protected]"],
  "can_forward": true,
  "unread_count": 3,
  "last_received_at": "2026-10-24T12:41:09.000000Z",
  "created_at": "2026-10-02T18:20:00.000000Z",
  "reply_name": "Ada do Suporte",
  "view": "list",
  "agent": {
    "enabled": false,
    "instructions": null,
    "actions": [],
    "tone": {
      "formality": 0.3,
      "length": 0.3
    }
  },
  "folders": {
    "inbox": {
      "threads": 12,
      "unread": 3
    },
    "sent": {
      "threads": 5
    },
    "drafts": {
      "threads": 1
    },
    "archive": {
      "threads": 40,
      "unread": 0
    },
    "spam": {
      "threads": 2,
      "unread": 2
    },
    "trash": {
      "threads": 0,
      "unread": 0
    }
  }
}

#Actualizar un buzón de entrada

PATCH/inboxes/{id}

Clave de acceso total

Cambia el nombre, las direcciones de reenvío, el nombre de respuesta, el diseño y el agente de IA. La dirección del buzón no cambia.

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.

Cuerpo de la solicitud

  • namestring
    Nombre nuevo.
  • forward_tostring[] | null
    Hasta 5 direcciones. null o [] desactiva el reenvío.
  • reply_namestring | null
    Nombre de quien responde: las respuestas salen como "Ada do Suporte" <suporte@…>. Hasta 100 caracteres.
  • viewstring
    Diseño del buzón en el panel: list (predeterminado), compact, board (tablero por etiquetas), split (lista y lectura lado a lado) o cards (tarjetas).
  • agentobject
    El agente de IA del buzón (consulta Agente de IA).
    • enabledboolean
      Activa o desactiva el agente.
    • instructionsstring | null
      Lo que debe hacer el agente, hasta 5.000 caracteres.
    • actionsstring[]
      draft_reply, forward_thread, add_labels, assign_thread, mark_as_spam, archive y move_to_trash.
    • toneobject
      formality (0 profesional, 1 desenfadado) y length (0 conciso, 1 detallado), de 0 a 1.

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
  • 403plan_limit_reached
    El plan no incluye el agente de IA del buzón de entrada (al activar agent.enabled).
  • 403paused_by_plan
    El buzón está en pausa porque supera el límite de buzones de entrada del plan.
  • 404not_found
    El recurso no existe o es de otro equipo.
PATCH /inboxes/{id}

cURL

curl -X PATCH https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "forward_to": ["[email protected]", "[email protected]"]
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    forward_to: ['[email protected]', '[email protected]'],
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "forward_to": ["[email protected]", "[email protected]"],
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->patch('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'forward_to' => ['[email protected]', '[email protected]'],
    ],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "inbox",
  "id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "paused_by_plan": false,
  "name": "Suporte",
  "address": "[email protected]",
  "domain_id": "0199f0e1-2a3b-7c4d-8e5f-6a7b8c9d0e1f",
  "receiving_address": "[email protected]",
  "catch_all": false,
  "forward_to": ["[email protected]", "[email protected]"],
  "can_forward": true,
  "unread_count": 3,
  "last_received_at": "2026-10-24T12:41:09.000000Z",
  "created_at": "2026-10-02T18:20:00.000000Z",
  "reply_name": "Ada do Suporte",
  "view": "list",
  "agent": {
    "enabled": false,
    "instructions": null,
    "actions": [],
    "tone": {
      "formality": 0.3,
      "length": 0.3
    }
  }
}

#Eliminar un buzón de entrada

DELETE/inboxes/{id}

Clave de acceso total

Elimina el buzón y todos los mensajes recibidos en él.

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
DELETE /inboxes/{id}

cURL

curl -X DELETE https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "inbox",
  "id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "deleted": true
}

#Listar correos electrónicos recibidos

GET/emails/receiving

Clave de acceso total

Lista los mensajes recibidos, del más reciente al más antiguo. La lista trae el resumen; el contenido viene en GET /emails/receiving/{id}.

Cada mensaje trae otp_token: {"code", "origin"} cuando el correo electrónico tiene un encabezado OTP-Token válido, o null. Consulta Encabezado OTP-Token.

Parámetros de consulta

  • limitinteger
    De 1 a 100. Predeterminado: 20.
  • afterstring
    Cursor de la página siguiente.
  • beforestring
    Cursor de la página anterior.
  • inbox_idstring
    Solo mensajes de un buzón de entrada.
  • unreadboolean
    Con true, solo los no leídos.
  • searchstring
    Búsqueda en remitente, destinatarios y asunto.

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
GET /emails/receiving

cURL

curl 'https://api.postal.so/emails/receiving?inbox_id=0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f&unread=true' \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/emails/receiving?inbox_id=0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f&unread=true', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/emails/receiving",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    params={
        "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
        "unread": "true",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('emails/receiving', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'query' => [
        'inbox_id' => '0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f',
        'unread' => 'true',
    ],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "received_email",
      "id": "019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
      "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
      "created_at": "2026-10-24T12:41:09.000000Z",
      "from": "Carla Souza <[email protected]>",
      "to": ["[email protected]"],
      "cc": [],
      "subject": "Troca de tamanho do pedido #10482",
      "message_id": "<[email protected]>",
      "read": false,
      "size": 18342,
      "has_attachments": true,
      "otp_token": null
    }
  ]
}

#Obtener un correo electrónico recibido

GET/emails/receiving/{id}

Clave de acceso total

Devuelve el mensaje con HTML, texto, encabezados y adjuntos. Los enlaces de descarga de los adjuntos valen durante 1 hora.

otp_token es {"code", "origin"} cuando el mensaje tiene exactamente un encabezado OTP-Token válido en la sección de encabezados superior, y null en cualquier otro caso (también en los correos electrónicos recibidos antes de esta función). El código no va en el webhook email.received. Consulta Encabezado OTP-Token.

Parámetros de ruta

  • idstringobligatorio
    ID del correo electrónico recibido.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
GET /emails/receiving/{id}

cURL

curl https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "received_email",
  "id": "019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "created_at": "2026-10-24T12:41:09.000000Z",
  "from": "Carla Souza <[email protected]>",
  "to": ["[email protected]"],
  "cc": [],
  "subject": "Troca de tamanho do pedido #10482",
  "message_id": "<[email protected]>",
  "read": false,
  "size": 18342,
  "has_attachments": true,
  "otp_token": null,
  "reply_to": [],
  "html": "<p>Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.</p>",
  "text": "Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.",
  "headers": {
    "date": "Fri, 24 Oct 2026 09:41:07 -0300",
    "mime-version": "1.0"
  },
  "attachments": [
    {
      "id": "019a1c00-3e50-7b6c-8d7e-9f0a1b2c3d4e",
      "filename": "label-photo.jpg",
      "content_type": "image/jpeg",
      "content_id": null,
      "size": 15620,
      "download_url": "https://api.postal.so/received-attachments/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/019a1c00-3e50-7b6c-8d7e-9f0a1b2c3d4e?expires=1761313269&signature=c0de…"
    }
  ]
}

#Marcar como leído o no leído

PATCH/emails/receiving/{id}

Clave de acceso total

Parámetros de ruta

  • idstringobligatorio
    ID del correo electrónico recibido.

Cuerpo de la solicitud

  • readbooleanobligatorio
    true marca como leído; false, como no leído.

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
  • 404not_found
    El recurso no existe o es de otro equipo.
PATCH /emails/receiving/{id}

cURL

curl -X PATCH https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "read": true
  }'

Node.js

const response = await fetch('https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    read: true,
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "read": True,
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->patch('emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'read' => true,
    ],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "received_email",
  "id": "019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "created_at": "2026-10-24T12:41:09.000000Z",
  "from": "Carla Souza <[email protected]>",
  "to": ["[email protected]"],
  "cc": [],
  "subject": "Troca de tamanho do pedido #10482",
  "message_id": "<[email protected]>",
  "read": true,
  "size": 18342,
  "has_attachments": true,
  "otp_token": null
}

#Eliminar un correo electrónico recibido

DELETE/emails/receiving/{id}

Clave de acceso total

Parámetros de ruta

  • idstringobligatorio
    ID del correo electrónico recibido.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
DELETE /emails/receiving/{id}

cURL

curl -X DELETE https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "received_email",
  "id": "019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
  "deleted": true
}

#Mensaje recibido original

GET/emails/receiving/{id}/raw

Clave de acceso total

El mensaje exactamente como llegó al servidor (RFC 5322, text/plain).

Parámetros de ruta

  • idstringobligatorio
    ID del correo electrónico recibido.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
GET /emails/receiving/{id}/raw

cURL

curl https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/raw \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/raw', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const message = await response.text();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/raw",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

message = response.text

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/raw', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$message = (string) $response->getBody();
200Respuesta (text/plain)
Received: from mail.cliente.com.br (mail.cliente.com.br [203.0.113.24])
From: Carla Souza <[email protected]>
To: [email protected]
Subject: Troca de tamanho do pedido #10482
Message-ID: <[email protected]>
…

#Reenviar un correo electrónico recibido

POST/emails/receiving/{id}/forward

Clave de acceso total

Reenvía el mensaje a un máximo de 5 direcciones, en segundo plano. Solo funciona para buzones de entrada de un dominio tuyo verificado (no catch-all), porque el reenvío sale de ese dominio.

Parámetros de ruta

  • idstringobligatorio
    ID del correo electrónico recibido.

Cuerpo de la solicitud

  • tostring[]obligatorio
    De 1 a 5 direcciones.

Errores

  • 422validation_error
    Dirección no válida, o el buzón de entrada no puede reenviar.
  • 404not_found
    El recurso no existe o es de otro equipo.
POST /emails/receiving/{id}/forward

cURL

curl -X POST https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/forward \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": ["[email protected]"]
  }'

Node.js

const response = await fetch('https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/forward', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: ['[email protected]'],
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/forward",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "to": ["[email protected]"],
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('emails/receiving/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/forward', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'to' => ['[email protected]'],
    ],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "received_email",
  "id": "019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
  "forwarded": true
}

#Conversaciones, carpetas y etiquetas

Cada correo recibido entra en una conversación (thread): se une a la conversación a la que responde, mediante los encabezados In-Reply-To y References, que se cotejan con los correos recibidos y con las respuestas enviadas desde el buzón de entrada. Sin referencia, abre una conversación nueva.

  • Carpetas: inbox, archive, spam y trash (campo folder) y sent (conversaciones con una respuesta enviada desde el buzón de entrada, fuera de la papelera). Un mensaje nuevo en una conversación archivada la devuelve a la Bandeja de entrada; en Spam y en la Papelera se queda donde está.
  • Las etiquetas son por buzón de entrada, tienen color y agrupan conversaciones. Una conversación puede tener varias.
  • Asignación: una conversación puede asignarse a un miembro del equipo (assigned_to, el id del usuario en tu cuenta).
  • Enviar: responder y abrir conversaciones nuevas siguen el mismo camino que POST /emails: el dominio debe estar verificado y el envío cuenta en la cuota.

#Adjuntos al escribir desde el buzón de entrada

Responder, enviar un mensaje nuevo, crear y actualizar un borrador y enviar un borrador aceptan attachments: [{"filename", "content", "content_type"}], con el contenido en Base64.

  • Límites: hasta 10 archivos por mensaje o borrador, filename de hasta 255 caracteres y 40 MB en total. La validación es la misma que la de POST /emails: un Base64 no válido o un total por encima del límite devuelven 422 validation_error.
  • Los borradores conservan los archivos hasta que se envían o se eliminan (eliminar el borrador, o la conversación definitivamente, elimina los archivos). El contenido nunca vuelve por la API: el borrador lista attachments: [{"id", "filename", "content_type", "size"}].
  • Actualizar un borrador: si el PATCH incluye attachments, la lista se sustituye. Para conservar un adjunto que el borrador ya tiene, envía solo su id ({"id": "…"}); los elementos con filename y content se añaden y lo que quede fuera se elimina. Sin el campo, los adjuntos no cambian; [] elimina todos.
  • Los mensajes enviados listan los adjuntos en attachments (id, filename, content_type, content_id, size y download_url, un enlace firmado que vale por 1 hora), con el mismo formato que los adjuntos recibidos.

#Listar conversaciones

GET/inboxes/{id}/threads

Clave de acceso total

Lista las conversaciones de una carpeta, de la actividad más reciente a la más antigua.

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.

Parámetros de consulta

  • folderstring
    inbox (predeterminado), sent, archive, spam o trash.
  • labelstring
    Solo conversaciones con esta etiqueta.
  • unlabeledboolean
    Con true, solo conversaciones sin etiqueta.
  • assigned_tostring
    ID de un miembro, me (tú) o none (sin responsable).
  • unreadboolean
    Con true, solo los no leídos.
  • searchstring
    Busca en el asunto, el fragmento y los remitentes.
  • limitinteger
    De 1 a 100. Predeterminado: 20.
  • afterstring
    ID de la conversación para la página siguiente.
  • beforestring
    ID de la conversación para la página anterior.

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
  • 404not_found
    El recurso no existe o es de otro equipo.
GET /inboxes/{id}/threads

cURL

curl 'https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads?folder=inbox&unread=true' \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads?folder=inbox&unread=true', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    params={
        "folder": "inbox",
        "unread": "true",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'query' => [
        'folder' => 'inbox',
        'unread' => 'true',
    ],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "thread",
      "id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
      "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
      "subject": "Troca de tamanho do pedido #10482",
      "folder": "inbox",
      "read": false,
      "unread_count": 1,
      "message_count": 2,
      "snippet": "Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.",
      "participants": [
        {
          "name": "Carla Souza",
          "email": "[email protected]",
          "is_me": false
        },
        {
          "name": "Ada do Suporte",
          "email": "[email protected]",
          "is_me": true
        }
      ],
      "has_attachments": true,
      "has_draft": false,
      "has_sent": true,
      "last_message_at": "2026-10-24T13:02:11.000000Z",
      "created_at": "2026-10-24T12:41:09.000000Z",
      "assigned_to": {
        "id": 12,
        "name": "Carlos Lima",
        "email": "[email protected]",
        "avatar_url": null
      },
      "labels": [
        {
          "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
          "name": "Importante",
          "color": "orange"
        }
      ]
    }
  ]
}

#Obtener una conversación

GET/inboxes/{id}/threads/{thread_id}

Clave de acceso total

Devuelve la conversación con todos los mensajes, del más antiguo al más nuevo (recibidos y enviados, con el mismo formato) y los borradores. Los enlaces de descarga de los adjuntos, recibidos y enviados, valen por 1 hora.

Los mensajes recibidos (direction: "received") incluyen otp_token, con el código del encabezado OTP-Token o null, como en GET /emails/receiving/{id}. Consulta Encabezado OTP-Token.

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.
  • thread_idstringobligatorio
    ID de la conversación.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
GET /inboxes/{id}/threads/{thread_id}

cURL

curl https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "thread",
  "id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "subject": "Troca de tamanho do pedido #10482",
  "folder": "inbox",
  "read": false,
  "unread_count": 1,
  "message_count": 2,
  "snippet": "Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.",
  "participants": [
    {
      "name": "Carla Souza",
      "email": "[email protected]",
      "is_me": false
    },
    {
      "name": "Ada do Suporte",
      "email": "[email protected]",
      "is_me": true
    }
  ],
  "has_attachments": true,
  "has_draft": false,
  "has_sent": true,
  "last_message_at": "2026-10-24T13:02:11.000000Z",
  "created_at": "2026-10-24T12:41:09.000000Z",
  "assigned_to": {
    "id": 12,
    "name": "Carlos Lima",
    "email": "[email protected]",
    "avatar_url": null
  },
  "labels": [
    {
      "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
      "name": "Importante",
      "color": "orange"
    }
  ],
  "messages": [
    {
      "object": "thread_message",
      "id": "019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d",
      "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
      "direction": "received",
      "from": "Carla Souza <[email protected]>",
      "to": ["[email protected]"],
      "cc": [],
      "bcc": [],
      "subject": "Troca de tamanho do pedido #10482",
      "message_id": "<[email protected]>",
      "created_at": "2026-10-24T12:41:09.000000Z",
      "read": false,
      "status": null,
      "html": "<p>Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.</p>",
      "text": "Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.",
      "otp_token": null,
      "attachments": []
    },
    {
      "object": "thread_message",
      "id": "019a1c03-2d43-7a54-9b65-3c4d5e6f7081",
      "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
      "direction": "sent",
      "from": "\"Ada do Suporte\" <[email protected]>",
      "to": ["[email protected]"],
      "cc": [],
      "bcc": [],
      "subject": "Re: Troca de tamanho do pedido #10482",
      "message_id": "<[email protected]>",
      "created_at": "2026-10-24T13:02:11.000000Z",
      "read": true,
      "status": "queued",
      "html": null,
      "text": "Oi, Carla! Já separamos o tamanho M para você.",
      "attachments": [
        {
          "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
          "filename": "tabela-de-medidas.pdf",
          "content_type": "application/pdf",
          "content_id": null,
          "size": 48213,
          "download_url": "https://api.postal.so/attachments/019a1c03-2d43-7a54-9b65-3c4d5e6f7081/019a1c05-4f65-7c76-9d87-5e6f70819203?expires=1761314531&signature=9a7e…"
        }
      ]
    }
  ],
  "drafts": []
}

#Actualizar una conversación

PATCH/inboxes/{id}/threads/{thread_id}

Clave de acceso total

Mueve de carpeta, marca como leída o no leída, asigna a un miembro y cambia las etiquetas. Todos los campos son opcionales.

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.
  • thread_idstringobligatorio
    ID de la conversación.

Cuerpo de la solicitud

  • folderstring
    inbox, archive, spam o trash.
  • readboolean
    true marca todos los mensajes recibidos como leídos; false marca el último como no leído.
  • assigned_tointeger | null
    ID de un miembro del equipo, o null para quitar al responsable.
  • labelsstring[]
    Sustituye las etiquetas de la conversación.
  • add_labelsstring[]
    Añade etiquetas.
  • remove_labelsstring[]
    Quita etiquetas.

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
  • 404not_found
    El recurso no existe o es de otro equipo.
PATCH /inboxes/{id}/threads/{thread_id}

cURL

curl -X PATCH https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "folder": "archive",
    "add_labels": ["019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f"],
    "read": true
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    folder: 'archive',
    add_labels: ['019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f'],
    read: true,
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "folder": "archive",
        "add_labels": ["019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f"],
        "read": True,
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->patch('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'folder' => 'archive',
        'add_labels' => ['019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f'],
        'read' => true,
    ],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "thread",
  "id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "subject": "Troca de tamanho do pedido #10482",
  "folder": "archive",
  "read": true,
  "unread_count": 0,
  "message_count": 2,
  "snippet": "Oi! Gostaria de trocar a camiseta do pedido #10482 pelo tamanho M.",
  "participants": [
    {
      "name": "Carla Souza",
      "email": "[email protected]",
      "is_me": false
    },
    {
      "name": "Ada do Suporte",
      "email": "[email protected]",
      "is_me": true
    }
  ],
  "has_attachments": true,
  "has_draft": false,
  "has_sent": true,
  "last_message_at": "2026-10-24T13:02:11.000000Z",
  "created_at": "2026-10-24T12:41:09.000000Z",
  "assigned_to": {
    "id": 12,
    "name": "Carlos Lima",
    "email": "[email protected]",
    "avatar_url": null
  },
  "labels": [
    {
      "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
      "name": "Importante",
      "color": "orange"
    }
  ]
}

#Eliminar una conversación

DELETE/inboxes/{id}/threads/{thread_id}

Clave de acceso total

Mueve la conversación a la papelera. Si ya está en la papelera, la elimina definitivamente, junto con los mensajes recibidos (las respuestas enviadas permanecen en el registro de correos).

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.
  • thread_idstringobligatorio
    ID de la conversación.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
DELETE /inboxes/{id}/threads/{thread_id}

cURL

curl -X DELETE https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "thread",
  "id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "deleted": true
}

#Actuar sobre varias conversaciones

POST/inboxes/{id}/threads/batch

Clave de acceso total

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.

Cuerpo de la solicitud

  • idsstring[]obligatorio
    De 1 a 100 IDs de conversaciones.
  • actionstringobligatorio
    read, unread, archive, spam, trash, inbox, delete, add_label, remove_label o assign.
  • label_idstring
    Obligatorio en add_label y remove_label.
  • assigned_tointeger | null
    En assign: el miembro, o null.

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
  • 404not_found
    El recurso no existe o es de otro equipo.
POST /inboxes/{id}/threads/batch

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/batch \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "ids": ["019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e"],
    "action": "archive"
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/batch', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    ids: ['019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e'],
    action: 'archive',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/batch",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "ids": ["019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e"],
        "action": "archive",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/batch', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'ids' => ['019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e'],
        'action' => 'archive',
    ],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "batch",
  "updated": 1
}

#Responder en una conversación

POST/inboxes/{id}/threads/{thread_id}/reply

Clave de acceso total

Envía una respuesta desde la dirección del buzón de entrada (con el reply_name), en la misma conversación, con In-Reply-To y References correctos. Sin to, responde al Reply-To (o al remitente) del último mensaje recibido; sin subject, usa Re: …. Requiere text o html y un dominio verificado. Acepta adjuntos.

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.
  • thread_idstringobligatorio
    ID de la conversación.

Cuerpo de la solicitud

  • textstring
    Texto de la respuesta.
  • htmlstring
    HTML de la respuesta.
  • tostring[]
    Destinatários.
  • ccstring[]
    Con copia.
  • bccstring[]
    Con copia oculta.
  • subjectstring
    Assunto.
  • draft_idstring
    Borrador que envía esta respuesta (se elimina).
  • attachmentsobject[]
    Adjuntos, hasta 10 archivos y 40 MB en total, con las mismas reglas que POST /emails. Aquí solo se admiten archivos enviados en el propio cuerpo (sin path ni content_id).
    • filenamestringobligatorio
      Nombre del archivo, hasta 255 caracteres.
    • contentstringobligatorio
      Contenido del archivo en Base64.
    • content_typestring
      Tipo MIME, como application/pdf. Si se omite, se deduce de la extensión.

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
  • 404not_found
    El recurso no existe o es de otro equipo.
  • 403validation_error
    El dominio del buzón de entrada no está verificado.
  • 403sending_paused
    Los envíos del equipo están pausados por una tasa alta de rebotes o quejas.
  • 403paused_by_plan
    El dominio del buzón de entrada está pausado por el plan.
  • 403plan_limit_reached
    Los adjuntos superan el límite del plan.
  • 429daily_quota_exceeded
    La cuota de envío del plan se agotó (también monthly_quota_exceeded).
POST /inboxes/{id}/threads/{thread_id}/reply

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/reply \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Oi, Carla! Já separamos o tamanho M para você.",
    "attachments": [
      {
        "filename": "tabela-de-medidas.pdf",
        "content": "JVBERi0xLjQK…",
        "content_type": "application/pdf"
      }
    ]
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/reply', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    text: 'Oi, Carla! Já separamos o tamanho M para você.',
    attachments: [
      {
        filename: 'tabela-de-medidas.pdf',
        content: 'JVBERi0xLjQK…',
        content_type: 'application/pdf',
      },
    ],
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/reply",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "text": "Oi, Carla! Já separamos o tamanho M para você.",
        "attachments": [
            {
                "filename": "tabela-de-medidas.pdf",
                "content": "JVBERi0xLjQK…",
                "content_type": "application/pdf",
            },
        ],
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/reply', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'text' => 'Oi, Carla! Já separamos o tamanho M para você.',
        'attachments' => [
            [
                'filename' => 'tabela-de-medidas.pdf',
                'content' => 'JVBERi0xLjQK…',
                'content_type' => 'application/pdf',
            ],
        ],
    ],
]);

$data = json_decode($response->getBody(), true);
201Respuesta
{
  "object": "thread_message",
  "id": "019a1c03-2d43-7a54-9b65-3c4d5e6f7081",
  "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "direction": "sent",
  "from": "\"Ada do Suporte\" <[email protected]>",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "subject": "Re: Troca de tamanho do pedido #10482",
  "message_id": "<[email protected]>",
  "created_at": "2026-10-24T13:02:11.000000Z",
  "read": true,
  "status": "queued",
  "html": null,
  "text": "Oi, Carla! Já separamos o tamanho M para você.",
  "attachments": [
    {
      "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
      "filename": "tabela-de-medidas.pdf",
      "content_type": "application/pdf",
      "content_id": null,
      "size": 48213,
      "download_url": "https://api.postal.so/attachments/019a1c03-2d43-7a54-9b65-3c4d5e6f7081/019a1c05-4f65-7c76-9d87-5e6f70819203?expires=1761314531&signature=9a7e…"
    }
  ]
}

#Enviar un mensaje nuevo

POST/inboxes/{id}/messages

Clave de acceso total

Envía un mensaje nuevo desde el buzón de entrada. Abre una conversación (thread_id en la respuesta), que aparece en la carpeta sent. Acepta adjuntos.

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.

Cuerpo de la solicitud

  • tostring[]obligatorio
    Destinatários.
  • subjectstringobligatorio
    Assunto.
  • textstring
    Texto.
  • htmlstring
    HTML.
  • ccstring[]
    Con copia.
  • bccstring[]
    Con copia oculta.
  • attachmentsobject[]
    Adjuntos, hasta 10 archivos y 40 MB en total, con las mismas reglas que POST /emails. Aquí solo se admiten archivos enviados en el propio cuerpo (sin path ni content_id).
    • filenamestringobligatorio
      Nombre del archivo, hasta 255 caracteres.
    • contentstringobligatorio
      Contenido del archivo en Base64.
    • content_typestring
      Tipo MIME, como application/pdf. Si se omite, se deduce de la extensión.

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
  • 404not_found
    El recurso no existe o es de otro equipo.
  • 403validation_error
    El dominio del buzón de entrada no está verificado.
  • 403sending_paused
    Los envíos del equipo están en pausa.
  • 403paused_by_plan
    El dominio del buzón de entrada está pausado por el plan.
  • 429daily_quota_exceeded
    La cuota de envío del plan se agotó (también monthly_quota_exceeded).
POST /inboxes/{id}/messages

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/messages \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": ["[email protected]"],
    "subject": "Seu pedido foi enviado",
    "text": "Oi, Carla! Seu pedido já saiu."
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/messages', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: ['[email protected]'],
    subject: 'Seu pedido foi enviado',
    text: 'Oi, Carla! Seu pedido já saiu.',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/messages",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "to": ["[email protected]"],
        "subject": "Seu pedido foi enviado",
        "text": "Oi, Carla! Seu pedido já saiu.",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/messages', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'to' => ['[email protected]'],
        'subject' => 'Seu pedido foi enviado',
        'text' => 'Oi, Carla! Seu pedido já saiu.',
    ],
]);

$data = json_decode($response->getBody(), true);
201Respuesta
{
  "object": "thread_message",
  "id": "019a1c03-2d43-7a54-9b65-3c4d5e6f7081",
  "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "direction": "sent",
  "from": "\"Ada do Suporte\" <[email protected]>",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "subject": "Re: Troca de tamanho do pedido #10482",
  "message_id": "<[email protected]>",
  "created_at": "2026-10-24T13:02:11.000000Z",
  "read": true,
  "status": "queued",
  "html": null,
  "text": "Oi, Carla! Já separamos o tamanho M para você.",
  "attachments": [
    {
      "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
      "filename": "tabela-de-medidas.pdf",
      "content_type": "application/pdf",
      "content_id": null,
      "size": 48213,
      "download_url": "https://api.postal.so/attachments/019a1c03-2d43-7a54-9b65-3c4d5e6f7081/019a1c05-4f65-7c76-9d87-5e6f70819203?expires=1761314531&signature=9a7e…"
    }
  ]
}

#Traducir un mensaje

POST/inboxes/{id}/threads/{thread_id}/messages/{message_id}/translate

Clave de acceso total

Traduce un mensaje de la conversación (recibido o enviado). Es asíncrono, como las demás solicitudes de IA: la respuesta es una tarea; consulta GET /ai/tasks/{id} hasta que esté lista. Usa 1 crédito de IA. El resultado es {"object": "ai_translation", "text", "subject", "from_language", "to_language", "credits"}.

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.
  • thread_idstringobligatorio
    ID de la conversación.
  • message_idstringobligatorio
    ID del mensaje.

Cuerpo de la solicitud

  • tostringobligatorio
    Idioma de destino: pt-BR, en, es, fr, de, it, ja, zh, ko, ru, nl o ar.
  • fromstring
    Idioma original. Si se omite, la IA lo detecta.

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
  • 404not_found
    El recurso no existe o es de otro equipo.
  • 429ai_credits_exhausted
    Los créditos de IA del mes se agotaron.
POST /inboxes/{id}/threads/{thread_id}/messages/{message_id}/translate

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/messages/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/translate \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to": "en"
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/messages/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/translate', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    to: 'en',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/messages/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/translate",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "to": "en",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/threads/019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e/messages/019a1c00-3e4f-7a5b-9c6d-7e8f9a0b1c2d/translate', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'to' => 'en',
    ],
]);

$data = json_decode($response->getBody(), true);
202Respuesta
{
  "object": "ai_task",
  "id": "019a1c04-3e54-7b65-8c76-4d5e6f708192",
  "status": "pending"
}

#Listar etiquetas

GET/inboxes/{id}/labels

Clave de acceso total

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
GET /inboxes/{id}/labels

cURL

curl https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "inbox_label",
      "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
      "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
      "name": "Importante",
      "color": "orange",
      "thread_count": 2
    }
  ]
}

#Crear una etiqueta

POST/inboxes/{id}/labels

Clave de acceso total

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.

Cuerpo de la solicitud

  • namestringobligatorio
    Hasta 50 caracteres, único en el buzón de entrada.
  • colorstring
    cyan, teal, green, lime, yellow, orange, indigo, purple, pink, brown o gray (predeterminado).

Errores

  • 422validation_error
    Nombre repetido o color no válido.
  • 404not_found
    El recurso no existe o es de otro equipo.
POST /inboxes/{id}/labels

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Importante",
    "color": "orange"
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    name: 'Importante',
    color: 'orange',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "name": "Importante",
        "color": "orange",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'name' => 'Importante',
        'color' => 'orange',
    ],
]);

$data = json_decode($response->getBody(), true);
201Respuesta
{
  "object": "inbox_label",
  "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "name": "Importante",
  "color": "orange",
  "thread_count": 0
}

#Actualizar una etiqueta

PATCH/inboxes/{id}/labels/{label_id}

Clave de acceso total

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.
  • label_idstringobligatorio
    ID de la etiqueta.

Cuerpo de la solicitud

  • namestring
    Nombre nuevo.
  • colorstring
    Nuevo color.

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
  • 404not_found
    El recurso no existe o es de otro equipo.
PATCH /inboxes/{id}/labels/{label_id}

cURL

curl -X PATCH https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "color": "pink"
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    color: 'pink',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "color": "pink",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->patch('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'color' => 'pink',
    ],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "inbox_label",
  "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "name": "Importante",
  "color": "pink",
  "thread_count": 2
}

#Eliminar una etiqueta

DELETE/inboxes/{id}/labels/{label_id}

Clave de acceso total

Quita la etiqueta de todas las conversaciones. Las conversaciones permanecen.

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.
  • label_idstringobligatorio
    ID de la etiqueta.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
DELETE /inboxes/{id}/labels/{label_id}

cURL

curl -X DELETE https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/labels/019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "inbox_label",
  "id": "019a1c01-0b21-7e32-9f43-1a2b3c4d5e6f",
  "deleted": true
}

#Listar borradores

GET/inboxes/{id}/drafts

Clave de acceso total

Borradores del buzón de entrada, del más reciente al más antiguo, con la lista de attachments de cada uno (sin el contenido). Los que sugiere el agente de IA tienen source: "agent".

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.

Parámetros de consulta

  • thread_idstring
    Solo borradores de una conversación.
  • limitinteger
    De 1 a 100. Predeterminado: 20.
  • afterstring
    Cursor de la página siguiente.
  • beforestring
    Cursor de la página anterior.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
GET /inboxes/{id}/drafts

cURL

curl 'https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts?thread_id=019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e' \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts?thread_id=019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    params={
        "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'query' => [
        'thread_id' => '019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e',
    ],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "list",
  "has_more": false,
  "data": [
    {
      "object": "draft",
      "id": "019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
      "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
      "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
      "source": "user",
      "to": ["[email protected]"],
      "cc": [],
      "bcc": [],
      "subject": "Re: Troca de tamanho do pedido #10482",
      "text": "Oi, Carla! Já separamos o tamanho M para você.",
      "html": null,
      "attachments": [
        {
          "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
          "filename": "tabela-de-medidas.pdf",
          "content_type": "application/pdf",
          "size": 48213
        }
      ],
      "created_by": {
        "id": 12,
        "name": "Carlos Lima"
      },
      "created_at": "2026-10-24T13:00:00.000000Z",
      "updated_at": "2026-10-24T13:01:30.000000Z"
    }
  ]
}

#Crear un borrador

POST/inboxes/{id}/drafts

Clave de acceso total

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.

Cuerpo de la solicitud

  • thread_idstring
    Conversación a la que responde el borrador. Sin ella, es un mensaje nuevo.
  • tostring[]
    Destinatários.
  • ccstring[]
    Con copia.
  • bccstring[]
    Con copia oculta.
  • subjectstring
    Assunto.
  • textstring
    Texto.
  • htmlstring
    HTML.
  • attachmentsobject[]
    Adjuntos del borrador, hasta 10 archivos y 40 MB en total. Consulta Adjuntos.
    • filenamestring
      Nombre del archivo, hasta 255 caracteres. Obligatorio sin id.
    • contentstring
      Contenido del archivo en Base64. Obligatorio sin id.
    • content_typestring
      Tipo MIME. Si se omite, se deduce de la extensión.
    • idstring
      Solo al actualizar: ID de un adjunto que el borrador ya tiene, para conservarlo (sin reenviar el contenido).

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
  • 404not_found
    El recurso no existe o es de otro equipo.
POST /inboxes/{id}/drafts

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
    "to": ["[email protected]"],
    "text": "Oi, Carla!"
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    thread_id: '019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e',
    to: ['[email protected]'],
    text: 'Oi, Carla!',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
        "to": ["[email protected]"],
        "text": "Oi, Carla!",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'thread_id' => '019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e',
        'to' => ['[email protected]'],
        'text' => 'Oi, Carla!',
    ],
]);

$data = json_decode($response->getBody(), true);
201Respuesta
{
  "object": "draft",
  "id": "019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "source": "user",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "subject": "Re: Troca de tamanho do pedido #10482",
  "text": "Oi, Carla! Já separamos o tamanho M para você.",
  "html": null,
  "attachments": [
    {
      "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
      "filename": "tabela-de-medidas.pdf",
      "content_type": "application/pdf",
      "size": 48213
    }
  ],
  "created_by": {
    "id": 12,
    "name": "Carlos Lima"
  },
  "created_at": "2026-10-24T13:00:00.000000Z",
  "updated_at": "2026-10-24T13:01:30.000000Z"
}

#Obtener un borrador

GET/inboxes/{id}/drafts/{draft_id}

Clave de acceso total

Devuelve el borrador con el texto, el HTML y la lista de attachments (sin el contenido de los archivos). created_by es la persona que lo escribió, o null cuando fue el agente de IA (source: "agent").

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.
  • draft_idstringobligatorio
    ID del borrador.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
GET /inboxes/{id}/drafts/{draft_id}

cURL

curl https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70 \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70', {
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.get(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->get('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "draft",
  "id": "019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "source": "user",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "subject": "Re: Troca de tamanho do pedido #10482",
  "text": "Oi, Carla! Já separamos o tamanho M para você.",
  "html": null,
  "attachments": [
    {
      "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
      "filename": "tabela-de-medidas.pdf",
      "content_type": "application/pdf",
      "size": 48213
    }
  ],
  "created_by": {
    "id": 12,
    "name": "Carlos Lima"
  },
  "created_at": "2026-10-24T13:00:00.000000Z",
  "updated_at": "2026-10-24T13:01:30.000000Z"
}

#Actualizar un borrador

PATCH/inboxes/{id}/drafts/{draft_id}

Clave de acceso total

Acepta los mismos campos que la creación, salvo thread_id. Con attachments, la lista de adjuntos se sustituye: envía {"id"} para conservar un adjunto que el borrador ya tiene y filename con content para añadir otro (consulta Adjuntos).

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.
  • draft_idstringobligatorio
    ID del borrador.

Cuerpo de la solicitud

  • tostring[]
    Destinatários.
  • ccstring[]
    Con copia.
  • bccstring[]
    Con copia oculta.
  • subjectstring
    Assunto.
  • textstring
    Texto.
  • htmlstring
    HTML.
  • attachmentsobject[]
    Adjuntos del borrador, hasta 10 archivos y 40 MB en total. Consulta Adjuntos.
    • filenamestring
      Nombre del archivo, hasta 255 caracteres. Obligatorio sin id.
    • contentstring
      Contenido del archivo en Base64. Obligatorio sin id.
    • content_typestring
      Tipo MIME. Si se omite, se deduce de la extensión.
    • idstring
      Solo al actualizar: ID de un adjunto que el borrador ya tiene, para conservarlo (sin reenviar el contenido).

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
  • 404not_found
    El recurso no existe o es de otro equipo.
PATCH /inboxes/{id}/drafts/{draft_id}

cURL

curl -X PATCH https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70 \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "Oi, Carla! Já separamos o tamanho M."
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70', {
  method: 'PATCH',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    text: 'Oi, Carla! Já separamos o tamanho M.',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.patch(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "text": "Oi, Carla! Já separamos o tamanho M.",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->patch('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'text' => 'Oi, Carla! Já separamos o tamanho M.',
    ],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "draft",
  "id": "019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
  "inbox_id": "0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f",
  "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "source": "user",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "subject": "Re: Troca de tamanho do pedido #10482",
  "text": "Oi, Carla! Já separamos o tamanho M.",
  "html": null,
  "attachments": [
    {
      "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
      "filename": "tabela-de-medidas.pdf",
      "content_type": "application/pdf",
      "size": 48213
    }
  ],
  "created_by": {
    "id": 12,
    "name": "Carlos Lima"
  },
  "created_at": "2026-10-24T13:00:00.000000Z",
  "updated_at": "2026-10-24T13:01:30.000000Z"
}

#Enviar un borrador

POST/inboxes/{id}/drafts/{draft_id}/send

Clave de acceso total

Envía el borrador (como respuesta, si pertenece a una conversación; como mensaje nuevo, si no) con sus adjuntos y lo elimina, junto con sus archivos.

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.
  • draft_idstringobligatorio
    ID del borrador.

Errores

  • 422validation_error
    Algún campo no es válido. El mensaje indica cuál.
  • 404not_found
    El recurso no existe o es de otro equipo.
  • 403validation_error
    El dominio del buzón de entrada no está verificado.
  • 403sending_paused
    Los envíos del equipo están en pausa.
  • 429daily_quota_exceeded
    La cuota de envío del plan se agotó (también monthly_quota_exceeded).
POST /inboxes/{id}/drafts/{draft_id}/send

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70/send \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70/send', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70/send",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70/send', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "thread_message",
  "id": "019a1c03-2d43-7a54-9b65-3c4d5e6f7081",
  "thread_id": "019a1c00-5a10-7d11-8e22-0f1a2b3c4d5e",
  "direction": "sent",
  "from": "\"Ada do Suporte\" <[email protected]>",
  "to": ["[email protected]"],
  "cc": [],
  "bcc": [],
  "subject": "Re: Troca de tamanho do pedido #10482",
  "message_id": "<[email protected]>",
  "created_at": "2026-10-24T13:02:11.000000Z",
  "read": true,
  "status": "queued",
  "html": null,
  "text": "Oi, Carla! Já separamos o tamanho M para você.",
  "attachments": [
    {
      "id": "019a1c05-4f65-7c76-9d87-5e6f70819203",
      "filename": "tabela-de-medidas.pdf",
      "content_type": "application/pdf",
      "content_id": null,
      "size": 48213,
      "download_url": "https://api.postal.so/attachments/019a1c03-2d43-7a54-9b65-3c4d5e6f7081/019a1c05-4f65-7c76-9d87-5e6f70819203?expires=1761314531&signature=9a7e…"
    }
  ]
}

#Eliminar un borrador

DELETE/inboxes/{id}/drafts/{draft_id}

Clave de acceso total

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.
  • draft_idstringobligatorio
    ID del borrador.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
DELETE /inboxes/{id}/drafts/{draft_id}

cURL

curl -X DELETE https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70 \
  -H "Authorization: Bearer $POSTAL_API_KEY"

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70', {
  method: 'DELETE',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
  },
});

const data = await response.json();

Python

import os
import requests

response = requests.delete(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->delete('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/drafts/019a1c02-1c32-7f43-8a54-2b3c4d5e6f70', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
]);

$data = json_decode($response->getBody(), true);
200Respuesta
{
  "object": "draft",
  "id": "019a1c02-1c32-7f43-8a54-2b3c4d5e6f70",
  "deleted": true
}

#Agente de IA

Con el agente activado (agent.enabled en PATCH /inboxes/{id}), una IA lee cada correo nuevo, sigue tus instructions y el tone, y solo puede hacer lo que figure en actions: sugerir una respuesta (draft_reply), aplicar etiquetas y crear las que falten (add_labels), asignar (assign_thread), archivar (archive), marcar como spam (mark_as_spam), mover a la papelera (move_to_trash) y reenviar (forward_thread).

  • Nunca envía nada por su cuenta: las respuestas se convierten en borradores (source: "agent") para que una persona los revise y los envíe.
  • Solo reenvía a direcciones escritas en las instrucciones.
  • Usa 1 crédito de IA por correo. No hace nada sin créditos, con el buzón de entrada o el agente desactivados, ni con correos automáticos (Auto-Submitted) o enviados por el propio buzón de entrada.
  • Para escribir buenas instructions, usa POST /inboxes/{id}/agent/improve-instructions, que reescribe tu borrador con más claridad.

#Mejorar las instrucciones del agente

POST/inboxes/{id}/agent/improve-instructions

Clave de acceso total

Reescribe el borrador de las instrucciones del agente para que sea más claro y específico, en el mismo idioma y con la misma intención, sin inventar datos: las direcciones de correo, los precios, los horarios y las reglas quedan como tú los escribiste. No guarda nada: guarda el resultado con PATCH /inboxes/{id} (agent.instructions) después de revisarlo.

Es asíncrono, como las demás solicitudes de IA: la respuesta es una tarea (ai_task) y el resultado llega por GET /ai/tasks/{id} como {"object": "agent_instructions", "instructions", "task_id", "status": "completed", "credits"}. Usa 1 crédito de IA.

Parámetros de ruta

  • idstringobligatorio
    ID del buzón de entrada.

Cuerpo de la solicitud

  • instructionsstringobligatorio
    El borrador de las instrucciones, de 1 a 4.000 caracteres.

Errores

  • 404not_found
    El recurso no existe o es de otro equipo.
  • 422validation_error
    Campo ausente o fuera de los límites.
  • 429ai_credits_exhausted
    Se acabaron los créditos de IA del mes. Se renuevan el día 1 (UTC).
  • 503ai_not_configured
    El asistente de IA no está disponible.
POST /inboxes/{id}/agent/improve-instructions

cURL

curl -X POST https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/agent/improve-instructions \
  -H "Authorization: Bearer $POSTAL_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "instructions": "responde as duvidas dos clientes e manda cobranca pro [email protected]"
  }'

Node.js

const response = await fetch('https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/agent/improve-instructions', {
  method: 'POST',
  headers: {
    Authorization: `Bearer ${process.env.POSTAL_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    instructions: 'responde as duvidas dos clientes e manda cobranca pro [email protected]',
  }),
});

const data = await response.json();

Python

import os
import requests

response = requests.post(
    "https://api.postal.so/inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/agent/improve-instructions",
    headers={"Authorization": f"Bearer {os.environ['POSTAL_API_KEY']}"},
    json={
        "instructions": "responde as duvidas dos clientes e manda cobranca pro [email protected]",
    },
    timeout=10,
)

data = response.json()

PHP

$client = new \GuzzleHttp\Client(['base_uri' => 'https://api.postal.so']);

$response = $client->post('inboxes/0199f5c1-0a1b-7c2d-8e3f-4a5b6c7d8e9f/agent/improve-instructions', [
    'headers' => ['Authorization' => 'Bearer '.getenv('POSTAL_API_KEY')],
    'json' => [
        'instructions' => 'responde as duvidas dos clientes e manda cobranca pro [email protected]',
    ],
]);

$data = json_decode($response->getBody(), true);
202Respuesta (tarea creada)
{
  "object": "ai_task",
  "id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
  "status": "pending"
}
200Respuesta final en GET /ai/tasks/{id}
{
  "object": "agent_instructions",
  "instructions": "Responda com educação às dúvidas dos clientes sobre pedidos, trocas e entregas.\nEncaminhe os assuntos de cobrança para [email protected].",
  "task_id": "019a1d40-2c3d-7e4f-8a5b-6c7d8e9f0a1b",
  "status": "completed",
  "credits": {
    "used": 13,
    "limit": 100,
    "remaining": 87,
    "resets_at": "2026-11-01T00:00:00Z"
  }
}

#Webhooks del buzón de entrada

Además de email.received, cuatro eventos acompañan a las conversaciones. Suscríbete a ellos en POST /webhooks; los formatos están en Webhooks.

EventoCuándo
inbox.thread.createdSe abrió una conversación nueva, por un correo recibido o por un mensaje enviado desde el buzón de entrada.
inbox.thread.updatedCambiaron la carpeta, el estado de leída, el responsable o las etiquetas (por PATCH, acción por lotes, envío a la papelera o por el agente), o un correo nuevo sacó la conversación del archivo. Un evento por cambio, y uno por conversación modificada en las acciones por lotes.
inbox.thread.deletedLa conversación se eliminó definitivamente.
inbox.draft.createdEl agente de IA creó un borrador (source: "agent"). Los borradores creados por personas no generan evento.

#Límites del plan

El plan define cuántos buzones de entrada tiene el equipo y qué funciones de ellos puede usar. Superar un límite responde 403 plan_limit_reached, con un mensaje que indica lo que falta. Los valores de cada plan están en Límites y en GET /plans.

Recurso del planClaveQué cambia en la API
Buzones de entradainboxesCuántos buzones de entrada puede tener el equipo. Por encima de ese número, POST /inboxes responde 403.
Buzones de entrada en tu dominiocustom_inbound_domainsSin esta función, solo hay buzones de entrada en in.postal.so: crear uno con domain_id responde 403, y el dominio no puede activar la recepción.
Agente de IA del buzón de entradainbox_agentActivar agent.enabled en PATCH /inboxes/{id} responde 403 sin ella.
Créditos de IAai_creditsEl agente y la traducción usan créditos cada mes; sin créditos, la traducción responde 429 ai_credits_exhausted y el agente no actúa.

Si el plan se reduce y el equipo se queda con más buzones de entrada que el nuevo límite, Postal no elimina ninguno: los más antiguos, hasta el límite, siguen activos y los más nuevos quedan pausados (paused_by_plan: true en el buzón). Un buzón pausado sigue en la lista y se puede leer, pero PATCH /inboxes/{id} responde 403 paused_by_plan y su agente de IA no actúa. Vuelve a la normalidad cuando el plan lo permite de nuevo o cuando eliminas otros buzones. Consulta también Límites del plan en Correos.