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

# Payment Notifications

> The daily list of borrowers whose balance cleared, for lenders who run the ACH themselves

# Payment Notifications Webhook

If you run the ACH yourself, Rightfoot tells you who to debit. Every morning it checks your enrolled borrowers' balances, and sends one signed batch to your webhook listing the borrowers whose balance covers the payment. You debit them through your own setup, then [report the outcomes](https://docs.rightfoot.com/api-reference/payments/upload-payment-results.md).

## Before you start

* [Register your webhook URL](https://docs.rightfoot.com/api-reference/payments/create-payment-connection.md#run-the-ach-yourself) as your default ACH key, and store the `signing_secret` from the response. It is shown only once.
* [Enroll borrowers](https://docs.rightfoot.com/api-reference/payments/create-payment-enrollments.md). Your collection policy and any `policy_overrides` decide when a balance is high enough.
* If your endpoint restricts inbound traffic by IP, [allowlist Rightfoot's IPs](https://docs.rightfoot.com/api-reference/webhooks/ip-allowlist.md).

## How it works

1. **Each morning at 7:30 a.m. ET**, Rightfoot starts checking the balances of your enrolled borrowers.
2. **Once those checks finish**, it sends one batch for the day. Every borrower whose balance cleared is in it, with the amount to debit and the balance it saw.
3. **You run the ACH** for each entry, for `amount_cents`, on the `debit_by` date. Store `payment_reference` with the payment in your system. It does not need to go on the ACH entry itself.
4. **You report each outcome** with [Report Payment Results](https://docs.rightfoot.com/api-reference/payments/upload-payment-results.md), keyed on `payment_reference`: `settled`, `returned` with its R-code and return date, or `not_attempted` if you did not run it.

> [!NOTE]
>
> A batch is sent every day, even when nobody cleared. An empty `notifications` list means a quiet day. No batch at all means something is wrong.

## Payload

```json theme={null}
{
  "type": "payment_notifications.batch",
  "batch_id": "6f1c2a7e-3b4d-4e8f-9a01-b2c3d4e5f607",
  "created_at": "2026-11-10T13:12:04Z",
  "notifications": [
    {
      "payment_reference": "rfpay-retry-3f9a6c1e-2b7d-4c8e-9f10-a1b2c3d4e5f6",
      "borrower_id": "1a2b3c4d",
      "obligation_ref": null,
      "amount_cents": 42500,
      "balance_cents": 118734,
      "balance_read_at": "2026-11-10T12:41:19Z",
      "debit_by": "2026-11-10"
    }
  ]
}
```

| Field | Description |
| - | - |
| `type` | Always `payment_notifications.batch` |
| `batch_id` | Identifies this batch. The same on every delivery attempt; use it to ignore a batch you have already processed |
| `created_at` | When the batch was put together (UTC) |
| `notifications` | One entry per debit to run. Empty on a day nobody cleared |
| `payment_reference` | Rightfoot's reference for this debit, an opaque string. Store it with the payment, and send it as `reference` when you report the outcome |
| `borrower_id` | The `authorizer_unique_id` you [enrolled](https://docs.rightfoot.com/api-reference/payments/create-payment-enrollments.md) the borrower with |
| `obligation_ref` | For a borrower enrolled with [`obligations`](https://docs.rightfoot.com/api-reference/payments/create-payment-enrollments#collecting-several-obligations-separately), the `id` of the obligation this debit collects. `null` otherwise |
| `amount_cents` | The amount to debit, in cents. `42500` is \$425.00 |
| `balance_cents` | The balance Rightfoot read that cleared the debit, in cents |
| `balance_read_at` | When that balance was read (UTC) |
| `debit_by` | The date (ET) the debit was decided. Run it that day: the balance was read that morning |

## Verifying the signature

Each request carries a `Rightfoot-Signature` header:

```text theme={null}
Rightfoot-Signature: t=1762780324,v1=5d2f0c...e91a
```

`t` is the Unix time the request was sent. `v1` is the hex HMAC-SHA256, keyed with your `signing_secret`, of `t`, a period, and the raw request body. To verify a request:

1. Split the header on `,` and read `t` and `v1`.
2. Compute the HMAC over `<t>.<raw body>`, using the body exactly as received, before any JSON parsing.
3. Compare it to `v1` with a constant-time comparison.
4. Reject the request if `t` is more than a few minutes old, so a captured request cannot be replayed.

```python Python theme={null}
import hashlib
import hmac
import time

TOLERANCE_SECONDS = 300


def verify(signing_secret: str, header: str, raw_body: bytes) -> bool:
    parts = dict(item.split("=", 1) for item in header.split(","))
    timestamp, signature = parts["t"], parts["v1"]
    if abs(time.time() - int(timestamp)) > TOLERANCE_SECONDS:
        return False
    expected = hmac.new(
        signing_secret.encode(),
        f"{timestamp}.".encode() + raw_body,
        hashlib.sha256,
    ).hexdigest()
    return hmac.compare_digest(expected, signature)
```

## Responding and retries

Respond with any `2xx` status once you have stored the batch. Rightfoot does not follow redirects, and waits up to 30 seconds for a response.

Anything else, a timeout included, is retried with backoff for about two hours. A batch still undelivered after that is held and sent again the next morning, under the same `batch_id`, before that day's batch. Every attempt is signed again with a fresh `t`; the body is the same each time.

> [!WARNING]
>
> Delivery is at least once. You can receive the same `batch_id` more than once, so check whether you have already processed it before running any debits.

## Reporting outcomes

Until you report an outcome for a `payment_reference`, Rightfoot treats that debit as in flight and does not notify you about that borrower again. Report every debit in the batch, including the ones you did not run (`not_attempted`).

Upload results at least once a day. Each upload, even one with only the header row, tells Rightfoot your results are current. If no upload arrives for 36 hours, Rightfoot stops notifying you of new debits until one does.

## Rotating the secret or changing the URL

To change the URL or rotate the secret, [replace the key's credentials](https://docs.rightfoot.com/api-reference/payments/update-payment-connection.md#replace-a-key) with `credentials: {"url": "..."}`. The response carries a new `signing_secret`, and every request from then on is signed with it. The previous secret stops being used immediately, so update your endpoint as soon as you have the new one.


## OpenAPI

````yaml WEBHOOK payment_notifications.batch
openapi: 3.1.0
info:
  title: Rightfoot API
  description: >-
    Submit a batch of authorizers for balance checks, retrieve processed
    balances, and check the status of a batch.
  version: 0.3.0-alpha
servers:
  - url: https://api.rightfoot.com
security:
  - BearerAuth: []
tags:
  - name: General Availability
    description: |
      **General Availability** indicates that the endpoint is production-ready.
  - name: In Development
    description: >
      **In Development** indicates that the endpoint is currently being built
      and will be available in an upcoming release.
paths: {}
components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: >
        Authentication to the API is performed via Bearer Token Authentication.
        Provide your API key as the bearer token in the Authorization header.


        All API requests must be made over HTTPS. Calls made over plain HTTP
        will fail. API requests without authentication will also fail.

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.