Logo
Published on

Implementing a Webhook for Sitecore Cloud Forms with ASP.NET Core

Authors
  • avatar
    Name
    Jorge Lusar
    Twitter

Sitecore Cloud Forms allows you to easily capture form submissions from your websites. But often you’ll want to send those submissions somewhere else — for example, into your own backend system for processing, persistence, or analytics.

This is where webhooks come in. A webhook is simply an HTTP endpoint that Sitecore calls when a form is submitted. Let’s walk through how I implemented a webhook in ASP.NET Core to handle form submissions.


The Payload from Sitecore Cloud Forms

When Sitecore Cloud Forms submits data to your webhook, it posts a JSON payload. For example, a contact form submission might look like this:

{
  "name": "Jorge",
  "lastName": "Lusar",
  "email": "jorge@test.com",
  "comment": "My comment",
  "domain": "forms.sitecorecloud.io"
}

Alongside the payload, Sitecore sends a set of useful request headers:

{
  "Date": "<timestamp when the data was submitted>",
  "X-Forwarded-For": "<the address of the end user, if available>",
  "User-Agent": "<browser where the data was submitted from, if available>",
  "Referer": "<request referer, if available>",
  "X-FormId": "someguid-region",
  "X-FormName": "Contact Form"
}

These headers help you track submissions (e.g., form ID, client IP, referer).


Defining Models for the Payload and Headers

First, I created simple C# models to represent the incoming form data and headers.

Form request headers:

public class FormRequestHeaders
{
    public string Date { get; set; }
    public string XForwardedFor { get; set; }
    public string UserAgent { get; set; }
    public string Referer { get; set; }
    public string FormId { get; set; }
    public string FormName { get; set; }
}

Contact form payload:

[DataContract]
public class ContactForm
{
    [DataMember(Name = "name")]
    [Required, MaxLength(50)]
    public required string Name { get; set; }

    [DataMember(Name = "email")]
    [Required, EmailAddress, DataType(DataType.EmailAddress)]
    [MaxLength(50)]
    [RegularExpression(RegularExpressions.EmailCharacters)]
    public required string Email { get; set; }

    [DataMember(Name = "comment")]
    [MaxLength(200)]
    public string? Comment { get; set; }

    [DataMember(Name = "domain")]
    [Required, MaxLength(50)]
    public required string Domain { get; set; }
}

Here I used data annotations to validate the payload automatically when ASP.NET Core binds it.


Implementing the Webhook Endpoint

Now let’s create an API controller to receive the form submissions.

[ApiController]
[Route("api/form")]
public class FormController(ILogger<FormController> logger, IFormService formService) : ControllerBase
{
    private readonly ILogger<FormController> logger = logger;
    private readonly IFormService formService = formService;

    private static readonly string[] RequiredHeaders =
    [
        "Date",
        "X-Forwarded-For",
        "User-Agent",
        "Referer",
        "X-FormId",
        "X-FormName"
    ];

    [HttpPost]
    [Route("contactform")]
    public async Task<IActionResult> Contact([FromBody] ContactForm payload)
    {
        var formRequestHeaders = GetFormRequestHeaders(HttpContext);
        if (formRequestHeaders == null)
        {
            return BadRequest(ModelState);
        }

        if (!ModelState.IsValid)
        {
            logger.LogError("Invalid model state for Generic form submission.");
            return BadRequest(ModelState);
        }

        await formService.SaveFormAsync(formRequestHeaders, payload);

        logger.LogInformation("Generic form: Name {Name} in Email {Email}", payload.Name, payload.Email);
        return Ok("Form received successfully.");
    }

    private FormRequestHeaders? GetFormRequestHeaders(HttpContext context)
    {
        var headers = context.Request.Headers;
        var missing = new List<string>();

        var formHeaders = new FormRequestHeaders();
        foreach (var h in RequiredHeaders)
        {
            string value = headers.TryGetValue(h, out var v) && !string.IsNullOrWhiteSpace(v)
                ? v.ToString()
                : string.Empty;

            switch (h)
            {
                case "Date": formHeaders.Date = value; break;
                case "X-Forwarded-For": formHeaders.XForwardedFor = value; break;
                case "User-Agent": formHeaders.UserAgent = value; break;
                case "Referer": formHeaders.Referer = value; break;
                case "X-FormId": formHeaders.FormId = value; break;
                case "X-FormName": formHeaders.FormName = value; break;
            }

            if (string.IsNullOrWhiteSpace(value))
                missing.Add(h);
        }

        if (missing.Count > 0)
        {
            logger.LogWarning("Missing required headers: {Headers}", string.Join(", ", missing));
            return null;
        }

        return formHeaders;
    }
}

A few important details:

  • The [ApiController] attribute automatically validates the model based on data annotations.
  • I check headers explicitly, since they aren’t model-bound.
  • IFormService is just an abstraction to save or process the form data — you could persist to a database, forward to another API, or log it.

Testing the Webhook

To test, you can use a tool like Postman or cURL to simulate the request.

Example cURL command:

curl -X POST https://localhost:5001/api/form/contactform 
  -H "Content-Type: application/json" 
  -H "Date: Thu, 21 Aug 2025 12:34:56 GMT" 
  -H "X-Forwarded-For: 192.168.0.1" 
  -H "User-Agent: Mozilla/5.0" 
  -H "Referer: https://localhost" 
  -H "X-FormId: someguid-region" 
  -H "X-FormName: Contact Form" 
  -d '{"name":"Jorge","email":"jorge@test.com","comment":"Hello!","domain":"forms.sitecorecloud.io"}'

If everything works, you should see:

Form received successfully.

Wrapping Up

With this setup, your ASP.NET Core application can securely receive and process Sitecore Cloud Form submissions.

You can extend this further by:

  • Adding authentication (e.g., API key validation).
  • Logging submissions to a database.
  • Forwarding data to a CRM or marketing automation tool.
  • Building analytics dashboards for form usage.