Antes de empezar
- Una conexión API creada en el panel web (app.grupotecnophone.com), con su Bearer Token de sandbox o de producción.
- La IP pública de su servidor registrada como autorizada para ese token.
- Un User-Agent que identifique a su aplicación, por ejemplo MiEmpresa/1.0.
- .NET 6 o superior. Los ejemplos usan HttpClient y System.Text.Json, que ya vienen incluidos: no hace falta instalar paquetes NuGet.
Guarde el token en una variable de entorno (en estos ejemplos, GTP_SMS_TOKEN) o en el almacén de secretos de su plataforma, y nunca lo incluya en una app móvil, de escritorio o en el frontend: las llamadas a la API deben salir siempre de su backend, desde una IP autorizada.
Enviar un SMS con C# y HttpClient
El siguiente programa de consola envía un SMS y procesa la respuesta:
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
var token = Environment.GetEnvironmentVariable("GTP_SMS_TOKEN")
?? throw new InvalidOperationException("Falta la variable GTP_SMS_TOKEN");
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
http.DefaultRequestHeaders.UserAgent.ParseAdd("MiEmpresa/1.0");
var payload = new
{
to = "+525512345678",
body = "Su codigo de verificacion es 482913. Vence en 5 minutos."
};
using var response = await http.PostAsJsonAsync(
"https://api.grupotecnophone.com/prod/v1/sms/send", payload);
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
if (response.IsSuccessStatusCode
&& root.TryGetProperty("status", out var status)
&& status.GetString() == "success")
{
Console.WriteLine($"Enviado. SID: {root.GetProperty("sid").GetString()} - segmentos: {root.GetProperty("num_segments").GetInt32()}");
}
else
{
var code = root.TryGetProperty("error", out var error) && error.ValueKind == JsonValueKind.Object
? error.GetProperty("code").GetString()
: $"HTTP_{(int)response.StatusCode}";
Console.WriteLine($"No enviado ({code}): {json}");
}
Respuesta de la API
Si el mensaje fue aceptado, la API responde con HTTP 200 y un JSON como este:
{
"sid": "sms_20261012143201_39bdafbd",
"status": "success",
"to": "+525512345678",
"body": "Su codigo de verificacion es 482913. Vence en 5 minutos.",
"encoding": "GSM7",
"num_chars": 56,
"num_segments": 1,
"error": null
}
Guarde el sid: es el identificador con el que podrá conciliar el reporte de entrega del mensaje. El campo num_segments indica cuántos segmentos se cobrarán; vea cómo se calculan los segmentos.
Enviar SMS a varios destinatarios con C#
Para enviar SMS masivos desde .NET conviene encapsular la llamada en una clase y reutilizar el mismo HttpClient. Así guarda el sid de cada mensaje y separa los errores.
using System.Net.Http.Json;
using System.Text.Json;
public sealed record SmsResult(bool Ok, string? Sid, string? Error);
public sealed class GtpSmsClient
{
private const string Endpoint = "https://api.grupotecnophone.com/prod/v1/sms/send";
private readonly HttpClient _http;
// Reciba un HttpClient ya configurado con el token, el User-Agent y el timeout
public GtpSmsClient(HttpClient http) => _http = http;
public async Task<SmsResult> SendAsync(string to, string body, CancellationToken ct = default)
{
using var response = await _http.PostAsJsonAsync(Endpoint, new { to, body }, ct);
var json = await response.Content.ReadAsStringAsync(ct);
try
{
using var doc = JsonDocument.Parse(json);
var root = doc.RootElement;
if (response.IsSuccessStatusCode
&& root.TryGetProperty("status", out var s) && s.GetString() == "success")
{
return new SmsResult(true, root.GetProperty("sid").GetString(), null);
}
var code = root.TryGetProperty("error", out var e) && e.ValueKind == JsonValueKind.Object
? e.GetProperty("code").GetString()
: null;
return new SmsResult(false, null, code ?? $"HTTP_{(int)response.StatusCode}");
}
catch (JsonException)
{
return new SmsResult(false, null, $"HTTP_{(int)response.StatusCode}");
}
}
}
// Uso: recorrer la lista a un ritmo moderado y guardar el sid de cada mensaje
var sms = new GtpSmsClient(http);
string[] destinatarios = { "+525512345678", "+528112345678" };
foreach (var numero in destinatarios)
{
var r = await sms.SendAsync(numero, "Su pedido fue enviado. Gracias por su compra.");
Console.WriteLine(r.Ok ? $"{numero}: {r.Sid}" : $"{numero}: error {r.Error}");
await Task.Delay(200);
}
En ASP.NET Core registre la clase con builder.Services.AddHttpClient<GtpSmsClient>() y configure ahí el token, el User-Agent y el timeout. Para volúmenes altos, encole los envíos en un servicio en segundo plano. Sin programar, también puede enviar SMS masivos desde Excel o CSV.
Enviar un código OTP con C#
Genere el código con RandomNumberGenerator, que es criptográficamente seguro, guarde solo su hash y compárelo en tiempo constante.
using System.Security.Cryptography;
using System.Text;
// Código de 6 dígitos con un generador criptográficamente seguro
var codigo = RandomNumberGenerator.GetInt32(0, 1_000_000).ToString("D6");
// Guarde solo el hash, con vencimiento y número máximo de intentos
var hash = SHA256.HashData(Encoding.UTF8.GetBytes($"{usuarioId}:{codigo}"));
await repositorio.GuardarOtpAsync(usuarioId, hash, DateTimeOffset.UtcNow.AddMinutes(5));
var r = await sms.SendAsync(telefono,
$"Su codigo de verificacion es {codigo}. Vence en 5 minutos. No lo comparta.");
// Al validar, compare los hashes en tiempo constante
bool EsValido(byte[] guardado, string ingresado) =>
CryptographicOperations.FixedTimeEquals(
guardado, SHA256.HashData(Encoding.UTF8.GetBytes($"{usuarioId}:{ingresado}")));
Más detalles del flujo en la guía de autenticación OTP por SMS y en nuestro servicio de OTP por SMS para empresas.
Errores frecuentes
| HTTP | Código | Qué significa |
|---|---|---|
| 400 | ENCRYPTION_REQUIRED | La conexión exige que to y body viajen cifrados con RSA-OAEP |
| 400 | ENCRYPTION_NOT_ALLOWED | La conexión opera en texto plano y se recibió contenido cifrado |
| 400 | INPUT_PHONE_BLACKLISTED | El número de destino está en lista negra |
| 400 | INSUFFICIENT_BALANCE | Saldo insuficiente para el envío |
| 401 | — | Token ausente o inválido |
| 403 | — | La IP de origen no está autorizada para el token |
| 500 | — | Error interno no esperado |
En los errores 400 la respuesta incluye un objeto error con code, code_num y message.
Probar en sandbox
Cambie el endpoint por https://api.grupotecnophone.com/test/v1/sms/send y use el token de sandbox. Ese entorno valida token, IP, payload y cifrado con la misma lógica que producción, pero no envía SMS reales.
Buenas prácticas
- Defina un timeout y registre cada respuesta con su
sid. - Ante un error de red sin respuesta, no reenvíe a ciegas: verifique primero para no duplicar el mensaje al usuario.
- Reintente solo errores 500, con espera progresiva y un número máximo de intentos.
- Encole los envíos masivos en lugar de enviarlos en el ciclo de una petición web.
- Si el contenido es sensible, active el cifrado de la conexión; la documentación de la API describe el formato de cifrado RSA-OAEP.
Vea también la API SMS México, la conexión SMPP y las guías para PHP, Laravel, Python, Node.js, Java y cURL. Si necesita credenciales de prueba, contáctenos.


