---
title: "Account Tasks & Interactions"
description: "Respond to Billabex agent account tasks: search, list, get, cancel, set aside, and add user interactions."
canonical: https://developer.billabex.com/en/guides/account-tasks/
lang: en
alternate: https://developer.billabex.com/fr/guides/taches-de-compte/
last-updated: 2026-09-22
---

# Account Tasks & Interactions

> Respond to Billabex agent account tasks: search, list, get, cancel, set aside, and add user interactions.

Source: https://developer.billabex.com/en/guides/account-tasks/
Language: English (en)
French version: https://developer.billabex.com/fr/guides/taches-de-compte/

Account tasks in Billabex represent **action items** that require human input or approval. They are **created exclusively by the Billabex AI agent** when it needs user guidance, contact information, or explicit approval to proceed with dunning activities on a specific account.

This guide covers how to interact with account tasks via the API: listing, retrieving, canceling, setting aside, and responding with interactions.

## Overview

Account tasks follow a simple lifecycle:

1. **Created** - The Billabex agent creates an account task when user input is required
2. **Open** - The task awaits user input
3. **Interaction** - A user responds via an interaction message
4. **Closed/Canceled** - The task is resolved by the agent or canceled by the user

Each account task has a **type** that determines what kind of response is expected.

> **Note**: Account tasks can only be created by the Billabex agent. The public API allows you to read tasks, respond to them, set them aside and cancel them - but not create new ones.

## Account Task Types

| Type                 | Description                                                          | Expected Response          |
| -------------------- | -------------------------------------------------------------------- | -------------------------- |
| `ApproveEligibility` | Approve or reject a contact for dunning                              | Structured: `boolean`      |
| `NeedContacts`       | Provide contact details for an account                               | Structured: contact object |
| `AskNextAction`      | Choose the next action                                               | Structured choice          |
| `NeedUserInput`      | Agent needs clarification or information                             | Text message               |
| `Unknown`            | Fallback task type                                                   | Structured: `boolean`      |
| `Notice`             | The agent reports something it already did; nothing is expected back | None                       |

### `extraData.idempotencyKey`

Task types that carry no structured payload of their own (`Notice`, `AskNextAction`,
`NeedContacts`, `ApproveFirstOutreach`, `Unknown`) expose `extraData.idempotencyKey`. It is a stable
key the agent sets when several tasks of the same type may legitimately coexist on one account, and
it is `null` otherwise. It does not change when the task is replayed, so use it to reconcile a task
you have already seen. A `Notice` about a customer complaint, for instance, is keyed
`contact-grievance:<incoming email id>`.

## Required Scopes

| Scope        | Access Level | Endpoints                                                                                                                                                              |
| ------------ | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `tasks:read` | Read-only    | `GET /account-tasks`, `GET /account-tasks/search`, `GET /account-tasks/:accountTaskId`                                                                                 |
| `tasks:all`  | Full access  | All read + `POST /account-tasks/:accountTaskId/cancel`, `POST /account-tasks/:accountTaskId/close`, `POST /account-tasks/:accountTaskId/set-pending`, add interactions |

## List Active Account Tasks

Retrieve active account tasks for a specific account.

```http
GET https://next.billabex.com/api/public/v1/account-tasks?accountId=ACCOUNT_ID
Authorization: Bearer YOUR_ACCESS_TOKEN
```

Query parameters:

| Parameter   | Type   | Required | Description                            |
| ----------- | ------ | -------- | -------------------------------------- |
| `accountId` | UUID   | Yes      | Filter account tasks by account        |
| `type`      | string | No       | Filter by account task type (optional) |

Example response:

```json
[
  {
    "task": {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "organizationId": "org-uuid",
      "accountId": "account-uuid",
      "type": "NeedUserInput",
      "title": "Clarification needed on invoice #1234",
      "status": "UserActionRequired",
      "isActive": true,
      "isClosed": false,
      "isCanceled": false,
      "thread": [
        {
          "id": "interaction-uuid",
          "message": {
            "type": "Text",
            "value": "The customer mentioned a dispute. How should I proceed?"
          },
          "attachments": [],
          "sentAt": "2024-05-01T10:15:30.000Z",
          "agent": {
            "email": "agent@example.com",
            "firstName": "Alex",
            "lastName": "Martin"
          }
        }
      ],
      "assignees": [],
      "createdAt": "2024-05-01T10:15:30.000Z",
      "closedAt": null,
      "canceledAt": null,
      "cancelReason": null,
      "canceledBy": null,
      "completedAt": null,
      "isPending": false,
      "pendingAt": null,
      "pendingUntil": null,
      "extraData": { "refEmailMessageIds": [] }
    }
  }
]
```

