> ## 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.

# Enroll and Update Borrowers

> Enroll borrowers and update them by re-sending — one route for your whole file

# Enroll and Update Borrowers

Enroll a batch of borrowers in a single call. Rightfoot stores each borrower, monitors their balance, and acts once there is enough in the account to collect.

This is also the update route: send a borrower you have already enrolled and their details and amount are refreshed in place. There is no separate update call.

Send up to 1000 borrowers per request.

## Send your whole file, every day

This endpoint is an **upsert** — re-sending a borrower is how you update them. Someone already being monitored comes back as `updated`: their details are refreshed and the `collectable_amount` in that request is applied to the monitor they already have.

That means you do not need to track what you have already sent, or which borrowers changed. Push your full delinquent file on whatever cadence suits you and let the response tell you what happened to each borrower. To stop monitoring a borrower altogether, use [Remove Borrowers from Monitoring](/api-reference/payments/delete-payment-enrollments).

### What a re-send changes

| Sent again                       | Effect on a monitored borrower                                                                  |
| -------------------------------- | ----------------------------------------------------------------------------------------------- |
| `collectable_amount`             | Applied to the existing monitor. Rightfoot works toward the new figure from the next payment on |
| Identity and bank account fields | Re-stored. The borrower keeps a single record however many times you send them                  |
| `merchant_ref`                   | Replaced                                                                                        |
| `charge_instrument`              | Replaced. Omitting it for a borrower you previously sent with a card moves them back to ACH     |

This is what to use when a borrower pays you directly and the amount you will accept drops: send them again with the lower `collectable_amount`.

<Warning>
  An update applies to the **next** payment, not to one already in flight. If Rightfoot has a payment out at the bank for this borrower, that payment runs to its original amount, and the new amount is picked up only if it comes back.
</Warning>

<Note>
  An unchanged re-send is reported `updated` as well. Rightfoot cannot tell "re-sent unchanged" from "re-sent with edits" — the stored details are encrypted and cannot be read back to compare — so read `updated` as "this borrower is monitored against the amount you just sent", not as "something about them changed".
</Note>

## Monitoring lapses if you stop sending

Monitoring expires 30 days after it starts. Sending a borrower again after their monitoring has expired starts fresh monitoring, using the `collectable_amount` in that request.

Re-sending a borrower who is *still* being monitored updates them but does not extend that window — the 30 days run from when monitoring started, not from your last push.

Your regular push is therefore what keeps monitoring alive. If a borrower drops out of your file, monitoring for them lapses within 30 days.

## What you send for each borrower

The identity and bank account fields are the same ones the balance endpoints accept, so an existing integration can reuse its serialization.

