> For the complete documentation index, see [llms.txt](https://docs.tylt.money/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tylt.money/tylt-crossramp-fiat-crypto-solutions/philippines-php/administration/webhook-for-administrators.md).

# Webhook for Administrators

Tylt provides a webhook mechanism for approved rail partners to receive real-time updates on the status of payment instances for both pay-ins and pay-outs. For the Philippines PHP rail, this webhook page applies to:

| Flow                       | Description                                                                                            |
| -------------------------- | ------------------------------------------------------------------------------------------------------ |
| QRPH Pay-In                | PHP pay-in transaction where the end user pays in PHP and the merchant receives USDT settlement value. |
| InstaPay / PESONet Pay-Out | PHP pay-out transaction where USDT value is used to initiate a PHP disbursement.                       |

Tylt sends webhook notifications whenever there is a status change in the transaction lifecycle. The lifecycle is tracked using `eventId`, which identifies the current state of the payment instance. The existing Tylt webhook page states that callbacks are sent on transaction status changes and that the lifecycle is tracked through `eventId`.&#x20;

***

### Setting Up the Webhook

The partner must provide Tylt with an HTTP POST endpoint that can receive JSON webhook payloads.

The webhook endpoint should be capable of:

1. Receiving JSON payloads from Tylt.
2. Verifying the authenticity of the webhook using HMAC-SHA256 signature validation.
3. Processing the transaction status update based on the `eventId`.
4. Returning an HTTP `200` response with `ok` in the response body.

Tylt signs each webhook payload using HMAC-SHA256 and sends the signature in the `X-TLP-SIGNATURE` header. The existing webhook page also specifies that the callback endpoint must validate the signature and acknowledge the webhook with HTTP 200 and body `"ok"`.&#x20;

***

### Webhook Header

| Header            | Description                                                                               |
| ----------------- | ----------------------------------------------------------------------------------------- |
| `X-TLP-SIGNATURE` | HMAC-SHA256 signature generated using the raw POST data and the partner’s API secret key. |

***

### Pay-In Lifecycle

For QRPH pay-ins, Tylt sends webhook updates as the pay-in instance moves through the payment lifecycle.

```
eventId 1 → Instance Created
eventId 2 → Order Created
eventId 3 → Payment Processing
eventId 4 → Payment Completed
```

Exception or unsuccessful states may include:

```
eventId 8  → Payment Failed
eventId 9  → Order Cancelled or Expired
eventId 10 → KYC Failed
```

The partner should use the `instanceId`, `merchantOrderId`, and `eventDetails.eventId` to identify and process the pay-in update.

***

### Pay-Out Lifecycle

For InstaPay / PESONet pay-outs, Tylt sends webhook updates as the pay-out instance moves through the payment lifecycle.

```
eventId 1 → Instance Created
eventId 2 → Order Created
eventId 3 → Payment Processing
eventId 4 → Payment Completed
```

Exception or unsuccessful states may include:

```
eventId 8  → Payment Failed
eventId 9  → Order Cancelled or Expired
eventId 10 → KYC Failed
```

The partner should use the `instanceId`, `merchantOrderId`, and `eventDetails.eventId` to identify and process the pay-out update.

***

### Validating Webhooks

Partners should validate the HMAC signature included in the `X-TLP-SIGNATURE` header to confirm that the webhook was sent by Tylt and that the payload has not been modified.

The HMAC signature is generated using:

```
HMAC-SHA256(raw POST data, API_SECRET)
```

The generated signature should be compared with the value received in the `X-TLP-SIGNATURE` header.

***

### Example Webhook Handling Code

```javascript
const express = require("express");
const crypto = require("crypto");

const app = express();
const PORT = 3000;
const apiSecretKey = "YOUR_TLP_API_SECRET_KEY";

// Middleware to parse incoming JSON requests
app.use(express.json());

// Callback endpoint
app.post("/callback", (req, res) => {
  const data = req.body;

  const tlpSignature = req.headers["x-tlp-signature"];

  const calculatedHmac = crypto
    .createHmac("sha256", apiSecretKey)
    .update(JSON.stringify(data))
    .digest("hex");

  if (calculatedHmac === tlpSignature) {
    console.log("Received Tylt webhook:", data);

    const eventId = data?.data?.eventDetails?.eventId;
    const instanceId = data?.data?.instanceId;
    const merchantOrderId = data?.data?.merchantOrderId;

    console.log("Event ID:", eventId);
    console.log("Instance ID:", instanceId);
    console.log("Merchant Order ID:", merchantOrderId);

    // Process the webhook based on eventId
    // 1  = Instance Created
    // 2  = Order Created
    // 3  = Payment Processing
    // 4  = Payment Completed
    // 8  = Payment Failed
    // 9  = Order Cancelled or Expired
    // 10 = KYC Failed

    return res.status(200).send("ok");
  }

  return res.status(400).send("Invalid HMAC signature");
});

app.listen(PORT, () => {
  console.log(`Server listening on port ${PORT}`);
});
```

### Webhook Acknowledgement

After receiving and validating the webhook, the partner must return:

```
HTTP 200
```

with the response body:

```
ok
```

If Tylt does not receive this acknowledgement, the webhook will not be retried automatically. Missed callbacks can be resent manually from the Tylt dashboard. ([Tylt Documentation](https://docs.tylt.money/introduction/tylt-crossramp-fiat-less-than-greater-than-crypto-solutions/philippines-php-english/instapay-pesonet-pay-out-widget/webhook-for-tylt-crossramp-pay-out))

***

### Important Considerations

| Area                 | Requirement                                                                                                           |
| -------------------- | --------------------------------------------------------------------------------------------------------------------- |
| Signature validation | Always verify the `X-TLP-SIGNATURE` header before processing the webhook.                                             |
| Response             | Always return HTTP `200` with `ok` in the response body after successful receipt.                                     |
| Manual retry         | If a callback is missed, the webhook can be manually resent from the Tylt dashboard.                                  |
| Lifecycle handling   | Use `eventDetails.eventId` to determine the current transaction state.                                                |
| Reconciliation       | Store `instanceId`, `merchantOrderId`, `eventId`, fiat amount, crypto amount, rate, and timestamp for reconciliation. |

***
