> ## Documentation Index
> Fetch the complete documentation index at: https://docs.archefusion.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Handle webhook notifications

Once an event occurs, for example, a payment succeeds or fails, Archefusion sends a notification to your webhook URL. However, you need to verify the event is genuinely from Archefusion and not from an unauthorized source.

## How to verify event origin

Events from Archefusion carry the `x-Archefusion-signature`. This signature is an HMAC SHA256 hash of the Archefusion timestamp, event ID, and raw payload, joined together and signed with your webhook secret.

To verify an event, recreate the signature on your server and compare it to the one in the header. If they match, the event is genuinely from Archefusion.

<Note>
  Your webhook secret is generated when you set up your webhook in the dashboard. See the [webhook setup](https://docs.archefusion.com/pages/webhook-setup#step-2-provide-your-webhook-url-in-archefusion-to-receive-payment-event-notifications-on-your-server) page for details.
</Note>

```javascript theme={null}
import crypto from 'crypto';

const webhook = async (req, res)=>{

    const secret = process.env.WEBHOOKSECRET // your webhook secret
    const signature = req.headers['x-Archefusion-signature']  // Archefusion webhook signature sent in the request header
    const timestamp = req.headers['x-Archefusion-timestamp']  // Archefusion webhook timestamp sent in the request header
    const eventId = req.headers['x-Archefusion-event-id'] // Archefusion event ID sent in the request header
    const rawBody = req.body.toString() // raw event payload

    const baseString = `${timestamp}.${eventId}.${rawBody}` 
    const expectedHash = crypto.createHmac('sha256', secret).update(baseString).digest('hex')  // create new signature
    if(expectedHash !== signature){  // verify if the new signature matches Archefusion webhook signature        
        return res.status(403).json({success: false, msg:"Invalid signature!"})
    }
    // if it matches do something with the event
    const event = JSON.parse(req.body.toString())
    

    return res.status(200).json({received:true})
}


export default webhook;
```

## Best practices:

* Configure your webhook route to receive the raw request body (for example, using express.raw in Express), since verification requires the exact unparsed payload. Do not parse and restringify it, as any change in whitespace or key order will break verification.

* Ensure your webhook secret is not accessible in the frontend.

* Handle idempotency gracefully. A webhook may be delivered more than once, so deduplicate by event ID to avoid processing the same event twice.

* Return a `200 OK` response promptly, then handle heavier processing afterward.

* Verify every event signature before acting on it.

## How Archefusion sends events

When your webhook URL receives an event, it needs to parse and acknowledge the event. Acknowledging an event means returning a `200 OK` in the HTTP response. Without acknowledging it, Archefusion flags it as a failed attempt and retries delivery on the following schedule:

**Attempt 1**: Immediately

**Attempt 2**: After 30 seconds

**Attempt 3**: After 2 minutes

**Attempt 4**: After 10 minutes

**Attempt 5**: After 30 minutes

## Supported events

Here are the events Archefusion currently supports. More events will be added to the list in the future.

| Event               | Description                                                                                                                               |
| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
| `payment.created`   | Fires when a payment is initiated.                                                                                                        |
| `payment.updated`   | Fires whenever a payment status changes. For example, when a payment moves from `INITIATED` to `PENDING`, or from `PENDING` to `SUCCESS`. |
| `payment.succeeded` | Fires when a payment succeeds.                                                                                                            |
| `payment.failed`    | Fires when a payment fails.                                                                                                               |
| `refund.created`    | Fires when a refund is initiated.                                                                                                         |
| `refund.successful` | Fires when a refund is successful.                                                                                                        |
| `refund.failed`     | Fires when a refund fails.                                                                                                                |

## Events payload

<CodeGroup>
  ```json payment.created theme={null}
  {
    "id": "f724a576-eeb0-4c49-ba49-7df1dc9b7133",
    "data": {
      "mode": "test",
      "amount": 1000,
      "status": "INITIATED",
      "currency": "NGN",
      "paymentId": "51134752-88f6-4c8d-a735-7a2d09f4794c",
      "executionStatus": "created",
      "merchantOrderId": "order-1e57-6y72-8u67-90o7",
      "gatewayReference": null
    },
    "type": "payment.created",
    "createdAt": "2026-07-28T10:18:55.403Z",
    "merchantNumber": "ARC-2026-000006"
  }
  ```

  ```json payment.updated theme={null}
  {
    "id": "ba893011-2139-41e7-976c-87f245a3b279",
    "data": {
      "mode": "test",
      "amount": 1000,
      "status": "SUCCESS",
      "currency": "NGN",
      "paymentId": "51134752-88f6-4c8d-a735-7a2d09f4794c",
      "executionStatus": "succeeded",
      "merchantOrderId": "order-1e57-6y72-8u67-90o7",
      "gatewayReference": "51134752-88f6-4c8d-a735-7a2d09f4794c"
    },
    "type": "payment.updated",
    "createdAt": "2026-07-28T10:19:42.898Z",
    "merchantNumber": "ARC-2026-000006"
  }
  ```

  ```json payment.succeeded theme={null}
  {
    "id": "1842221b-1ddd-4915-a2f1-554d38a1b359",
    "data": {
      "mode": "test",
      "amount": 1000,
      "status": "SUCCESS",
      "currency": "NGN",
      "paymentId": "51134752-88f6-4c8d-a735-7a2d09f4794c",
      "executionStatus": "succeeded",
      "merchantOrderId": "order-1e57-6y72-8u67-90o7",
      "gatewayReference": "51134752-88f6-4c8d-a735-7a2d09f4794c"
    },
    "type": "payment.succeeded",
    "createdAt": "2026-07-28T10:19:42.899Z",
    "merchantNumber": "ARC-2026-000006"
  }
  ```

  ```json payment.failed theme={null}
  {
    "id": "b40823fd-ba01-49e7-8105-fd0c55d0fbb8",
    "data": {
      "mode": "test",
      "amount": 1000,
      "status": "FAILED",
      "currency": "NGN",
      "paymentId": "c21b31c3-f1df-4eb1-8fd3-fbfef2b24928",
      "executionStatus": "failed",
      "merchantOrderId": "order-1780323504",
      "gatewayReference": "paystack_c21b31c3-f1df-4eb1-8fd3-fbfef2b24928"
    },
    "type": "payment.failed",
    "createdAt": "2026-06-01T14:30:44.852Z",
    "merchantNumber": "ARC-2026-000006"
  }
  ```

  ```json refund.created theme={null}
  {
    "id": "f4be02f1-8ef6-4a3e-bf4a-f1682522b86d",
    "data": {
      "amount": 250,
      "status": "PROCESSING",
      "currency": "NGN",
      "refundId": "3e754f03-d962-4417-9260-a1b135bf421a",
      "paymentId": "a435d5ea-ef31-4a76-936c-c143d3017ac8",
      "merchantOrderId": "order-1780910581",
      "gatewayReference": "105222"
    },
    "type": "refund.created",
    "createdAt": "2026-06-08T09:25:26.916Z",
    "merchantNumber": "ARC-2026-000006"
  }
  ```

  ```json refund.successful theme={null}
  {
    "id": "35cd9f1c-b01e-4f5d-9faa-9153eb7a7c16",
    "data": {
      "amount": 250,
      "status": "SUCCESSFUL",
      "currency": "NGN",
      "refundId": "3e754f03-d962-4417-9260-a1b135bf421a",
      "paymentId": "a435d5ea-ef31-4a76-936c-c143d3017ac8",
      "merchantOrderId": "order-1780910581",
      "gatewayReference": "105222"
    },
    "type": "refund.successful",
    "createdAt": "2026-06-08T09:45:09.463Z",
    "merchantNumber": "ARC-2026-000006"
  }
  ```

  ```json refund.failed theme={null}
  {
    "id": "8218f10a-cb53-474a-ac99-ac34a79b2801",
    "data": {
      "amount": 100,
      "status": "FAILED",
      "currency": "NGN",
      "refundId": "455d6b73-0818-4ddf-a8c5-364bc9510c6b",
      "paymentId": "cc7ef247-3f13-40c3-a830-5798f72ae8f9",
      "merchantOrderId": "sim_flutterwave_live_1781267766468",
      "gatewayReference": null
    },
    "type": "refund.failed",
    "createdAt": "2026-06-12T13:58:43.178Z",
    "merchantNumber": "ARC-2026-000003"
  }
  ```
</CodeGroup>