## Search Account Tasks

`GET /account-tasks` answers a single account and returns its active tasks in
one shot. To work across the whole organization, with filters, sorting and
pagination, use the search endpoint.

```http
GET https://next.billabex.com/api/public/v1/account-tasks/search?organizationId=ORG_ID&first=20
Authorization: Bearer YOUR_ACCESS_TOKEN
```

Query parameters:

| Parameter        | Type    | Required | Description                                                                              |
| ---------------- | ------- | -------- | ---------------------------------------------------------------------------------------- |
| `organizationId` | UUID    | Yes      | Organization to search in                                                                |
| `accountId`      | UUID    | No       | Restrict to a single account                                                             |
| `query`          | string  | No       | Free text search on the task title and the account name                                  |
| `filters`        | string  | No       | Filter conditions, see [Pagination & Filtering](/en/guides/pagination/#filtering-a-list) |
| `sortBy`         | string  | No       | `createdAt`, `updatedAt`, `title`, `accountFullName`, `status` or `balance`              |
| `sortOrder`      | string  | No       | `asc` or `desc`                                                                          |
| `first`          | integer | No       | Between `1` and `100`                                                                    |
| `after`          | string  | No       | Cursor from `pageInfo.endCursor`                                                         |

Filterable fields:

| Field         | Operators                                                               |
| ------------- | ----------------------------------------------------------------------- |
| `title`       | `contains`, `startsWith`, `equals`                                      |
| `accountName` | `contains`, `startsWith`, `equals`                                      |
| `status`      | `is`, `isNot`, `isAnyOf`                                                |
| `type`        | `is`, `isNot`, `isAnyOf`                                                |
| `tags`        | `containsAny`, `containsAll`, `containsNone`, `isEmpty`, `isNotEmpty`   |
| `balance`     | `equals`, `greaterThan`, `lessThan`, `between`, `isEmpty`, `isNotEmpty` |
| `createdAt`   | `on`, `before`, `after`, `between`                                      |
| `updatedAt`   | `on`, `before`, `after`, `between`, `isEmpty`, `isNotEmpty`             |

`type` accepts `ApproveEligibility`, `ApproveContactChange`,
`ApprovePaymentArrangement`, `ApproveFirstOutreach`, `NeedContacts`,
`AskNextAction`, `NeedUserInput`, `PaymentScheduleInvalidated`, `Notice` and
`Unknown`. `status` accepts the values of the table further down this page.

Open tasks of a given type, most recent first:

```http
GET https://next.billabex.com/api/public/v1/account-tasks/search?organizationId=ORG_ID&filters=status%3Ais%3AUserActionRequired%3Btype%3Ais%3ANeedContacts&sortBy=createdAt&sortOrder=desc
```

Tasks created since the start of the quarter, on accounts owing more than 1000:

```http
GET https://next.billabex.com/api/public/v1/account-tasks/search?organizationId=ORG_ID&filters=createdAt%3Aafter%3A2026-06-30%3Bbalance%3AgreaterThan%3A1000
```

Example response:

```json
{
  "nodes": [
    {
      "id": "123e4567-e89b-12d3-a456-426614174000",
      "organizationId": "org-uuid",
      "accountId": "account-uuid",
      "accountFullName": "Acme Corporation",
      "accountTagIds": [],
      "title": "Clarification needed on invoice #1234",
      "type": "NeedUserInput",
      "status": "UserActionRequired",
      "assignees": [],
      "assigneesFullName": [],
      "isActive": true,
      "isClosed": false,
      "isCanceled": false,
      "isPending": false,
      "balance": 1250.5,
      "currency": "EUR",
      "createdAt": "2026-05-01T10:15:30.000Z",
      "updatedAt": "2026-05-02T08:00:00.000Z",
      "closedAt": null,
      "canceledAt": null,
      "cancelReason": null,
      "canceledBy": null,
      "pendingAt": null,
      "pendingUntil": null,
      "completedAt": null,
      "lastAgentInteractionAt": "2026-05-01T10:15:30.000Z"
    }
  ],
  "pageInfo": {
    "endCursor": "v2:8f14e45fceea:2"
  }
}
```

Two things to keep in mind:

- Items do not carry the interaction `thread`. Call
  `GET /account-tasks/:accountTaskId` for the thread of a task.
- The `endCursor` belongs to the filters and the sort it was issued for.
  Changing either one invalidates it, and the call returns `400` with the code
  `LIST_CURSOR_INVALID`.

## Get an Account Task

Retrieve a single account task by ID.

```http
GET https://next.billabex.com/api/public/v1/account-tasks/:accountTaskId
Authorization: Bearer YOUR_ACCESS_TOKEN
```

Returns the same structure as above, wrapped in `{ "task": { ... } }`.

## Cancel an Account Task

Cancel an active account task. This marks the task as canceled without resolving it.

```http
POST https://next.billabex.com/api/public/v1/account-tasks/:accountTaskId/cancel
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "reason": "Invoice re-sent by hand, the contact confirmed reception."
}
```

The body is optional, and so is `reason` (2000 characters at most). Sending one
is strongly recommended: cancelling is the only ending that answers nothing, and
several task types hold the automatic follow-ups back while they are active, so
cancelling one hands the cadence back to the agent. The reason is shown next to
the cancellation and is read back by the agent before it reopens the same
subject on that account.

Returns the updated account task with `isCanceled: true`, `canceledAt` set,
`cancelReason` carrying the reason when one was given, and `canceledBy` naming
who cancelled and through which door:

```json
{
  "canceledBy": {
    "userId": "usr_01H8...",
    "userFullName": "Camille Roy",
    "channel": "PublicApi"
  }
}
```

A payment check cannot be cancelled: a task whose `extraData.paymentDeclarationId` is set answers
`400` with the code `app-tasking.account-task.payment-review-requires-decision` and
`details.paymentDeclarationId`. Record the decision on the declaration (see
[Announced payments](#announced-payments)): it closes the check.

`canceledBy` is `null` when the agent cancelled the task, and on cancellations
recorded before 2026-09-03. `channel` is `Webapp`, `PublicApi` or `Mcp`, and is
`null` on a cancellation recorded before the door was tracked. `userFullName` is
`null` when the user could not be named, typically after they left the
organization.

## End an Account Task

Close a task whose subject was dealt with elsewhere, without asking the agent anything: no model
decides the closure and no email is written for it.

```http
POST https://next.billabex.com/api/public/v1/account-tasks/:accountTaskId/close
Authorization: Bearer YOUR_ACCESS_TOKEN
```

The body is empty; any field sent is refused with `400`. The author is the token user. Only a
`NeedUserInput` task asking for information can be ended this way, and not while one of your
replies is being processed (`InProgress`):

| Refusal (`400`)                                             | Case                                                                            |
| ----------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `app-tasking.account-task.payment-review-requires-decision` | payment check: record the decision on the declaration                           |
| `app-tasking.account-task.closure-not-allowed`              | another task type, an announced correction or a dispute: answer with its action |
| `app-tasking.account-task.reply-in-progress`                | a reply is being processed: retry once the agent answered                       |
| `app-tasking.account-task.canceled`                         | the task is cancelled and stays so                                              |

Missing information is not marked as provided and pauses stay in place. The closure may lift the
hold on follow-ups this task represented: read the account dunning status to know. Returns the task
with `isClosed: true`, `closedAt` and `closedBy` (same shape as `canceledBy`). Sending the same
closure again returns the closed task with its first author. `closedBy` is `null` on a closure the
agent decided.

## Set an Account Task Aside

Defer a task instead of answering it now, optionally naming the date it should
come back.

```http
POST https://next.billabex.com/api/public/v1/account-tasks/:accountTaskId/set-pending
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json

{
  "pendingUntil": "2026-09-20T08:00:00.000Z"
}
```

The body is optional, and so is `pendingUntil`, which must be an ISO 8601
instant in the future. Pass it whenever you know when the answer will be
available: on that date Billabex posts an agent message on the task explaining
that it is back, and the task returns to `UserActionRequired` on its own.

Omit it and the task still comes back, after 30 days. **A task never stays set
aside forever**, which matters because several task types hold the automatic
follow-ups of their account back while they are active: deferring one postpones
the decision, it does not hand the cadence back. Cancel or answer the task for
that.

Returns the updated account task with `isPending: true`, `pendingAt` set, and
`pendingUntil` carrying the date when one was given.

## Add an Interaction

Respond to an account task by adding a user interaction. This is how users provide the requested input to the Billabex agent.

```http
POST https://next.billabex.com/api/public/v1/account-tasks/:accountTaskId/interactions
Authorization: Bearer YOUR_ACCESS_TOKEN
Content-Type: application/json
```

### Text Interaction

For `NeedUserInput` account tasks:

```json
{
  "interaction": {
    "message": {
      "type": "Text",
      "value": "Please offer a 3-month payment plan with 10% interest."
    },
    "attachments": []
  }
}
```

### Structured Interaction

For `ApproveEligibility`, `NeedContacts`, and `Unknown` account tasks, use structured messages.

#### AskNextAction

`AskNextAction` requires a structured response: `take-over`, `resume` or `add-contact`. The last choice requires `contact.email` and `contact.language`; `contact.fullName` is optional. Clients previously sending free text now receive an immediate HTTP 400 error, without recording an interaction or changing task status. No new schema or migration is required.

```json
{
  "interaction": {
    "message": {
      "type": "Structured",
      "schema": "AskNextAction",
      "schemaVersion": 1,
      "value": { "choice": "resume" }
    }
  }
}
```

#### ApproveEligibility Response

```json
{
  "interaction": {
    "message": {
      "type": "Structured",
      "schema": "ApproveEligibility",
      "schemaVersion": 1,
      "value": true
    }
  }
}
```

The `value` is a boolean: `true` to approve, `false` to reject.

#### NeedContacts Response

```json
{
  "interaction": {
    "message": {
      "type": "Structured",
      "schema": "NeedContacts",
      "schemaVersion": 1,
      "value": {
        "fullName": "Jane Doe",
        "email": "jane@company.com",
        "language": "en"
      }
    }
  }
}
```

Required fields in `value`:

- `fullName` (string)
- `email` (string)
- `language` (string, e.g., "en", "fr")

#### Unknown Response

```json
{
  "interaction": {
    "message": {
      "type": "Structured",
      "schema": "Unknown",
      "schemaVersion": 1,
      "value": true
    }
  }
}
```

## Message Schema Reference

| Account Task Type    | Schema Name                                    | Version | Value Type                               |
| -------------------- | ---------------------------------------------- | ------- | ---------------------------------------- |
| `ApproveEligibility` | `ApproveEligibility`                           | 1       | `boolean`                                |
| `NeedContacts`       | `NeedContacts`                                 | 1       | `{ fullName, email, language }`          |
| `Unknown`            | `Unknown`                                      | 1       | `boolean`                                |
| `AskNextAction`      | `AskNextAction`                                | 1       | `{ choice, contact? }`                   |
| `NeedUserInput`      | `DecideOutgoingEmailDraft` for a pending draft | 1       | `{ communicationId, decision }`, or text |

## Validation Rules

The API enforces strict validation on interaction messages:

- `NeedUserInput` accepts text, or `DecideOutgoingEmailDraft` when it carries a
  `pendingCommunicationId`
- **Structured account tasks** must receive `type: "Structured"` messages with matching schema
- Schema name and version must match exactly
- Value must conform to the expected type for that schema

Invalid messages return a `400 Bad Request` with details.

To approve an email prepared by the task, send a structured interaction with the same
`communicationId` and `decision: "send"`. Use `decision: "discard"` to abandon it. A successful POST
proves that the decision was recorded; read the communication and verify
`awaitingApproval: false` before treating the email as armed. Free text such as "yes, send it"
never approves the draft.

## Account Task Status Values

| Status               | Description                                                                                                                                                            |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Open`               | Task created, awaiting processing                                                                                                                                      |
| `InProgress`         | Agent is processing the last reply. If that processing fails, the task returns to `UserActionRequired` within 3 hours with an agent message, and accepts a reply again |
| `UserActionRequired` | Waiting for user input                                                                                                                                                 |
| `Closed`             | Task completed successfully                                                                                                                                            |
| `Canceled`           | Task was canceled                                                                                                                                                      |
| `Pending`            | Task set aside, coming back on `pendingUntil` or after 30 days                                                                                                         |

## Common Errors

### 400 Bad Request - Invalid Message

```json
{
  "statusCode": 400,
  "message": "Text message is required for this task type"
}
```

Cause: Sending a structured message to a text-only account task type.

### 404 Not Found - Account Task Not Found

```json
{
  "statusCode": 404,
  "message": "Account task 123e4567-e89b-12d3-a456-426614174000 not found"
}
```

Cause: The account task ID does not exist or belongs to another organization.

### 403 Forbidden - Insufficient Scope

```json
{
  "statusCode": 403,
  "message": "Forbidden",
  "error": "insufficient_scope"
}
```

Cause: Your token lacks `tasks:all` for write operations.

## Resolving follow-up safeguards

A `NeedUserInput` answer follows the same rules in the web app, the API and MCP. An instruction is
neither confirmed nor the task closed if its effects could not be applied. Closing, cancelling or
setting a task aside does not resolve the payment or the correction it asks you to verify.

For an announced correction, update the invoices concerned, then answer the task. If the correction
was abandoned, say so explicitly and authorize the follow-ups to resume on the original invoice. A
plain "thanks" or a generic resumption is not that decision. An announced payment requires an
explicit decision, which can now be recorded from its linked task as described below.

The review closes automatically when its linked invoices are settled. For a declaration with no
invoice references, it also closes if the account has at least one invoice and every invoice is
individually settled. The decision is `InvoicesSettled`, without implicitly confirming payment
receipt. An account with no invoices or a missing invoice reference still requires review. This
rule applies across all three surfaces.

A text reply in a linked payment-review task can carry the user's decision: receipt
(`PaymentReceived`), non-receipt found after checking (`PaymentNotReceived`), or resuming reminders
without ruling on the payment (`ResumeAuthorized`). The decision is recorded on behalf of the
replying user, using the same authorization and accounting checks as the account operation, and
needs no repeat in the account form. Processing is asynchronous: read the task and payment
declarations again for the result. If the linked invoices remain open after a receipt, the receipt
is recorded and reminders stay held until reconciliation: while the settlement grace runs, the task
is closed; once it has passed, a task asks for the invoices to be updated or for reminders to
resume. A canceled or closed review task is not recreated, except once at the end of that grace if
it was opened before it. A closure request alone, a claim relayed
from the debtor, a promise or a request to keep waiting decide nothing, and this interaction never
changes invoices. The webapp, public API and MCP share this behavior; `CreditApplied`,
`InvoicesSettled` and `Withdrawn` use the explicit account operations.

## Announced payments

An announced payment is not a recorded payment. The register stays active after its task is closed
or cancelled. Use the `dunning:manage` scope and the endpoints under
`/api/public/v1/accounts/{accountId}/payment-declarations`:

- `GET`: read the declarations and their decisions.
- `POST`: record `sourceRef`, `declaredAt`, and optionally `paymentDate`, `amount`, `currency`,
  `declaredStage`, `invoiceNumbers`, `proofDocumentAttached`. Reuse `sourceRef` when you retry.
- `POST /{declarationId}/resolve`: supply a `resolution`, and an optional `note`. Without a note, a
  factual note derived from the decision is recorded under your name.

A check task carries `extraData.paymentDeclarationId`, the identity of the declaration it checks
(opaque, not necessarily a UUID). Every declaration carries `reviewTaskStatus` (`open`, `closed`,
`canceled`, `unknown`, `null` without a task) and `resolvedByUserFullName`. A decision next to a check
still `open` is recorded: its closure is being retried, and replaying the same decision retries it.

A payment already held open is not recorded twice. When a new declaration names exactly the same
invoices as an unresolved one, comes from another `sourceRef`, and no amount, currency or payment
date disagrees (equal, or given on one side only), it is folded into the existing declaration
instead of creating a second one. The existing declaration fills in what it did not know, takes the
stage of the latest announcement, and lists the new one, as it was sent, under `restatements`. The
response then holds your `sourceRef` inside that list rather than as a declaration of its own, and
resolving with it decides the declaration it was folded into. A different amount, currency, payment
date or set of invoices, or a declaration already decided, still creates a new declaration.

`proofDocumentAttached` says the customer attached a document they present as proof of that
payment: a transfer advice, a bank receipt, a screenshot of the transaction. The field proves
nothing about the money arriving and changes neither the announced stage nor the hold on
follow-ups; it says the reconciliation starts from a reference rather than from a bare claim, and
the review task reports it. A declaration recorded before the field existed carries `false`.

The review question opens as soon as the declaration is recorded, not when its grace period
expires.

Resolutions: `PaymentReceived`, `PartiallyReceived`, `CreditApplied`, `InvoicesSettled`,
`PaymentNotReceived`, `Withdrawn`, `ResumeAuthorized`. `PartiallyReceived` confirms part of the
payment arrived: first set the total paid of each invoice concerned (`PUT
/invoices/{invoiceId}/paid-amount` with `expectedPaidAmount`, the amount you read), then record the
decision. It changes no invoice, requires at least one partly paid invoice and a remaining balance,
and follow-ups resume on the balance still owed. A confirmed receipt (`paymentConfirmedAt`) does not release the
follow-ups while invoices stay open. `ResumeAuthorized` authorizes the resumption without denying
the payment. A credit note or a settled invoice does not prove a bank receipt. All these routes
return the current list of declarations.

A person's decision is final. Replaying the same decision changes nothing and returns the list
unchanged; sending a different one answers `400` with the code
`app-dunning.payment-declaration.already-resolved`, whose `details` field carries `declarationId`,
`resolution`, `resolvedAt` and `resolvedBy`. Show that decision rather than the failure.

One exception, and only one: the decision the billing data took on its own. Settling the invoices of
a declaration decides it as `InvoicesSettled` within the second, with `resolvedBy` set to
`accounting`. That decision answers whether the invoices are cleared, never whether the money
arrived, and an explicit user decision replaces it without an error. It is the only path by which
`PaymentReceived` is ever recorded on such a declaration, and `PaymentReceived` is the only decision
that lets the next reminder thank the debtor. The replacement goes one way: the automatic review
never revisits a person's decision, and the invoice verification applies to a replacement exactly as
to a first decision. Re-read the declaration before offering a decision to a user.

## Next Steps

- **[Getting Started](/en/guides/getting-started/)** - Integration quick start
- **[OAuth Scopes](/en/guides/scopes/)** - Required permissions
- **[Pagination & Filtering](/en/guides/pagination/)** - Cursors and the `filters` parameter
- **[Rate Limiting](/en/guides/rate-limiting/)** - Avoid hitting limits
- **[API Reference](/en/api-reference/)** - Full endpoint documentation

## Support

Questions about account tasks and interactions?  
Contact us via the [website contact form](https://billabex.com).

## Confirm reminder recipients

A `NeedUserInput` task with `extraData.idempotencyKey = "dunning-recipient-review"` blocks automatic reminders when more than five contacts with valid email addresses are enabled without a confirmed selection. Read the current account contacts, then use the existing structured interaction endpoint or MCP `add-account-task-structured-interaction` tool:

```json
{
  "type": "Structured",
  "schema": "ConfirmDunningRecipients",
  "schemaVersion": 1,
  "value": {
    "reviewedContacts": [
      { "id": "contact-id", "email": "contact@example.com" }
    ],
    "selectedContactIds": ["contact-id"],
    "confirmLargeSelection": false
  }
}
```

`reviewedContacts` contains all enabled contacts with valid emails that the user reviewed, including their current addresses. `selectedContactIds` must be a nonempty subset with no duplicates. More than five selected contacts requires `confirmLargeSelection: true`. Unselected reviewed contacts are disabled, not deleted. The task closes after the selection is saved. If a contact is added or an address changes before submission, read the contacts again. Text replies and generic task closure cannot confirm this selection.

## Contact designation and reminder resumption

For a `NeedUserInput` task with `extraData.idempotencyKey = "dunning-recipient-routing"`,
a text reply such as `hello@example.com` designates the contact for future reminders.
The agent adds or reuses that contact before closing the task. Disabled contacts and rejected
addresses remain protected; an unsuccessful write keeps the task open. Providing an address
alone does not request an immediate email or authorize resuming paused reminders.

A task with key `dunning-resume-confirmation` asks whether reminders should resume. Reply with
`Resume reminders` or `Keep reminders paused` through the existing text-interaction endpoint
or MCP tool. French replies `Reprendre les relances` and `Maintenir la suspension` are also
supported. The task closes only after the corresponding account action succeeds.

---

Billabex developer portal. OpenAPI specification: https://developer.billabex.com/openapi.json.
Agent instructions: https://developer.billabex.com/llms.txt. Complete documentation: https://developer.billabex.com/llms-full.txt.
