There are multiple versions of this document. Pick the options that suit you best.

SDK Language

Webhooks

The client.Webhooks sub-client provides full CRUD operations for webhook subscriptions. Webhooks allow the OneSend2U platform to push real-time event notifications to your application's HTTP endpoint when message states change, errors occur, or user actions are detected.

Available methods

Method Description
GetListAsync(request) List webhooks with optional filters and pagination
GetAsync(id) Get a webhook by ID
CreateAsync(request) Create a new webhook
UpdateAsync(id, request) Update an existing webhook
DeleteAsync(id) Delete a webhook
TestAsync(webhook) Send a test payload to a webhook endpoint
RotateSigningSecretAsync(id) Rotate the HMAC signing secret for a webhook

Webhook events

Webhooks are triggered by combinations of event types and message states:

EventType enum

Value Description
Status Message state changes (use with Statuses filter)
UserAction User interactions such as button clicks in WhatsApp
Consent Consent-related events
ErrorProcessingMessages Errors during message processing

MessageProcessState (Statuses filter)

Value Description
Initial Message created
Pending Queued for sending
Sending Being sent to provider
Success Delivered to provider
Error Delivery failed
Discarded Discarded before sending
NotConsented Recipient has not consented
Unknown State undetermined

Creating a webhook

using OneSend2U.Sdk.Models.Enums;
using OneSend2U.Sdk.Webhooks.Models;

var webhook = await client.Webhooks.CreateAsync(new CreateWebhookRequest
{
    Name           = "Delivery Status Webhook",
    TargetEndPoint = "https://yourapp.example.com/webhooks/onesend2u",
    HttpMethod     = "POST",
    Status         = true,          // active
    NumberOfRetries = 3,
    RetryStrategy  = RetryStrategy.FixedDelay,
    RetryDelay     = 60,            // seconds between retries
    IsSigningEnabled = true,        // enable HMAC-SHA256 signature
    SigningSecretGracePeriodMinutes = 60,
    EventTypes = [EventType.Status],
    Statuses   = [MessageProcessState.Success, MessageProcessState.Error],
    DeploymentEnvironmentId = deploymentEnvId
});

Console.WriteLine($"Webhook created: {webhook.Id}");
Console.WriteLine($"Signing enabled: {webhook.IsSigningEnabled}");

CreateWebhookRequest fields

Field Type Default Description
Name string (required) Webhook name
TargetEndPoint string (required) Target URL to receive events
HttpMethod string "POST" HTTP method for delivery
Headers string? null Custom headers as a JSON string
NumberOfRetries int 0 Number of retry attempts on failure
RetryStrategy RetryStrategy FixedDelay Retry timing strategy (see table below)
RetryDelay int 60 Seconds between retries
Timeout int 0 Request timeout in seconds (0 = no timeout)
Status bool true Whether the webhook is active
DeploymentEnvironmentId Guid (required) Environment scope
ApplicationIds List<Guid> [] Limit to specific applications
EventTypes List<EventType> [] Event types that trigger this webhook
Statuses List<MessageProcessState> [] Message states that trigger this webhook
UserActions List<string> [] User action identifiers
IsSigningEnabled bool false Enable HMAC-SHA256 payload signing
SigningSecretGracePeriodMinutes int 60 Grace period for secret rotation

Retry strategies

Value Description
FixedDelay Fixed delay between retry attempts
ExponentialBackoff Exponentially increasing delay
LinearBackoff Linearly increasing delay
Jitter Randomized delay

WebhookResponse fields

GetAsync and CreateAsync return a WebhookResponse with these fields:

