.NET / C#
Send vTilt events from .NET code (ASP.NET Core, console apps, Azure Functions) by POSTing to the ingest API.
There's no native .NET SDK yet, but vTilt's wire format is a plain JSON POST so any C# code can emit events. The pattern below uses HttpClient + System.Text.Json; the same shape works inside ASP.NET Core, ASP.NET MVC, Blazor Server, Azure Functions, console apps, and worker services.
#1. Configure
# .env / appsettings.json
VTILT_TOKEN=YOUR_PROJECT_TOKEN
VTILT_HOST=https://www.vtilt.comtext
#2. Capture client
A typed HttpClient exposed through IHttpClientFactory. Captures are awaited inside Task.Run so handlers don't block on network IO.
// Vtilt/VtiltClient.cs
using System.Net.Http.Json;
public sealed class VtiltClient
{
private readonly HttpClient _http;
private readonly string _token;
private readonly ILogger<VtiltClient> _log;
public VtiltClient(HttpClient http, IConfiguration config, ILogger<VtiltClient> log)
{
_http = http;
_http.BaseAddress = new Uri(config["VTILT_HOST"] ?? "https://www.vtilt.com");
_http.Timeout = TimeSpan.FromSeconds(2);
_token = config["VTILT_TOKEN"] ?? throw new InvalidOperationException("VTILT_TOKEN missing");
_log = log;
}
public void Capture(string distinctId, string @event, IDictionary<string, object>? properties = null)
{
var payload = new
{
api_key = _token,
@event = @event,
distinct_id = distinctId,
properties = properties ?? new Dictionary<string, object>(),
};
_ = Task.Run(async () =>
{
try
{
await _http.PostAsJsonAsync("/api/e", payload);
}
catch (Exception ex)
{
_log.LogWarning(ex, "vtilt capture failed");
}
});
}
public void Identify(string distinctId, IDictionary<string, object> properties)
{
Capture(distinctId, "$identify", new Dictionary<string, object> { ["$set"] = properties });
}
}csharp
// Program.cs
builder.Services.AddHttpClient<VtiltClient>();csharp
#3. Capture events
public class CheckoutController : ControllerBase
{
private readonly VtiltClient _vtilt;
public CheckoutController(VtiltClient vtilt) => _vtilt = vtilt;
[HttpPost("/checkout")]
public IActionResult Complete([FromBody] CheckoutRequest req)
{
_vtilt.Identify(req.UserId, new Dictionary<string, object> { ["plan"] = "pro" });
_vtilt.Capture(req.UserId, "purchase_completed", new Dictionary<string, object>
{
["amount"] = req.Amount,
["currency"] = "USD",
});
return Ok(new { ok = true });
}
}csharp
#4. Azure Functions
[Function("Checkout")]
public async Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Function, "post")] HttpRequestData req)
{
var data = await req.ReadFromJsonAsync<CheckoutRequest>();
_vtilt.Capture(data!.UserId, "purchase_completed", new Dictionary<string, object> { ["amount"] = data.Amount });
var res = req.CreateResponse(HttpStatusCode.OK);
await res.WriteAsJsonAsync(new { ok = true });
return res;
}csharp
#Next steps
- Identify & alias — full identification model.
- Event forwarding — fan events out to GA4, Meta CAPI, PostHog.
- Reverse proxy — block-resistant ingestion.