| Field                              | Required | Notes                                                                                                                           |
| ---------------------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `authorizer_unique_id`             | Yes      | Your own identifier for this borrower                                                                                           |
| `first_name`, `last_name`          | Yes      |                                                                                                                                 |
| `phone_number`                     | Yes      |                                                                                                                                 |
| `date_of_birth`                    | Yes      | Object with `day`, `month`, `year`                                                                                              |
| `address`                          | Yes      | Only `zip_code` is required within it                                                                                           |
| `ssn` or `ein`                     | Yes      | One of the two                                                                                                                  |
| `account_number`, `routing_number` | Yes      | See below                                                                                                                       |
| `collectable_amount`               | Yes      | Cents. The most you will accept today                                                                                           |
| `days_past_due`                    | Yes      | As of this request                                                                                                              |
| `merchant_ref`                     | No       | The tag of the [processor key](/api-reference/payments/create-payment-connection) to settle to. Omit it to use your default key |
| `charge_instrument`                | No       | See [How Rightfoot collects](#how-rightfoot-collects)                                                                           |

<Note>
  **Keep `authorizer_unique_id` stable for the life of the loan.** Monitoring history accumulates against it. An identifier that changes between pushes resets that history, and monitoring quietly degrades rather than failing.
</Note>

### The bank account is always required

`account_number` and `routing_number` are required for every borrower, including those you intend to charge by card. They are what Rightfoot uses to read the balance, and no charge instrument substitutes for them.

### The amount is a ceiling, in cents

`collectable_amount` is an integer number of cents — `42500` is \$425.00. Rightfoot treats it as a **ceiling**, and will not attempt more than that in a single payment.

## How Rightfoot collects

Omit `charge_instrument` entirely and Rightfoot collects by ACH from the bank account you supplied. That is the common case.

To have a card charged instead, supply exactly one of:

<CardGroup cols={2}>
  <Card title="processor_token" icon="shield-check">
    A token held by your payment processor. Preferred — no sensitive card data leaves your systems. Send `card_last4` alongside it.
  </Card>

  <Card title="card" icon="credit-card">
    Raw card details, stored securely by Rightfoot. Use this only when you cannot produce a processor token.
  </Card>
</CardGroup>

<Note>
  `card_last4` is required with `processor_token`. The token itself is opaque to Rightfoot, so the last four digits are what confirm the card belongs to the account being monitored — a card drawing on a different account would make the balance Rightfoot reads meaningless.
</Note>

Each borrower in the same request can use a different option. Only the `charge_instrument` differs — the identity and bank account fields are the same either way.

<CodeGroup>
  ```json ACH theme={null}
  {
    "authorizer_unique_id": "1a2b3c4d",
    "account_number": "1234567890",
    "routing_number": "021000021",
    "collectable_amount": 42500,
    "days_past_due": 47
  }
  ```

  ```json Processor token theme={null}
  {
    "authorizer_unique_id": "5e6f7g8h",
    "account_number": "9876543210",
    "routing_number": "071000013",
    "collectable_amount": 18000,
    "days_past_due": 22,
    "charge_instrument": {
      "processor_token": "tok_9f3a21c7",
      "card_last4": "4412"
    }
  }
  ```

  ```json Raw card theme={null}
  {
    "authorizer_unique_id": "9i0j1k2l",
    "account_number": "5544332211",
    "routing_number": "111000025",
    "collectable_amount": 7500,
    "days_past_due": 9,
    "charge_instrument": {
      "card": {
        "debit_card_number": "4111111111111111",
        "cvv": "123",
        "expiration_month": 4,
        "expiration_year": 2030
      }
    }
  }
  ```
</CodeGroup>

<Warning>
  These snippets show only the fields that differ. Identity fields (`first_name`, `last_name`, `phone_number`, `date_of_birth`, `address`, and `ssn` or `ein`) are required on every borrower — see the full example above.
</Warning>

## Reading the response

The response is `207`, and every borrower you sent has an entry in `results`, in the order you sent them. **Check each `outcome` rather than relying on the status code** — one bad record never blocks the rest of the batch.

| Outcome    | What it means                                                                           | What to do   |
| ---------- | --------------------------------------------------------------------------------------- | ------------ |
| `enrolled` | Stored, and monitoring started                                                          | Nothing      |
| `updated`  | Monitoring was already running; details refreshed and the amount you sent applied to it | Nothing      |
| `held`     | Stored, but not monitored — Rightfoot cannot read this bank                             | See `reason` |
| `failed`   | The details in this request were not stored                                             | See `reason` |

<Note>
  `already_enrolled` is no longer returned. It stays in the response as a count that is always `0` so that an integration parsing the old shape keeps working — a re-sent borrower is reported `updated` instead.
</Note>

### Held borrowers

A `held` borrower's details are kept. Monitoring simply did not start, because Rightfoot cannot reach their bank.

| Reason                        | Meaning                                                                                                                                                                    |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `institution_not_supported`   | Rightfoot does not currently support this bank. Your data is correct; there is no action to take. Keep sending them — monitoring starts automatically if support is added. |
| `unresolvable_routing_number` | The routing number did not match a known bank. Check it.                                                                                                                   |

### Failed borrowers

| Reason                     | Meaning                                                                                                              |
| -------------------------- | -------------------------------------------------------------------------------------------------------------------- |
| `duplicate_in_request`     | This identifier appeared more than once in the request. Every copy is rejected rather than guessing which you meant. |
| `tokenization_failed`      | The borrower's details were rejected. `message` names the problem — correct it and send again.                       |
| `card_tokenization_failed` | The card details were rejected. See `message`.                                                                       |
| `chunk_failed`             | A temporary failure affecting part of the batch. Send these borrowers again.                                         |
| `monitor_open_failed`      | The borrower was stored but monitoring did not start. Send them again.                                               |

<Note>
  `chunk_failed` and `monitor_open_failed` are both safe to retry — re-sending a borrower who did enroll returns `updated` and re-applies the same amount, rather than creating anything twice.
</Note>


## OpenAPI

````yaml POST /v1/payment_enrollments
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: Early Access
    description: >
      **Early Access** indicates that the endpoint is available for early access
      customers and is being actively refined.
  - name: In Development
    description: >
      **In Development** indicates that the endpoint is currently being built
      and will be available in an upcoming release.
paths:
  /v1/payment_enrollments:
    post:
      tags:
        - In Development
      summary: Enroll or update borrowers in payment monitoring
      description: >
        Enroll a batch of borrowers so Rightfoot monitors each one's balance and
        acts

        when funds are available to collect.


        **Upsert.** Re-sending a borrower is how you update them: their details
        are

        refreshed and the `collectable_amount` in that request is applied to the

        monitor they already have, and they come back as `updated`. So you can
        send

        your full delinquent file every day without tracking what you have sent

        before, or which borrowers changed. An update applies to the next
        payment,

        not to one already in flight.


        **Partial success.** One bad record never blocks the rest of the batch.
        The

        response is `207` and every borrower carries its own outcome, so you can
        act

        on each independently.


        **Monitoring lifetime.** Monitoring expires 30 days after it starts.
        Sending a

        borrower again once their monitoring has expired starts fresh monitoring
        using

        the amount in that request; re-sending one who is still monitored
        updates them

        but does not extend the window. Your daily push is therefore what keeps

        monitoring alive — if you stop sending a borrower, monitoring for them
        lapses

        within 30 days.
      operationId: create_payment_enrollments
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/PaymentEnrollmentRequest'
            example:
              borrowers:
                - authorizer_unique_id: 1a2b3c4d
                  first_name: John
                  last_name: Doe
                  phone_number: '5551234567'
                  date_of_birth:
                    day: 14
                    month: 3
                    year: 1988
                  address:
                    street_address_line1: 123 Main St
                    city: Anytown
                    state: CA
                    zip_code: '12345'
                    country: US
                  ssn: '123456789'
                  account_number: '1234567890'
                  routing_number: '021000021'
                  collectable_amount: 42500
                  days_past_due: 47
                  merchant_ref: acme-nv
                - authorizer_unique_id: 5e6f7g8h
                  first_name: Maria
                  last_name: Alvarez
                  phone_number: '5559876543'
                  date_of_birth:
                    day: 2
                    month: 11
                    year: 1979
                  address:
                    street_address_line1: 88 Oak Ave
                    street_address_line2: Apt 4B
                    city: Springfield
                    state: IL
                    zip_code: '62701'
                    country: US
                  ssn: '987654321'
                  account_number: '9876543210'
                  routing_number: '071000013'
                  collectable_amount: 18000
                  days_past_due: 22
                  charge_instrument:
                    processor_token: tok_9f3a21c7
                    card_last4: '4412'
                - authorizer_unique_id: 9i0j1k2l
                  first_name: Andre
                  last_name: Boateng
                  phone_number: '5552223333'
                  date_of_birth:
                    day: 30
                    month: 6
                    year: 1991
                  address:
                    street_address_line1: 500 Pine Rd
                    city: Austin
                    state: TX
                    zip_code: '78701'
                    country: US
                  ssn: '456789123'
                  account_number: '5544332211'
                  routing_number: '111000025'
                  collectable_amount: 7500
                  days_past_due: 9
                  charge_instrument:
                    card:
                      debit_card_number: '4111111111111111'
                      cvv: '123'
                      expiration_month: 4
                      expiration_year: 2030
      responses:
        '207':
          description: >
            Batch processed. Every borrower in the request has an entry in
            `results`;

            inspect each `outcome` rather than relying on the status code.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/PaymentEnrollmentResponse'
              example:
                enrolled: 2
                updated: 1
                already_enrolled: 0
                held: 0
                failed: 0
                results:
                  - authorizer_unique_id: 1a2b3c4d
                    outcome: enrolled
                    reason: null
                    message: null
                  - authorizer_unique_id: 5e6f7g8h
                    outcome: updated
                    reason: null
                    message: null
                  - authorizer_unique_id: 9i0j1k2l
                    outcome: enrolled
                    reason: null
                    message: null
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
              example:
                status: error
                status_code: 400
                error:
                  code: BAD_REQUEST
                  message: borrowers must contain between 1 and 1000 entries.
                  timestamp: '2024-12-12T12:00:00Z'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ApiErrorResponse'
              example:
                status: error
                status_code: 401
                error:
                  code: UNAUTHORIZED
                  message: Invalid or missing API key.
                  timestamp: '2024-12-12T12:00:00Z'
components:
  schemas:
    PaymentEnrollmentRequest:
      type: object
      required:
        - borrowers
      properties:
        borrowers:
          type: array
          minItems: 1
          maxItems: 1000
          description: The borrowers to enroll. Up to 1000 per request.
          items:
            $ref: '#/components/schemas/PaymentEnrollmentBorrower'
    PaymentEnrollmentResponse:
      type: object
      description: Counts plus one result per borrower, in the order you sent them.
      properties:
        enrolled:
          type: integer
          description: Borrowers now stored, with monitoring newly started.
        updated:
          type: integer
          description: >
            Borrowers who were already being monitored. Their details were
            refreshed

            and the amount in this request applied to their existing monitor.
        already_enrolled:
          type: integer
          description: |
            Always `0`. Kept so the response shape stays stable for integrations
            parsing it; a re-sent borrower is reported under `updated` instead.
        held:
          type: integer
          description: Borrowers stored but not monitored.
        failed:
          type: integer
          description: Borrowers whose details from this request were not stored.
        results:
          type: array
          items:
            $ref: '#/components/schemas/PaymentEnrollmentResult'
    ApiErrorResponse:
      type: object
      properties:
        status:
          type: string
          description: Status of the error, either "error" or "success"
        status_code:
          type: integer
          description: HTTP status code of the error
        error:
          type: object
          properties:
            code:
              type: string
              description: Error code (e.g., "RESOURCE_NOT_FOUND")
            message:
              type: string
              description: Detailed error message.
            details:
              type: string
              description: Additional details about the error
            timestamp:
              type: string
              format: date-time
              description: Time the error occurred
            suggestion:
              type: string
              description: Suggested action to resolve the error
        documentation_url:
          type: string
          format: uri
          description: URL to the documentation for the error
    PaymentEnrollmentBorrower:
      type: object
      description: >
        One borrower to enroll. The identity and bank account fields match the

        `Authorizer` schema used by the balance endpoints, so an existing
        integration

        can reuse the same serialization.
      required:
        - authorizer_unique_id
        - first_name
        - last_name
        - phone_number
        - date_of_birth
        - address
        - account_number
        - routing_number
        - collectable_amount
        - days_past_due
      anyOf:
        - required:
            - ssn
        - required:
            - ein
      properties:
        authorizer_unique_id:
          type: string
          description: >
            Your own identifier for this borrower. It must stay the same for
            this

            borrower for the life of the loan — monitoring history accumulates

            against it, so an identifier that changes between pushes resets that

            history and monitoring degrades without failing.
        first_name:
          type: string
          description: The borrower's first name.
        last_name:
          type: string
          description: The borrower's last name.
        phone_number:
          type: string
          description: The borrower's phone number.
        date_of_birth:
          type: object
          description: Object containing day, month, and year of birth.
          required:
            - day
            - month
            - year
          properties:
            day:
              type: integer
              description: The day of birth, between 1 and 31.
            month:
              type: integer
              description: The month of birth, between 1 and 12.
            year:
              type: integer
              description: The four-digit year of birth.
        address:
          type: object
          required:
            - zip_code
          properties:
            street_address_line1:
              type: string
              description: Address line 1 (e.g., street, PO Box, or company name).
            street_address_line2:
              type: string
              description: Address line 2 (e.g., apartment, suite, unit, or building).
            city:
              type: string
              description: City, district, suburb, town, or village.
            state:
              type: string
              description: State, county, province, or region.
            zip_code:
              type: string
              description: >-
                ZIP or postal code. Must be 5 digits or in 5+4 format (e.g.,
                94107 or 94107-1234).
              pattern: ^\d{5}(?:-\d{4})?$
            country:
              type: string
              description: Two-letter country code.
        ssn:
          type: string
          description: >-
            The borrower's Social Security Number. Either SSN or EIN is
            required.
        ein:
          type: string
          description: >-
            The borrower's Employer Identification Number. Either SSN or EIN is
            required.
        account_number:
          type: string
          description: >
            The borrower's bank account number. Always required, including when
            you

            supply a card as the charge instrument — this is what Rightfoot uses
            to

            read the balance, and no charge instrument substitutes for it.
        routing_number:
          type: string
          pattern: ^\d{9}$
          description: The routing number for the bank account. Always required.
        collectable_amount:
          type: integer
          minimum: 1
          description: >
            The most you will accept against this borrower today, in cents

            (`42500` is $425.00). Rightfoot treats this as a ceiling and will
            not

            attempt more than this in a single payment.
        days_past_due:
          type: integer
          minimum: 0
          description: How many days past due this borrower is, as of this request.
        merchant_ref:
          type: string
          description: >
            Optional. The tag of the processor key this borrower should settle
            to, if you hold

            more than one. Omit it and Rightfoot uses your default key for the
            rail

            being charged.
        charge_instrument:
          $ref: '#/components/schemas/PaymentChargeInstrument'
    PaymentEnrollmentResult:
      type: object
      description: The outcome for a single borrower.
      properties:
        authorizer_unique_id:
          type: string
          description: The identifier you sent for this borrower.
        outcome:
          type: string
          enum:
            - enrolled
            - updated
            - already_enrolled
            - held
            - failed
          description: >
            `enrolled` — stored, and monitoring started.


            `updated` — monitoring was already running. The borrower's details
            were

            refreshed and the amount in this request applied to that monitor. An

            unchanged re-send is reported this way too.


            `already_enrolled` — no longer returned. Retained in the enum so
            that

            integrations parsing the old shape keep working.


            `held` — the borrower's details were stored, but monitoring did not
            start

            because Rightfoot cannot read their bank. See `reason`.


            `failed` — the details in this request were not stored. See
            `reason`.
        reason:
          type:
            - string
            - 'null'
          enum:
            - null
            - institution_not_supported
            - unresolvable_routing_number
            - duplicate_in_request
            - tokenization_failed
            - card_tokenization_failed
            - chunk_failed
            - monitor_open_failed
          description: >
            Why, for `held` and `failed` outcomes. `null` otherwise.


            `institution_not_supported` — Rightfoot does not currently support
            this

            bank. Your data is fine; no action available.


            `unresolvable_routing_number` — the routing number did not match a
            known

            bank. Check it.


            `duplicate_in_request` — this identifier appeared more than once in
            the

            request. Every copy is rejected rather than guessing which you
            meant.


            `tokenization_failed` — the borrower's details were rejected. See

            `message` for the specific problem, correct it, and send again.


            `card_tokenization_failed` — the card details were rejected. See
            `message`.


            `chunk_failed` — a temporary failure affecting part of the batch.
            Send

            these borrowers again.


            `monitor_open_failed` — the borrower was stored but monitoring did
            not

            start. Send them again.
        message:
          type:
            - string
            - 'null'
          description: A human-readable explanation, when one is available.
    PaymentChargeInstrument:
      type: object
      description: >
        Optional. How Rightfoot should take payment. Omit it entirely to be
        charged

        by ACH against the bank account you already supplied.


        Supply exactly one of `card` or `processor_token`. Sending both,
        neither, or

        a `processor_token` without `card_last4` is rejected.
      oneOf:
        - title: Processor token
          required:
            - processor_token
            - card_last4
          not:
            required:
              - card
        - title: Raw card
          required:
            - card
          not:
            required:
              - processor_token
      properties:
        processor_token:
          type: string
          description: >
            A token held by your payment processor that represents the card.
            Preferred

            over sending raw card details — nothing sensitive leaves your
            systems.

            Requires `card_last4`.
        card_last4:
          type: string
          description: >
            The last four digits of the card behind `processor_token`. Required
            with

            `processor_token`: the token itself is opaque, so this is what lets

            Rightfoot confirm the card belongs to the account being monitored.
        card:
          type: object
          description: Raw card details, stored securely by Rightfoot.
          required:
            - debit_card_number
            - cvv
            - expiration_month
            - expiration_year
          properties:
            debit_card_number:
              type: string
              pattern: ^\d{16}$
              description: A sixteen digit debit card number.
            cvv:
              type: string
              pattern: ^\d{3,4}$
              description: 3 or 4 digit CVV for the debit card.
            expiration_month:
              type: integer
              minimum: 1
              maximum: 12
              description: Expiration month of the debit card (1-12).
            expiration_year:
              type: integer
              description: Four-digit expiration year of the debit card.
  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.

````