Webhooks Overview
Webhooks allow MeSomb to notify your application when an important event happens in your account.
Instead of repeatedly calling the MeSomb API to check whether a payment has changed, your application can expose a webhook endpoint. MeSomb will send an HTTP request to that endpoint whenever a subscribed event occurs.
Typical webhook use cases include:
- confirming that a payment transaction succeeded;
- updating an order after a checkout session is completed;
- releasing goods or services after a secure payment is funded;
- synchronizing payment status with your internal system;
- receiving dispute, refund, payout, or account lifecycle notifications.
How webhooks work
A webhook is an HTTP POST request sent by MeSomb to a URL that you configure.
Example flow:
Example webhook endpoint URL:
<span><span style="color: undefined">https://example.com/webhooks/mesomb</span></span>
<span><span style="color: undefined"></span></span>
Your endpoint must be publicly reachable over HTTPS.
Webhook event structure
All MeSomb webhook events use the same top-level structure.
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"id"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"adbbf6bd-1746-4545-a3ce-8b153a7a31b2"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"object_type"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"payment"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"event_type"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"payment.succeeded"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"api_version"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"2026-05-01"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"created_at"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"2026-05-07T22:15:30Z"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"livemode"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">true</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"data"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"object"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"id"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"txn_01HX9K7A1R6F8MZK2Q9V7N4P5B"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"object"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"payment_transaction"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"status"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"succeeded"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"amount"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-constant)">15000</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"currency"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"XAF"</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"reference"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"ORDER-10045"</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span><span style="color: var(--shiki-token-punctuation)">,</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"previous_attributes"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> {</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"status"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"pending"</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)"> }</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
Top-level fields
| Field | Type | Description |
id | string | Unique ID of the webhook event. Use this for deduplication. |
object_type | string | Always event. |
event_type | string | Name of the event, for example payment.success. |
api_version | string | MeSomb API version used for this event payload. |
created_at | string | Date and time when the event was created, in ISO 8601 UTC format. |
livemode | boolean | true for live events, false for test events. |
data.object | object | Snapshot of the resource affected by the event. |
data.previous_attributes | object | Previous values for fields that changed, when available. |
Event naming convention
MeSomb event names follow this format:
<span><span style="color: undefined">domain.resource.action</span></span>
<span><span style="color: undefined"></span></span>
Examples:
<span><span style="color: undefined">payment.transaction.succeeded</span></span>
<span><span style="color: undefined">checkout.session.completed</span></span>
<span><span style="color: undefined">secure_payment.transaction.released</span></span>
<span><span style="color: undefined">dispute.created</span></span>
<span><span style="color: undefined">payout.paid</span></span>
<span><span style="color: undefined"></span></span>
The event name should be used by your application to decide how to process the webhook.
Example:
<span><span style="color: var(--shiki-token-keyword)">if</span><span style="color: var(--shiki-color-text)"> event</span><span style="color: var(--shiki-token-punctuation)">[</span><span style="color: var(--shiki-token-string-expression)">"type"</span><span style="color: var(--shiki-token-punctuation)">]</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">==</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"payment.transaction.succeeded"</span><span style="color: var(--shiki-token-punctuation)">:</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">handle_successful_payment</span><span style="color: var(--shiki-token-punctuation)">(event[</span><span style="color: var(--shiki-token-string-expression)">"data"</span><span style="color: var(--shiki-token-punctuation)">][</span><span style="color: var(--shiki-token-string-expression)">"object"</span><span style="color: var(--shiki-token-punctuation)">])</span></span>
<span><span style="color: var(--shiki-token-keyword)">elif</span><span style="color: var(--shiki-color-text)"> event</span><span style="color: var(--shiki-token-punctuation)">[</span><span style="color: var(--shiki-token-string-expression)">"type"</span><span style="color: var(--shiki-token-punctuation)">]</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">==</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"checkout.session.completed"</span><span style="color: var(--shiki-token-punctuation)">:</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-function)">handle_completed_checkout</span><span style="color: var(--shiki-token-punctuation)">(event[</span><span style="color: var(--shiki-token-string-expression)">"data"</span><span style="color: var(--shiki-token-punctuation)">][</span><span style="color: var(--shiki-token-string-expression)">"object"</span><span style="color: var(--shiki-token-punctuation)">])</span></span>
<span></span>
Webhook response requirements
Your webhook endpoint should return a 2xx HTTP status code when the event has been successfully received.
Recommended responses:
<span><span style="color: undefined">HTTP/1.1 200 OK</span></span>
<span><span style="color: undefined"></span></span>
or:
<span><span style="color: undefined">HTTP/1.1 204 No Content</span></span>
<span><span style="color: undefined"></span></span>
Any non-2xx response may cause MeSomb to retry the webhook.
Retry behavior
MeSomb may retry webhook delivery when your endpoint does not return a 2xx response.
Retries can happen when:
- your endpoint returns
4xxor5xx; - your endpoint times out;
- the network connection fails;
- MeSomb cannot confirm that your server received the event.
Your webhook integration must be idempotent because the same event can be delivered more than once.
Recommended retry policy:
<span><span style="color: undefined">Automatic retry window: up to 72 hours</span></span>
<span><span style="color: undefined">Manual replay window: up to 90 days</span></span>
<span><span style="color: undefined">Full event history retention: 90 days</span></span>
<span><span style="color: undefined">Lightweight metadata retention: up to 2 years</span></span>
<span><span style="color: undefined"></span></span>
Duplicate events and idempotency
Webhook events can be delivered more than once.
Always use the event ID to detect duplicate events:
<span><span style="color: var(--shiki-color-text)">{</span></span>
<span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-keyword)">"id"</span><span style="color: var(--shiki-token-punctuation)">:</span><span style="color: var(--shiki-color-text)"> </span><span style="color: var(--shiki-token-string-expression)">"adbbf6bd-1746-4545-a3ce-8b153a7a31b2"</span></span>
<span><span style="color: var(--shiki-color-text)">}</span></span>
<span></span>
Recommended behavior:
- Receive the webhook.
- Verify the signature.
- Check whether the event ID has already been processed.
- If already processed, return
200 OKwithout running the business action again. - If not processed, process the event and store the event ID.
Webhook signature verification
Every webhook request sent by MeSomb is signed.
Your server should verify the X-MeSomb-Webhook-Signature header before processing the event.
Webhook signature verification protects your integration against fake or modified webhook requests.
Read the full guide in: Verifying webhook signatures.
Security recommendations
Before using webhooks in production:
- use HTTPS for your webhook endpoint;
- verify the
X-MeSomb-Webhook-Signatureheader; - use the raw request body for signature verification;
- reject old timestamps;
- store processed event IDs for deduplication;
- do not expose webhook secrets in frontend or mobile code;
- return
2xxonly when the event has been successfully received.
Example webhook handler flow
Recommended processing model
Webhook endpoints should respond quickly.
For production systems, avoid running heavy business logic directly inside the webhook HTTP request.
Recommended approach:
- Verify the signature.
- Store the event.
- Return a
2xxresponse. - Process the event asynchronously using a background worker.
This reduces timeout risks and improves webhook delivery reliability.