---
description: >-
  Receive real-time notifications when payment statuses change. Configure
  webhooks to automate your payment workflow.
---

# Webhooks

Webhooks deliver real-time notifications to your server when a payment reaches a specific status. Instead of polling the API for updates, your system receives a POST request with the payment details.

## Supported Events

| Event | Trigger | Typical Action |
|---|---|---|
| `AUTHORIZED` | Payment successfully authorized (funds reserved) | Confirm payment method is valid |
| `CAPTURED` | Payment captured and funds settled | Fulfill order, release goods |
| `CHALLENGE` | 3DS challenge initiated; awaiting cardholder verification | Wait for follow-up (`AUTHORIZED` or `DECLINED`) |
| `DECLINED` | Payment rejected by issuer or fraud rules | Prompt user for another method |
| `VOIDED` | Authorization cancelled before capture | Update order as cancelled |
| `REFUNDED` | Funds returned to cardholder (full or partial) | Confirm refund in your system |

## Webhook Configuration

Webhook endpoints are configured during merchant onboarding. Contact the Inyo team to:

1. Provide your webhook URL(s)
2. Select which events you want to receive
3. Configure authentication (Basic auth or Bearer token)

> **Webhook registration API**: Webhooks can also be managed programmatically via the notification endpoints. See the [OpenAPI spec](https://sandbox-gw.simpleps.com/q/swagger-ui/#/) for `POST`, `GET`, and `DELETE /notification` endpoints.

## Webhook Payload

All webhook events deliver the same JSON structure — the full payment object at the time of the event:

```json
{
  "paymentId": "469670e2-31e3-4700-b9f2-abee99418dc4",
  "parentPaymentId": "469670e2-31e3-4700-b9f2-abee99418dc4",
  "externalPaymentId": "order-12345",
  "redirectAcsUrl": "",
  "amount": 95.00,
  "created": "2025-01-21 16:31:02",
  "approved": true,
  "message": "Payment Approved",
  "acquirerMessage": "IN_PROCESS_AUTHORISED",
  "automaticReversed": false,
  "status": "AUTHORIZED",
  "captured": false,
  "voided": false,
  "authCode": "469670e2-31e3-4700-b9f2-abee99418dc4",
  "issuerName": "BANK OF AMERICA",
  "issuerCountry": "UNITED STATES",
  "cvcResult": "NOT_SENT",
  "avsResult": "NOT_SENT",
  "aavAddressResult": "NOT_CHECKED",
  "aavPostcodeResult": "NOT_CHECKED",
  "aavCardholderNameResult": "N/A",
  "aavTelephoneResult": "NOT_CHECKED"
}
```

## Payload Fields

See [Authorizing a Card Payment](payment/pulling-funds/cards/authorizing/) for a full description of all response fields. The webhook payload is identical to the payment response object.

## Handling Webhooks

### Best Practices

1. **Return 200 quickly** — Acknowledge receipt before doing heavy processing. Queue the event for async handling.

2. **Verify the source** — Validate that webhook requests come from Inyo's IP range or verify the authentication credentials.

3. **Handle duplicates** — Webhooks may be delivered more than once. Use `paymentId` + `status` as an idempotency key.

4. **Check `status`, not just `approved`** — The `status` field (`AUTHORIZED`, `CAPTURED`, `DECLINED`, etc.) is the authoritative state of the payment.

5. **Don't rely solely on webhooks** — Always confirm the payment status via the [GET payment endpoint](payment/pulling-funds/listing-payments/by-external-id.md) before fulfilling orders. Webhooks are notifications, not the source of truth.

### Example Handler (Node.js)

```javascript
app.post('/webhooks/inyo', (req, res) => {
  // Acknowledge immediately
  res.status(200).send('OK');

  const payment = req.body;
  const { externalPaymentId, status, paymentId } = payment;

  switch (status) {
    case 'AUTHORIZED':
      // Payment authorized — ready to capture
      markOrderAsAuthorized(externalPaymentId, paymentId);
      break;
    case 'CAPTURED':
      // Funds settled — fulfill the order
      fulfillOrder(externalPaymentId);
      break;
    case 'DECLINED':
      // Payment failed — notify customer
      notifyDecline(externalPaymentId, payment.message);
      break;
    case 'VOIDED':
      // Authorization cancelled
      cancelOrder(externalPaymentId);
      break;
    case 'REFUNDED':
      // Funds returned
      processRefund(externalPaymentId, payment.amount);
      break;
  }
});
```

## Retry Policy

If your endpoint returns a non-2xx response or times out, Inyo will retry delivery with exponential backoff. Ensure your handler is idempotent to handle retries safely.
