Enviar SMS
Esta guía trata el endpoint de envío individual, POST /v1/sms/messages. Crea una carga JSON con un destinatario, un remitente, un cuerpo y una categoría. Bird devuelve 202 Accepted con un ID de mensaje y entrega de forma asíncrona. Cada solicitud envía un mensaje a un destinatario. Para enviar muchos mensajes a la vez, usa el envío por lotes. Para enviar una plantilla en lugar de tu propio texto, proporciona un objeto template en lugar de text y category. Las plantillas integradas rechazan from; las plantillas del espacio de trabajo lo exigen.
Antes de enviar: habilita el país de destino
Tu espacio de trabajo envía SMS solo a los países de su lista de destinos permitidos. El país de origen de tu organización es el país de la dirección de registro de su perfil de empresa, o el de la dirección de facturación si la dirección de registro no tiene país. Tu espacio de trabajo empieza con la lista de permitidos vacía. Cada vez que se establece o cambia el país de origen, Bird añade ese país a la lista. Bird rechaza un envío a un país que no está en la lista con 422 SMSDestinationNotEnabled antes de resolver un remitente. Habilita los países en los que operas en SMS > Destinations en el panel.
En la misma página puedes fijar un límite de gasto diario para cada país, en la moneda de tu monedero. Para fijarlo necesitas un monedero de organización y permiso para leerlo; para quitarlo, no. El límite se aplica tanto si el país está habilitado como si no. Cuando el gasto del día en ese país lo alcanza, Bird rechaza los mensajes nuevos a ese país con 422 SMSDestinationDailySpendExceeded hasta la medianoche UTC. También se rechazan los mensajes de verificación. El gasto se contabiliza cuando se cobra el mensaje, así que los mensajes ya aceptados pueden superar el límite en lo que cuesten. Un cambio de límite tarda hasta un minuto en aplicarse a los envíos.
Un envío mínimo
La carga de texto libre válida más pequeña consta de un destinatario to, un remitente from, un cuerpo text y un category.
const msg = await bird.sms.send({
from: "+15557654321",
to: "+14155550100",
text: "Your verification code is 123456.",
category: "authentication",
});
console.log(msg.id, msg.status);msg = client.sms.send(
from_="+15557654321",
to="+15551234567",
text="Your verification code is 123456.",
category="authentication",
)
print(msg.id, msg.status)msg, err := client.Sms.Send(context.Background(), bird.SmsSendParams{
From: "+15557654321",
To: "+15551234567",
Text: "Your verification code is 123456.",
Category: bird.SMSCategoryAuthentication,
})
if err != nil {
log.Fatal(err)
}
fmt.Println(msg.Id, *msg.Status)$message = $bird->sms->send(
from: '+15557654321',
to: '+15551234567',
text: 'Your verification code is 123456.',
category: 'authentication',
);
echo $message->getId(), ' ', $message->getStatus();bird sms send --body-file - <<'JSON'
{
"to": "+14155550100",
"from": "+15557654321",
"text": "Your verification code is 123456.",
"category": "authentication",
"options": {
"smart_encoding": true
},
"tags": [
{
"name": "campaign",
"value": "signup"
}
],
"metadata": {
"user_id": "usr_12345"
}
}
JSONcurl -X POST https://eu1.platform.bird.com/v1/sms/messages \
-H "Authorization: Bearer bk_eu1_..." \
-H "Content-Type: application/json" \
-d '{
"to": "+31612345678",
"from": "Bird",
"text": "Your Bird verification code is 481920. It expires in 10 minutes.",
"category": "authentication"
}'Usa tu host regional (https://us1.platform.bird.com o https://eu1.platform.bird.com) con una clave bk_{region}_... correspondiente. La respuesta es el mensaje aceptado:
{
"id": "sms_01ky7qmwgpfkybj9ecrnwjx714",
"direction": "outbound",
"status": "accepted",
"to": "+31612345678",
"from": "Bird",
"text": "Your Bird verification code is 481920. It expires in 10 minutes.",
"category": "authentication",
"segments": { "count": 1, "encoding": "GSM_7BIT", "characters": 64 },
"cost": null,
"carrier": null,
"mcc_mnc": null,
"sent_at": null,
"delivered_at": null,
"created_at": "2026-07-23T14:56:34.326Z"
}status: accepted significa que Bird tiene el mensaje y lo está procesando; cost es null porque el precio se calcula durante el procesamiento. Lo que ocurre después se explica en el modelo asíncrono.
Cómo construir la carga
Destinatario
to es un único destinatario en formato E.164: un + inicial, el código de país y el número del abonado, como +31612345678. Cada mensaje va a un solo destinatario, sin cc, bcc ni array de destinatarios. Para llegar a muchas personas, envía un lote.
Remitente
from es el remitente que ve el destinatario. Es obligatorio en un envío de texto libre, salvo que messaging_profile_id indique un perfil de mensajería con un remitente para el país de destino o un remitente predeterminado. Admite dos formas, y cuáles funcionan depende del país de destino:
- Un ID de remitente alfanumérico: de 3 a 11 letras, dígitos, espacios, guiones, guiones bajos o puntos, con al menos una letra y sin separador al principio ni al final, como
BirdoAcme-Co. Debe contener una letra, así que se rechaza una cadena de dígitos con signos de puntuación como555 555. Algunos países exigen registrarlo, y otros, como EE. UU., no admiten remitentes alfanuméricos. Los destinatarios no pueden responderles. - Un número que pertenece a tu espacio de trabajo, en E.164 o solo con dígitos. Cualquier
fromformado solo por dígitos se interpreta como numérico y se busca entre tus remitentes, así que se rechaza un número cualquiera que no te pertenezca. Que funcione como código largo, número gratuito o código corto depende del propio número, no de cuántos dígitos escribas. Unfromde 6 dígitos no es un código corto por tener 6 dígitos; lo es si el número que posees es un código corto.
Un remitente que no es válido para el destino se rechaza con un 422 que indica el motivo (por ejemplo, SMSAlphaNotSupported donde no hay remitentes alfanuméricos disponibles). En un envío con plantilla integrada no se acepta from: Bird elige un remitente según el destino y la categoría. Una plantilla del espacio de trabajo requiere tu propio remitente en from o un perfil de mensajería que lo proporcione.
Para reservar un ID de remitente, consultar qué exige cada país y registrarlo país por país, consulta IDs de remitente de SMS.
Para reunir en un solo lugar los remitentes y valores predeterminados de un programa, crea un perfil de mensajería e indícalo en messaging_profile_id. Si omites from, Bird envía desde el remitente del perfil para el país de destino o desde su remitente predeterminado. Si omites category, se usa la categoría predeterminada del perfil, y options.smart_encoding, cost_center, validity_period y options.callback_url toman la configuración del perfil. Los valores que defines en el mensaje tienen prioridad.
Cuerpo y categoría
text es el cuerpo del mensaje, de al menos un carácter. Se factura y se entrega en segmentos; cada envío tiene un máximo de 12 segmentos (unos 1836 caracteres GSM-7, o 804 si el cuerpo usa la codificación ampliada UCS-2). Un cuerpo que supera ese máximo se rechaza con un 422 en lugar de truncarse.
category es obligatorio en un envío de texto libre, salvo que el perfil de mensajería de messaging_profile_id defina una categoría predeterminada. Clasifica el mensaje como transactional, marketing, authentication o service, lo que indica a Bird y a los operadores por qué envías. Un código de verificación de un solo uso usa authentication; una promoción usa marketing. Elige la categoría que corresponda a la finalidad del mensaje.
Etiquetas y metadatos
Ambos adjuntan tus propios datos a un envío, pero cumplen funciones distintas:
tagsson pares{name, value}estructurados (máximo 20 por envío; nombre de 1 a 32 caracteres, valor de 1 a 64, solo[A-Za-z0-9_-]ASCII, con distinción entre mayúsculas y minúsculas y nombres únicos dentro de un envío). Son dimensiones de filtrado de primer nivel: filtra la lista de mensajes por etiqueta. Úsalas para etiquetas con pocos valores distintos, comocampaignoexperiment_variant.metadataes un objeto JSON arbitrario (máximo 2 KB serializado). Se almacena, se devuelve en las lecturas de API y se reenvía en cada evento de webhook, pero no es una dimensión de filtrado. Úsalo para el contexto que quieres recuperar: identificadores internos, claves foráneas o cualquier dato que quieras recibir de vuelta con cada evento.referencees una cadena de 1 a 255 caracteres por la que quieres encontrar el mensaje, como un número de pedido o de ticket.?reference=en la lista de mensajes la compara de forma exacta, distinguiendo mayúsculas y minúsculas, y varios mensajes pueden compartir el mismo valor. Se devuelve en las lecturas y se reenvía en cada evento de webhook. Defínelo solo cuando necesites buscar por él, y guarda cualquier otro dato enmetadata.
{
"tags": [{ "name": "campaign", "value": "spring-2026" }],
"metadata": { "user_id": "usr_12345", "order_id": "ord_98765" },
"reference": "ord_98765"
}Referencia de campos
| Campo | Tipo | Obligatorio | Límites / notas |
|---|---|---|---|
to | cadena (E.164) | sí | Un destinatario por mensaje |
from | string | sí* | Número E.164 propio, ID de remitente alfanumérico (3–11 caracteres, al menos una letra) o código corto (5–6 dígitos) |
text | string | sí* | Al menos 1 carácter; máximo de 12 segmentos |
category | string | sí* | transactional, marketing, authentication o service |
template | objeto | sí* | {slug, parameters?, language?} para una plantilla integrada o del espacio de trabajo |
tags | {name, value}[] | no | Máx. 20; nombre de 1 a 32 caracteres, valor de 1 a 64 caracteres; solo [A-Za-z0-9_-] |
metadata | objeto | no | JSON arbitrario, máximo 2 KB serializado |
reference | string | no | De 1 a 255 caracteres; coincidencia exacta para ?reference= en la lista de mensajes |
messaging_profile_id | string | no | Un perfil de mensajería que aporta el remitente y los valores predeterminados; los valores del mensaje tienen prioridad |
validity_period | integer | no | De 60 a 172.800 segundos desde la aceptación; omítelo para usar el valor del perfil de mensajería o las reglas propias del operador |
options | objeto | no | Ajustes de procesamiento por mensaje, como smart_encoding (consulta segmentos y codificación) y callback_url (consulta URL de callback) |
* Los envíos de texto libre requieren from, text y category, salvo que un perfil de mensajería puede aportar from y category. Los envíos con plantilla requieren template en lugar de text y category (y rechazan ambos). Las plantillas integradas rechazan from; las plantillas del espacio de trabajo lo requieren.
Enviar con una plantilla
Define template para enviar una plantilla integrada o una plantilla publicada del espacio de trabajo. La creación de plantillas del espacio de trabajo está en versión preliminar de API. El catálogo, las variables y las reglas de remitente se describen en plantillas de SMS.
Segmentos y codificación
SMS se factura por segmento. Un mensaje que cabe en la codificación GSM-7 dispone de 160 caracteres en un solo segmento; con UCS-2 (que se activa con emojis, caracteres CJK u otros caracteres fuera de GSM) el límite baja a 70. Los mensajes más largos se dividen en varios segmentos, cada uno con un límite algo menor. Cada respuesta indica el segments resuelto: los count facturables, la encoding y el número de caracteres. Los segmentos son la unidad por la que se te factura; consulta coste.
Cuando los caracteres tipográficos son lo único que deja un cuerpo fuera de GSM-7, la codificación inteligente puede reducir su número de segmentos. Define options.smart_encoding como true y Bird sustituirá las comillas tipográficas, los guiones, los puntos suspensivos y caracteres similares por sus equivalentes en GSM-7 antes del envío. Está desactivada de forma predeterminada porque modifica el cuerpo que redactaste.
Para consultar el juego de caracteres completo, los caracteres de la tabla de extensión que ocupan dos posiciones, el tamaño de los emojis, lo que sustituye la codificación inteligente y el cálculo de segmentos, consulta Límites de caracteres.
Envío por lotes
POST /v1/sms/batches envía hasta 100 mensajes independientes en una sola solicitud, siguiendo el patrón de envío masivo de SMS: una llamada API para muchos destinatarios. Las solicitudes por lotes usan la política de limitación de solicitudes sms_batch, distinta de la política sms_send de los envíos individuales. El cuerpo es un objeto JSON cuyo array messages contiene los objetos de mensaje descritos en Construir la carga útil:
const result = await bird.sms.sendBatch({
messages: [
{
from: "+15557654321",
to: "+15551111111",
text: "Hi Alice!",
category: "marketing",
},
{
from: "+15557654321",
to: "+15552222222",
text: "Hi Bob!",
category: "marketing",
},
],
});batch = client.sms.send_batch(
messages=[
{
"from_": "+15557654321",
"to": "+15551111111",
"text": "Hi Alice!",
"category": "marketing",
},
{
"from_": "+15557654321",
"to": "+15552222222",
"text": "Hi Bob!",
"category": "marketing",
},
]
)
for msg in batch.data:
print(msg.id, msg.status)batch, err := client.Sms.SendBatch(context.Background(), bird.SmsSendBatchParams{
Messages: []bird.SmsSendParams{
{
From: "+15557654321", To: "+15551111111",
Text: "Hi Alice!", Category: bird.SMSCategoryMarketing,
},
{
From: "+15557654321", To: "+15552222222",
Text: "Hi Bob!", Category: bird.SMSCategoryMarketing,
},
},
})
if err != nil {
log.Fatal(err)
}
for _, msg := range batch.Data {
fmt.Println(msg.Id, *msg.Status)
}$batch = $bird->sms->sendBatch(messages: [
(new SMSMessageSendRequest())
->setFrom('+15557654321')
->setTo('+15551111111')
->setText('Hi Alice!')
->setCategory('marketing'),
(new SMSMessageSendRequest())
->setFrom('+15557654321')
->setTo('+15552222222')
->setText('Hi Bob!')
->setCategory('marketing'),
]);
foreach ($batch->getData() ?? [] as $item) {
echo $item->getId(), ' ', $item->getStatus(), "\n";
}curl -X POST "https://{region}.platform.bird.com/v1/sms/batches" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"to": "+15551111111",
"from": "+15557654321",
"text": "Hi Alice!",
"category": "marketing"
},
{
"to": "+15552222222",
"from": "+15557654321",
"text": "Hi Bob!",
"category": "marketing"
}
]
}'La validación es de todo o nada: si algún mensaje del lote no es válido, se rechaza la solicitud completa con un 422 y no se envía nada, así que un lote nunca se aplica a medias. Si la solicitud se acepta, la respuesta 202 incluye cada mensaje aceptado, en el orden de envío, en data, además de un summary con el accepted_count. A partir de ahí, cada mensaje es independiente: el fallo de un destinatario nunca afecta a los demás.
El modelo asíncrono: qué significa 202
Un envío correcto devuelve 202 Accepted con un ID de mensaje y status: accepted. Los errores de la solicitud se devuelven de inmediato: un campo no válido, un cuerpo que supera el límite de segmentos, un país de destino que no has habilitado o un remitente no válido devuelven un 422. Un espacio de trabajo sin saldo en el monedero recibe un 402.
La entrega es asíncrona. El mensaje pasa a sent cuando Bird lo entrega al operador. Después, un acuse de entrega establece delivered, undelivered, failed o expired a través de eventos y webhooks y de los endpoints de lectura. Este diseño tiene tres consecuencias:
- El coste se calcula después de la aceptación. El
costde un mensaje esnullen el momento de aceptarlo y se rellena cuando Bird calcula el precio del envío durante el procesamiento. Vuelve a leer el mensaje, o espera al evento de entrega, para ver el cargo calculado hasta ese momento; coste y facturación explica los componentes y cuándo uno queda sin precio. - Un mensaje puede rechazarse después del 202. Si el cargo falla durante el procesamiento, el mensaje termina en
rejectedcon un webhooksms.rejectedy no se te factura; un monedero agotado aparece comolast_error.code: insufficient_balance. - Las lecturas pueden ir brevemente por detrás del 202. El mensaje aparece en los endpoints de lectura poco después del
202, así que un404justo después de un envío se resuelve solo en unos instantes.
Campos reservados
Bird rechaza los siguientes campos de la solicitud con 422 SMSUnsupportedFeature:
scheduled_at, media_urls, broadcast_id, campaign_id, audience_id, contact_id, topic_id, personalization, options.max_price_per_segment, options.track_clicks (cuando true)
No incluyas estos campos en un envío.
Reintentar de forma segura
Envía el encabezado Idempotency-Key con un valor único por cada envío lógico. Si una solicitud se completa sin devolver respuesta, repite la misma solicitud con la misma clave. Bird devuelve el resultado original en lugar de enviar un mensaje duplicado. Consulta idempotencia para conocer el formato de la clave y su retención.
Coste y facturación
Los SMS salientes se facturan por segmento. Lo que pagas depende del país de destino y del operador; algunas rutas añaden un recargo de terceros, como las tarifas de operador de 10DLC en EE. UU.
El cost de un mensaje desglosa el cargo en componentes con nombre. transaction_amount es lo que Bird cobró por transportar el mensaje, passthrough_amount es cualquier tarifa de terceros repercutida y amount es la suma de los componentes que ya tienen precio, expresada en currency_code. Un componente sin precio es null en lugar de "0.00000", así que un mensaje cuyo recargo nunca se resolvió informa amount solo con el cargo de transporte. La referencia de mensajes documenta cada campo.
El recargo se calcula en la medida de lo posible. Bird lo resuelve al registrar el acuse de entrega, dentro de un plazo limitado. Si no se resuelve en ese plazo, passthrough_amount queda como null de forma permanente: Bird no vuelve a intentarlo y amount sigue siendo el cargo de transporte.
Los SMS entrantes se facturan en dos líneas: la tarifa entrante por segmento y, cuando corresponde, un recargo de operador entrante. Ambos aparecen en el propio cost del mensaje recibido: la tarifa como transaction_amount y el recargo como passthrough_amount. A diferencia del recargo saliente, el entrante se calcula cuando se acepta el mensaje y no en la entrega, por lo que nunca se completa más tarde.
Revisa el coste y los segmentos de cada mensaje en el registro de SMS.
Próximos pasos
- Plantillas de SMS: envía una plantilla integrada y deja que Bird elija el remitente.
- Registro de SMS: busca un mensaje y examina su ciclo de vida, sus segmentos y su coste.
- Eventos: recibe los eventos de entrega en tus sistemas.
- URL de callback de SMS: envía los eventos de un mensaje o de un perfil a su propio endpoint HTTPS.
- Métricas de SMS: supervisa la tasa de entrega, la tasa de fallos y el volumen aceptado.
- Idempotencia: reintenta de forma segura con la cabecera
Idempotency-Key. - Envía tu primer SMS: un vídeo que recorre la misma configuración en el panel
Recursos relacionados
Continúa con la documentación, guías y ejemplos de este tema.