Enviar email
curl --request POST \
--url http://api.gu1.ai/marketplace/messaging/send-email \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"to": "<string>",
"fromEmail": "<string>",
"fromSenderId": {},
"templateParams": {},
"templateId": {},
"subject": "<string>",
"htmlBody": "<string>",
"textBody": "<string>"
}
'import requests
url = "http://api.gu1.ai/marketplace/messaging/send-email"
payload = {
"to": "<string>",
"fromEmail": "<string>",
"fromSenderId": {},
"templateParams": {},
"templateId": {},
"subject": "<string>",
"htmlBody": "<string>",
"textBody": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
to: '<string>',
fromEmail: '<string>',
fromSenderId: {},
templateParams: {},
templateId: {},
subject: '<string>',
htmlBody: '<string>',
textBody: '<string>'
})
};
fetch('http://api.gu1.ai/marketplace/messaging/send-email', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "http://api.gu1.ai/marketplace/messaging/send-email",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'to' => '<string>',
'fromEmail' => '<string>',
'fromSenderId' => [
],
'templateParams' => [
],
'templateId' => [
],
'subject' => '<string>',
'htmlBody' => '<string>',
'textBody' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "http://api.gu1.ai/marketplace/messaging/send-email"
payload := strings.NewReader("{\n \"to\": \"<string>\",\n \"fromEmail\": \"<string>\",\n \"fromSenderId\": {},\n \"templateParams\": {},\n \"templateId\": {},\n \"subject\": \"<string>\",\n \"htmlBody\": \"<string>\",\n \"textBody\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://api.gu1.ai/marketplace/messaging/send-email")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"to\": \"<string>\",\n \"fromEmail\": \"<string>\",\n \"fromSenderId\": {},\n \"templateParams\": {},\n \"templateId\": {},\n \"subject\": \"<string>\",\n \"htmlBody\": \"<string>\",\n \"textBody\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://api.gu1.ai/marketplace/messaging/send-email")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"to\": \"<string>\",\n \"fromEmail\": \"<string>\",\n \"fromSenderId\": {},\n \"templateParams\": {},\n \"templateId\": {},\n \"subject\": \"<string>\",\n \"htmlBody\": \"<string>\",\n \"textBody\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyReferencia API
Enviar email
POST /marketplace/messaging/send-email — email transaccional con plantilla o HTML inline. Consulta el esquema del request, códigos de respuesta y autenticación.
POST
/
marketplace
/
messaging
/
send-email
Enviar email
curl --request POST \
--url http://api.gu1.ai/marketplace/messaging/send-email \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"to": "<string>",
"fromEmail": "<string>",
"fromSenderId": {},
"templateParams": {},
"templateId": {},
"subject": "<string>",
"htmlBody": "<string>",
"textBody": "<string>"
}
'import requests
url = "http://api.gu1.ai/marketplace/messaging/send-email"
payload = {
"to": "<string>",
"fromEmail": "<string>",
"fromSenderId": {},
"templateParams": {},
"templateId": {},
"subject": "<string>",
"htmlBody": "<string>",
"textBody": "<string>"
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
to: '<string>',
fromEmail: '<string>',
fromSenderId: {},
templateParams: {},
templateId: {},
subject: '<string>',
htmlBody: '<string>',
textBody: '<string>'
})
};
fetch('http://api.gu1.ai/marketplace/messaging/send-email', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "http://api.gu1.ai/marketplace/messaging/send-email",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'to' => '<string>',
'fromEmail' => '<string>',
'fromSenderId' => [
],
'templateParams' => [
],
'templateId' => [
],
'subject' => '<string>',
'htmlBody' => '<string>',
'textBody' => '<string>'
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "http://api.gu1.ai/marketplace/messaging/send-email"
payload := strings.NewReader("{\n \"to\": \"<string>\",\n \"fromEmail\": \"<string>\",\n \"fromSenderId\": {},\n \"templateParams\": {},\n \"templateId\": {},\n \"subject\": \"<string>\",\n \"htmlBody\": \"<string>\",\n \"textBody\": \"<string>\"\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("http://api.gu1.ai/marketplace/messaging/send-email")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"to\": \"<string>\",\n \"fromEmail\": \"<string>\",\n \"fromSenderId\": {},\n \"templateParams\": {},\n \"templateId\": {},\n \"subject\": \"<string>\",\n \"htmlBody\": \"<string>\",\n \"textBody\": \"<string>\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("http://api.gu1.ai/marketplace/messaging/send-email")
http = Net::HTTP.new(url.host, url.port)
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"to\": \"<string>\",\n \"fromEmail\": \"<string>\",\n \"fromSenderId\": {},\n \"templateParams\": {},\n \"templateId\": {},\n \"subject\": \"<string>\",\n \"htmlBody\": \"<string>\",\n \"textBody\": \"<string>\"\n}"
response = http.request(request)
puts response.read_bodyEnvía un email transaccional para la organización autenticada. Requiere la integración Email en Marketplace y proveedor configurado. Requisitos generales en Resumen de mensajería.
Si no envías Remitente personalizado (
Si envías
Ejemplo de rechazo (dominio no verificado):
No envíes
Obligatorio al menos uno de
Los filtros opcionales son
Tras pasar validaciones, el proveedor externo puede devolver
Endpoint
POST https://api.gu1.ai/marketplace/messaging/send-email
Cabeceras
Authorization: Bearer TU_API_KEY
Content-Type: application/json
X-Organization-ID: <uuid> # opcional si hay varias orgs
Cuerpo de la petición
Común
string
required
Email del destinatario.
string
Dirección completa de envío (ej.
noreply@mi-dominio.com). El dominio debe estar registrado y verificado en tu organización (Configuración → Email → Dominios). No hace falta dar de alta esa dirección exacta en Remitentes si el dominio ya está verificado. Excluyente con fromSenderId.string (UUID)
UUID del remitente en Configuración → Email → Remitentes. Excluyente con
fromEmail.fromEmail ni fromSenderId, la API usa el remitente por defecto de la plataforma Gu1. Eso es el comportamiento esperado, no un error.
Remitente personalizado (fromEmail)
Si envías fromEmail, por ejemplo example@mi-dominio.com, la API valida el dominio (mi-dominio.com) antes de intentar el envío. La respuesta es siempre 400 con { "success": false, "error": "<mensaje en inglés>" }; no se envía el correo hasta que el dominio esté verificado.
| Situación | Qué devuelve la API |
|---|---|
| El dominio nunca se agregó en Gu1 para tu org | Domain "mi-dominio.com" is not registered in Gu1. Add and verify it under Settings → Email → Domains before using sender "example@mi-dominio.com". |
| El dominio está en Gu1 pero aún no verificaste DNS (o falló Verificar) | Domain "mi-dominio.com" is not verified yet. Complete DNS records and click Verify under Settings → Email → Domains. |
| El dominio está verificado | La petición sigue; puedes usar cualquier local-part en ese dominio (example@…, noreply@…, etc.) sin crear la fila en Remitentes. Si la dirección existe en Remitentes, se usa el nombre configurado allí. |
{
"success": false,
"error": "Domain \"mi-dominio.com\" is not verified yet. Complete DNS records and click Verify under Settings → Email → Domains."
}
object
Valores para
{{placeholders}} en asunto, HTML, texto o plantilla. Por defecto {}.Modo A — Plantilla
string (UUID)
required
Plantilla con canal email.
htmlBody ni textBody.
string
Opcional si la plantilla define asunto o queda vacío tras variables.
Modo B — Contenido inline
SintemplateId.
string
required
Asunto; admite
{{variables}} con templateParams.string
HTML (~500k caracteres máx.).
string
Texto plano; si no hay HTML se envuelve en HTML mínimo.
htmlBody o textBody.
Ejemplo — plantilla
{
"to": "usuario@ejemplo.com",
"templateId": "550e8400-e29b-41d4-a716-446655440000",
"templateParams": { "name": "Ada", "token": "482910" },
"fromEmail": "noreply@tudominio.com"
}
Ejemplo — HTML con variables
{
"to": "usuario@ejemplo.com",
"subject": "Tu código: {{token}}",
"htmlBody": "<p>Tu código es <strong>{{token}}</strong></p>",
"templateParams": { "token": "482910" }
}
Respuesta exitosa
{
"success": true,
"deliveryId": "b52c98a4-64e0-4af1-a12a-7daf4d18b14a",
"messageId": "provider-message-id",
"rfcMessageId": "<message@example.com>",
"emailProvider": "resend"
}
deliveryId identifica el registro de entrega en Gu1. Los dominios SendGrid
existentes siguen soportados y devuelven emailProvider: "sendgrid".
Seguimiento de entregas
Lista los correos enviados y su evento más reciente:GET /marketplace/messaging/email-deliveries?limit=50&provider=resend
provider (resend o sendgrid), source
(transactional_api, automation, export o platform) y before
(el timestamp ISO devuelto en nextCursor).
Cada fila incluye IDs del mensaje, remitente, destinatario, asunto, origen,
estado actual y timestamps como sentAt, deliveredAt, openedAt,
clickedAt, bouncedAt y failedAt. Aperturas y clics están disponibles
cuando el tracking está activo en el dominio. Las protecciones de privacidad
de algunos clientes de correo pueden generar aperturas automáticas, por lo que
openedAt es una señal de interacción y no prueba de lectura humana. Los
envíos legacy de SendGrid siguen listándose, pero no reciben eventos históricos
de apertura desde el webhook de Resend.
Errores y códigos HTTP
En la mayoría de los casos la API responde con JSON{ "success": false, "error": "<mensaje en inglés>" }. Los cuerpos inválidos pueden devolver otro formato (p. ej. detalle Zod) con 400. Todos los error de negocio van en inglés.
| Situación | HTTP | Ejemplo de error (texto real de la API) |
|---|---|---|
| Integración Email desactivada para la org | 400 | Email integration is not active. Enable Email (global_sender_email) in Applications (Marketplace) for this organization. |
| Servidor sin URL del proveedor de mensajería | 400 | MS_PROVIDER_URL is not configured on the server. Configure the messaging provider to send email. |
| Precio > 0 y sin saldo ni cupo de pack | 400 | Insufficient balance for this send (cost … credits). … (ver respuesta completa) |
| Envío OK pero fallo al registrar cobro | 400 | Insufficient balance to record billing for this send. Current balance (…) is below required (…). … |
templateId y cuerpo inline a la vez | 400 (validación) | Use either templateId + templateParams, or htmlBody/textBody only — not both. |
| Sin plantilla ni cuerpo | 400 (validación) | Provide templateId or htmlBody/textBody. |
Modo inline sin subject | 400 (validación) | subject is required when not using a template. |
| Plantilla inexistente u otra organización | 404 | Template not found or not accessible for this organization. |
| Plantilla con otro canal (no email) | 400 | Template channel is "<channel>"; email is required. |
| Plantilla sin asunto tras variables | 400 | The template has no subject or it is empty after replacing variables. Send subject in the request body or set the subject on the template. |
fromSenderId y fromEmail juntos | 400 | Send only one of fromSenderId or fromEmail, not both. |
fromSenderId desconocido | 400 | fromSenderId does not match a sender for this organization. Check Settings → Email → Senders. |
Dominio del fromEmail no registrado en la org | 400 | Domain "…" is not registered in Gu1. Add and verify it under Settings → Email → Domains before using sender "…". |
| Dominio registrado pero sin verificar (DNS) | 400 | Domain "…" is not verified yet. Complete DNS records and click Verify under Settings → Email → Domains. |
Asunto vacío tras {{…}} (inline) | 400 | Subject is empty after replacing variables. |
| Cuerpo vacío tras render | 400 | htmlBody or textBody is empty after rendering. |
| Sin organización en sesión | 401 | { "error": "Organization ID not found" } (sin campo success) |
{ "success": false, "error": "…" } con 200; comprueba siempre success y error.Was this page helpful?