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

# Webhooks

> Register a webhook URL and understand the incoming payment notification payload.

Interstellas sends real-time payment notifications to a URL you register. When a virtual account receives a payment, Interstellas POSTs a notification to your endpoint.

## Registering your webhook URL

```
PATCH /clients/settings/update-webhook-url
```

### Headers

<ParamField header="Authorization" type="string" required>
  `Bearer YOUR_ACCESS_TOKEN`
</ParamField>

<ParamField header="SECRET_KEY" type="string" required>
  Your API secret key.
</ParamField>

<ParamField header="businessId" type="string" required>
  Your business ID.
</ParamField>

### Request body

<ParamField body="webhookUrl" type="string" required>
  The HTTPS URL that Interstellas will POST payment notifications to. Must be publicly accessible.
</ParamField>

### Response

<ResponseField name="status" type="boolean">
  `true` on success.
</ResponseField>

<ResponseField name="message" type="string">
  Confirmation message, e.g. `"Webhook Url updated successfully"`.
</ResponseField>

### Code example

<CodeGroup>
  ```bash cURL theme={null}
  curl -X PATCH https://sandbox.stellasbank.com/api/v1/clients/settings/update-webhook-url \
    -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
    -H "SECRET_KEY: YOUR_SECRET_KEY" \
    -H "businessId: YOUR_BUSINESS_ID" \
    -H "Content-Type: application/json" \
    -d '{
      "webhookUrl": "https://yourdomain.com/webhooks/interstellas"
    }'
  ```

  ```js Node.js theme={null}
  const res = await fetch(
    "https://sandbox.stellasbank.com/api/v1/clients/settings/update-webhook-url",
    {
      method: "PATCH",
      headers: {
        Authorization: "Bearer YOUR_ACCESS_TOKEN",
        SECRET_KEY: "YOUR_SECRET_KEY",
        businessId: "YOUR_BUSINESS_ID",
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ webhookUrl: "https://yourdomain.com/webhooks/interstellas" }),
    }
  );
  const json = await res.json();
  ```

  ```python Python theme={null}
  import requests

  res = requests.patch(
      "https://sandbox.stellasbank.com/api/v1/clients/settings/update-webhook-url",
      headers={
          "Authorization": "Bearer YOUR_ACCESS_TOKEN",
          "SECRET_KEY": "YOUR_SECRET_KEY",
          "businessId": "YOUR_BUSINESS_ID",
      },
      json={"webhookUrl": "https://yourdomain.com/webhooks/interstellas"},
  )
  print(res.json())
  ```
</CodeGroup>

***

## Incoming payment notification

When a payment is received into one of your virtual accounts, Interstellas sends a `POST` request to your registered webhook URL.

### Verifying the request

Incoming webhook requests include an `AUTH-KEY` header provided by Interstellas. Validate this header on your server to confirm the request originated from Interstellas and not a third party.

<Warning>
  **TODO** — The source documentation does not specify the format of the `AUTH-KEY` value or how to validate it. Confirm this with the Interstellas team before going live.
</Warning>

### Payload fields

<ResponseField name="transactionReference" type="string">
  The unique reference for this transaction, generated by Interstellas.
</ResponseField>

<ResponseField name="virtualAccountRef" type="string">
  The reference for the virtual account that received the payment.
</ResponseField>

<ResponseField name="transactionDate" type="string">
  ISO 8601 timestamp of when the transaction occurred.
</ResponseField>

<ResponseField name="amount" type="integer">
  Amount received, in **kobo**. Divide by 100 to convert to naira.
</ResponseField>

<ResponseField name="charge" type="integer">
  Fee deducted from the payment, in kobo.
</ResponseField>

<ResponseField name="availableBalance" type="integer">
  Account balance available after the transaction, in kobo.
</ResponseField>

<ResponseField name="ledgerBalance" type="integer">
  Total ledger balance after the transaction, in kobo.
</ResponseField>

<ResponseField name="accountName" type="string">
  Name on the paying account.
</ResponseField>

<ResponseField name="accountNumber" type="string">
  The paying account number.
</ResponseField>

<ResponseField name="recipientAccountNumber" type="string">
  The virtual account number that received the payment.
</ResponseField>

<ResponseField name="recpientAccountName" type="string">
  The name on the receiving virtual account.
</ResponseField>

<ResponseField name="description" type="string">
  Transaction description.
</ResponseField>

<ResponseField name="narration" type="string">
  Narration provided by the payer.
</ResponseField>

<ResponseField name="depositorDetails" type="object">
  Additional details about the payer.
</ResponseField>

### Example payload

```json theme={null}
{
  "transactionReference": "TXN_REF_ABC001",
  "virtualAccountRef": "VA_REF_XYZ789",
  "transactionDate": "2024-06-09T14:22:00.000Z",
  "amount": 500000,
  "charge": 10000,
  "availableBalance": 4500000,
  "ledgerBalance": 4500000,
  "accountName": "Grace Hopper",
  "accountNumber": "0123456789",
  "recipientAccountNumber": "9012345678",
  "recpientAccountName": "Ada Lovelace",
  "description": "Payment for order_12345",
  "narration": "Invoice #INV-001",
  "depositorDetails": {}
}
```

### Required response

Your endpoint must return `HTTP 200 OK` to acknowledge receipt. If Interstellas does not receive a `200`, it will retry the notification.

```js Node.js (Express) theme={null}
app.post("/webhooks/interstellas", (req, res) => {
  const authKey = req.headers["auth-key"];
  // TODO: validate authKey against your expected value

  const payload = req.body;
  // Process the payment notification
  console.log("Payment received:", payload.transactionReference);

  res.status(200).send("OK");
});
```