Field Type Description
Id Guid Webhook ID
Name string? Webhook name
TargetEndPoint string? Target URL
HttpMethod string? HTTP method
Headers string? Custom headers as JSON
Body string? Custom request body template (if set)
NumberOfRetries int Retry attempts on failure
RetryStrategy RetryStrategy Retry timing strategy
RetryDelay int Seconds between retries
Timeout int Request timeout in seconds
Status bool Whether the webhook is active
DeploymentEnvironmentId Guid Environment scope
ApplicationIds List<Guid> Application scope
EventTypes List<EventType> Triggering event types
Statuses List<MessageProcessState> Triggering message states
UserActions List<string> Triggering user action identifiers
IsSigningEnabled bool Whether HMAC signing is enabled
SigningSecret string? Current signing secret (encrypted at rest; the cleartext is returned only here so you can store it)
HasPreviousSigningSecret bool true while the previous secret is still valid (during the rotation grace period)
SigningSecretRotatedAt DateTime? Timestamp of the last RotateSigningSecretAsync call
SigningSecretGracePeriodMinutes int Grace period for secret rotation
ConcurrencyStamp string? Optimistic concurrency stamp

Updating a webhook

Pass the ConcurrencyStamp from the GetAsync response to prevent conflicting updates:

var existing = await client.Webhooks.GetAsync(webhookId);

await client.Webhooks.UpdateAsync(webhookId, new UpdateWebhookRequest
{
    Name             = existing.Name!,
    TargetEndPoint   = "https://yourapp.example.com/webhooks/v2",
    HttpMethod       = existing.HttpMethod ?? "POST",
    Status           = existing.Status,
    NumberOfRetries  = existing.NumberOfRetries,
    RetryStrategy    = existing.RetryStrategy,
    RetryDelay       = existing.RetryDelay,
    EventTypes       = existing.EventTypes,
    Statuses         = existing.Statuses,
    ConcurrencyStamp = existing.ConcurrencyStamp,
    DeploymentEnvironmentId = existing.DeploymentEnvironmentId
});

Deleting a webhook

await client.Webhooks.DeleteAsync(webhookId);
Console.WriteLine("Webhook deleted.");

Testing a webhook

Sends a synthetic test event to the webhook endpoint without creating a real notification. You pass the full WebhookResponse object (fetch it first with GetAsync).

WebhookTestResponse fields:

Field Type Description
StatusCode int? HTTP status code returned by your endpoint
ReasonPhrase string? HTTP reason phrase (e.g., "OK", "Not Found")
FormattedContent string? Pretty-printed response body
RawContent string? Raw response body
ErrorMessage string? Error message if the request could not be delivered
// Fetch the webhook first — TestAsync takes the full WebhookResponse
var webhook = await client.Webhooks.GetAsync(webhookId);
var testResult = await client.Webhooks.TestAsync(webhook);

Console.WriteLine($"Test status code: {testResult.StatusCode}");
Console.WriteLine($"Reason: {testResult.ReasonPhrase}");

if (testResult.ErrorMessage != null)
    Console.WriteLine($"Delivery error: {testResult.ErrorMessage}");
else
    Console.WriteLine($"Response body: {testResult.FormattedContent}");

Rotating the signing secret

Use RotateSigningSecretAsync to generate a new HMAC signing secret. The previous secret stays valid for the duration of SigningSecretGracePeriodMinutes so your endpoint can drain in-flight events before switching.

var rotated = await client.Webhooks.RotateSigningSecretAsync(webhookId);
Console.WriteLine($"New secret: {rotated.SigningSecret}");
// Previous secret remains valid during grace period — validate against both secrets in the meantime

Secret rotation

When you rotate a signing secret, the previous secret remains valid for the configured grace period (SigningSecretGracePeriodMinutes). During this window, validate incoming payloads against both secrets:

var webhook = await client.Webhooks.GetAsync(webhookId);

// During grace period, HasPreviousSigningSecret will be true.
// Validate against both secrets to avoid dropping valid events.
var result = WebhookSignatureValidator.Validate(
    payload:         requestBody,
    secrets:         [webhook.SigningSecret, previousSecret],
    signatureHeader: request.Headers["X-OneSend2U-Webhook-Signature"],
    timestampHeader: request.Headers["X-OneSend2U-Webhook-Timestamp"],
    webhookIdHeader: request.Headers["X-OneSend2U-Webhook-Id"]);

See Webhook Verification for the full signature validation guide.