How to Send iMessages from C# / .NET
C# and .NET developers can send iMessages through the Sendblue REST API using HttpClient and ASP.NET Core. This tutorial covers setting up a typed client with dependency injection, sending messages, receiving webhooks, and handling media attachments with proper error handling.
Prerequisites
You need:
- .NET 8+ (or .NET 6+ with minor adjustments)
- Sendblue API credentials — sign up free to get your API key and secret
- An ASP.NET Core project (for webhook endpoints)
Add your credentials to appsettings.json:
{
"Sendblue": {
"ApiKey": "your-api-key-here",
"ApiSecret": "your-api-secret-here",
"BaseUrl": "https://api.sendblue.co"
}
}HttpClient Setup with Dependency Injection
Create a typed HttpClient service for Sendblue. This is the recommended approach in .NET for managing HTTP connections efficiently:
// Services/SendblueClient.cs
using System.Net.Http.Json;
using Microsoft.Extensions.Options;
public class SendblueOptions
{
public string ApiKey { get; set; } = "";
public string ApiSecret { get; set; } = "";
public string BaseUrl { get; set; } = "https://api.sendblue.co";
}
public class SendblueClient
{
private readonly HttpClient _http;
public SendblueClient(HttpClient http, IOptions<SendblueOptions> opts)
{
_http = http;
_http.BaseAddress = new Uri(opts.Value.BaseUrl);
_http.DefaultRequestHeaders.Add("sb-api-key-id", opts.Value.ApiKey);
_http.DefaultRequestHeaders.Add("sb-api-secret-key", opts.Value.ApiSecret);
}
public async Task<SendMessageResponse> SendMessageAsync(
string number, string content,
string? sendStyle = null, string? mediaUrl = null)
{
var payload = new Dictionary<string, string?>
{
["number"] = number,
["content"] = content,
["send_style"] = sendStyle,
["media_url"] = mediaUrl,
};
// Remove null values
var filtered = payload
.Where(kv => kv.Value != null)
.ToDictionary(kv => kv.Key, kv => kv.Value);
var response = await _http.PostAsJsonAsync("/api/send-message", filtered);
response.EnsureSuccessStatusCode();
return await response.Content.ReadFromJsonAsync<SendMessageResponse>()
?? throw new Exception("Empty response from Sendblue");
}
public async Task<EvaluateServiceResponse> EvaluateServiceAsync(string number)
{
var response = await _http.GetFromJsonAsync<EvaluateServiceResponse>(
$"/api/evaluate-service?number={Uri.EscapeDataString(number)}");
return response ?? throw new Exception("Empty response");
}
}Register the client in Program.cs:
builder.Services.Configure<SendblueOptions>(
builder.Configuration.GetSection("Sendblue"));
builder.Services.AddHttpClient<SendblueClient>();Send Your First iMessage
Inject the SendblueClient into your controller or service and send a message:
// Controllers/MessageController.cs
[ApiController]
[Route("api/[controller]")]
public class MessageController : ControllerBase
{
private readonly SendblueClient _sendblue;
public MessageController(SendblueClient sendblue)
{
_sendblue = sendblue;
}
[HttpPost("send")]
public async Task<IActionResult> Send([FromBody] SendRequest req)
{
var result = await _sendblue.SendMessageAsync(
req.Number,
req.Content,
req.SendStyle
);
return Ok(result);
}
}
public record SendRequest(string Number, string Content, string? SendStyle = null);Test it with a simple HTTP call:
curl -X POST https://localhost:5001/api/message/send \
-H "Content-Type: application/json" \
-d '{"number": "+15551234567", "content": "Hello from .NET!", "sendStyle": "celebration"}'ASP.NET Core Webhook Endpoint
Create a controller to receive incoming messages from Sendblue webhooks:
// Controllers/WebhookController.cs
[ApiController]
[Route("webhooks")]
public class WebhookController : ControllerBase
{
private readonly SendblueClient _sendblue;
private readonly ILogger<WebhookController> _logger;
public WebhookController(SendblueClient sendblue,
ILogger<WebhookController> logger)
{
_sendblue = sendblue;
_logger = logger;
}
[HttpPost("receive")]
public async Task<IActionResult> ReceiveMessage(
[FromBody] IncomingMessage message)
{
_logger.LogInformation(
"[{Service}] {From}: {Content}",
message.Service, message.FromNumber, message.Content);
// Auto-reply
await _sendblue.SendMessageAsync(
message.FromNumber,
"Thanks! We received your message and will respond shortly."
);
return Ok(new { status = "ok" });
}
}
public record IncomingMessage
{
[JsonPropertyName("from_number")]
public string FromNumber { get; init; } = "";
[JsonPropertyName("content")]
public string Content { get; init; } = "";
[JsonPropertyName("media_url")]
public string? MediaUrl { get; init; }
[JsonPropertyName("service")]
public string Service { get; init; } = "";
}Set your webhook URL in the Sendblue dashboard. Use ngrok for local testing: ngrok http 5001.
Send Media
Attach images, videos, PDFs, or contact cards:
// Send an image
await _sendblue.SendMessageAsync(
"+15551234567",
"Here's your document:",
mediaUrl: "https://example.com/document.pdf"
);
// Send a contact card (vCard)
await _sendblue.SendMessageAsync(
"+15551234567",
null,
mediaUrl: "https://example.com/contact.vcf"
);Contact cards (.vcf files) are a unique Sendblue feature. When the recipient saves the card, your business name appears on all future messages — highly effective for sales teams.
Typed Response Models
Define strongly typed models for API responses:
public record SendMessageResponse
{
[JsonPropertyName("status")]
public string Status { get; init; } = "";
[JsonPropertyName("message_id")]
public string? MessageId { get; init; }
[JsonPropertyName("error_message")]
public string? ErrorMessage { get; init; }
}
public record EvaluateServiceResponse
{
[JsonPropertyName("is_imessage")]
public bool IsImessage { get; init; }
[JsonPropertyName("number")]
public string Number { get; init; } = "";
}With typed responses, you get IntelliSense support and compile-time safety for all API interactions.
Error Handling
Wrap API calls in robust error handling with Polly for retry policies:
// In Program.cs — add Polly retry
builder.Services.AddHttpClient<SendblueClient>()
.AddTransientHttpErrorPolicy(p =>
p.WaitAndRetryAsync(3, attempt =>
TimeSpan.FromSeconds(Math.Pow(2, attempt))));
// Or handle errors manually in the service:
public async Task<SendMessageResponse?> SendMessageSafeAsync(
string number, string content)
{
try
{
return await SendMessageAsync(number, content);
}
catch (HttpRequestException ex)
{
_logger.LogError(ex, "Failed to send iMessage to {Number}", number);
return null;
}
}Polly provides automatic retries with exponential backoff — perfect for handling transient network issues.
Next Steps
You're now sending iMessages from C# and .NET. Continue with:
- Full API documentation — Group messaging, typing indicators, reactions, and all endpoints
- Webhook integration guide — Handle all 7 webhook types
- Build an AI agent — Connect an LLM to iMessage for intelligent auto-replies
- API comparison — See how Sendblue compares to Twilio and Vonage
Get your free API keys and start sending iMessages from .NET today.
Ready to send your first iMessage?
Get API access in minutes. Free sandbox, no credit card required.