Skip to content
Merxian

Webhooks

Receive events

Merxian sends each event as an HTTP POST request with a JSON body. Your endpoint must verify it and answer with a 2xx status within 10 seconds.

On this page

The request#

Part Value
Method POST
URL The URL of your endpoint
Body The event envelope, as JSON in UTF-8

Headers#

Header Value
Content-Type application/json
User-Agent Merxian-Webhooks/1.0
Merxian-Signature The timestamp and one or more signatures. See Verify signatures.
Merxian-Event-Id The event ID, whevt_…. It is the same as id in the body.
Merxian-Delivery-Id The ID of the delivery of this event to this endpoint, whd_…. Retries of the delivery keep the same ID.
Merxian-Webhook-Version The payload version of the endpoint, for example 2026-08-01.

Keep the Merxian-Delivery-Id in your logs. It identifies one delivery if you need help from Merxian.

The event envelope#

Event envelope
{
  "id": "whevt_7Kd2VpXn4TqB9mLzR",
  "object": "event",
  "type": "refund.succeeded",
  "created_at": "2026-09-20T13:02:59Z",
  "api_version": "2026-08-01",
  "account_id": "acct_5Rn8bQ2xW7mK4tLzP",
  "data": {
    "object": {
      "id": "rfnd_9KcT4mW2xQ7bN5vLp",
      "object": "refund",
      "status": "succeeded",
      "transaction_id": "txn_0F8mQ2rXbT4kL9pZa",
      "payment_id": "pay_3RtN6wYc8mK2hQvJd",
      "amount": 5000,
      "currency": "EUR"
    }
  }
}

The example shows only some fields of the refund. 5000 in EUR is €50.00.

Field Type Description
id string The event ID, whevt_…. It is the same on every delivery of the event. Use it to find duplicates.
object string Always event.
type string The event type, for example payment.succeeded.
created_at string (date-time) When Merxian created the event, in ISO 8601 UTC. It is not always the time of the change.
api_version string The payload version of the endpoint.
account_id string The account of the event, acct_….
data.object object The resource of the event. Each event page shows its fields.
data.previous_attributes object On some update events, such as customer.updated, the fields that changed, with their values before the change. Absent on other events.

Payloads use snake_case field names. Some state values differ from the API values. See State values in payloads.

Ignore fields that this documentation does not list. Merxian can add fields to a payload version.

The response#

Your response What Merxian does
Any 2xx status within 10 seconds The delivery succeeded. Merxian ignores the response body.
408, 429, or 5xx Merxian tries again later.
No response within 10 seconds, or a connection error Merxian tries again later.
A 3xx redirect The delivery fails. Merxian does not follow redirects and does not try again.
Any other 4xx The delivery fails. Merxian does not try again.

See Delivery and retries for the retry schedule.

A minimal handler#

This Node.js example uses only the standard library. It is an example, not an SDK. verifySignature is the function on Verify signatures.

server.js
import { createServer } from 'node:http'
import { verifySignature } from './verify.js'

const secret = process.env.MERXIAN_WEBHOOK_SECRET

createServer((request, response) => {
  if (request.method !== 'POST' || request.url !== '/webhooks/merxian') {
    response.writeHead(404).end()
    return
  }
  const chunks = []
  request.on('data', (chunk) => chunks.push(chunk))
  request.on('end', () => {
    const rawBody = Buffer.concat(chunks)
    if (!verifySignature(rawBody, request.headers['merxian-signature'], secret)) {
      response.writeHead(400).end()
      return
    }
    const event = JSON.parse(rawBody.toString('utf8'))
    // enqueue is your own function. Store the event, then process it after you answer.
    enqueue(event)
    response.writeHead(200).end()
  })
}).listen(3000)

Return 400 for a request with a signature that is not valid. Merxian does not retry a 4xx, so a real event gets no retry after this answer. Make sure that your secret is correct before you go live.

Try refund, payment.succeeded,POST /v1/transactions, orIdempotency-Key.