Feedback
Tell BillerAPI when a resource it produced is right or wrong. You report on the resource itself — a bill or an account connection — not on the internal run that produced it. The gateway attributes the feedback to the producing agent run and folds it into the self-improvement loop, so extraction quality improves for that biller over time.
Related guide: Submitting Feedback · Error catalog: Feedback errors
202 withattributable: false — you never get a hard error just because the run can’t be located. A404 means the resource doesn’t exist or isn’t yours.The Feedback object
Response attributes
| Field | Type | Description |
|---|---|---|
| resource_type* | string | Echoes the submitted resource_type. |
| resource_id* | string | Echoes the submitted resource_id. |
| agent_type* | string | The agent whose output the feedback was attributed to (e.g. `recording_agent`, `script_execution_agent`). |
| signal* | string | Echoes the submitted signal. |
| attributable* | boolean | True when the feedback was attributed to a known agent run (a producing run was found). False when the producing run is unknown or has aged out — the feedback is still recorded as observed and returned with 202 (soft-accept). |
| run_id | string | The agent run the feedback was attributed to. Omitted when `attributable` is false. |
| already_existed* | boolean | True when this exact submission was already recorded (idempotent replay). False when this submission wrote a new record. |
Example Feedback response
{
"resource_type": "bill",
"resource_id": "bill_abc123",
"agent_type": "recording_agent",
"signal": "wrong_amount",
"attributable": true,
"run_id": "run_abc123",
"already_existed": false
}/v1/feedbackSubmit feedback about a resource. Authenticate with yourAPI key /API key. Idempotent: send the same idempotency_key with an identical body to retry safely.
Request body (application/json)
| Field | Type | Description |
|---|---|---|
| resource_type* | string (enum) | The kind of resource the feedback is about: `bill` or `account_connection`. (`payment` is reserved — see below.) |
| resource_id* | string | The id of the resource — a bill id or an account-connection (account link) id. Ownership is enforced tenant-scoped against your client. Max 256 chars. |
| signal* | string (enum) | What you observed. Valid values depend on `resource_type` — see the Signal vocabulary section. Max 64 chars. |
| idempotency_key* | string | Required so retries do not double-count. Recommended format: ULID or `idem_<timestamp>_<resource_id>`. The `Idempotency-Key` header is an optional override; this body field is authoritative. Max 256 chars. |
| notes | string | Free-text notes. PII-safe storage required; never echoed back in metrics. Max 4096 chars. |
Returns: The feedback object on 202 Accepted.attributable is false when the producing run could not be located (still recorded). On idempotent replay, already_existed is true.
curl -X POST "https://sandbox.api.billerapi.com/v1/feedback" \
-H "Authorization: Bearer $BILLERAPI_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"resource_type": "bill",
"resource_id": "bill_abc123",
"signal": "wrong_amount",
"idempotency_key": "idem_2026-06-25T18:00:00Z_bill_abc123",
"notes": "Bill shows $0 but my paper bill says $84.32."
}'Signal vocabulary
The valid signal values depend on resource_type. Each type has one positive signal (“this is correct”) plus the ways it can be wrong. A signal that isn’t valid for the resource returns 400 FEEDBACK_INVALID_SIGNAL.
resource_type: "bill"
Bill signals
| Field | Type | Description |
|---|---|---|
| bill_correct | positive | The bill is accurate — confirms the extraction was right. |
| wrong_amount | incorrect | The amount due is wrong. |
| wrong_due_date | incorrect | The due date is wrong. |
| wrong_status | incorrect | The paid/unpaid status is wrong (e.g. shows due but already paid). |
| duplicate_bill | incorrect | This bill is a duplicate of another. |
| not_my_bill | incorrect | This bill does not belong to the account. |
| missing_line_items | incorrect | Charges / line items are missing. |
| stale_data | incorrect | The data is out of date. |
resource_type: "account_connection"
Account-connection signals
| Field | Type | Description |
|---|---|---|
| accounts_correct | positive | The discovered accounts are correct. |
| wrong_account | incorrect | A discovered account is wrong. |
| missing_account | incorrect | An account that should have been discovered is missing. |
| wrong_biller_matched | incorrect | The connection was matched to the wrong biller. |
| duplicate_account | incorrect | A discovered account is a duplicate. |
resource_type: "payment" is reserved. Submitting it returns 501 PAYMENT_EXECUTION_NOT_AVAILABLE.
Status codes
Responses
| Field | Type | Description |
|---|---|---|
| 202 Accepted | success | Feedback recorded. `attributable` is true when a producing run was found, false on soft-accept (run unknown / aged out). |
| 400 FEEDBACK_INVALID_SIGNAL | error | The signal is not valid for the resource_type, or `idempotency_key` is missing. |
| 401 | error | Missing or invalid client credentials. |
| 404 FEEDBACK_RUN_NOT_FOUND | error | The resource does not exist or is not owned by your client (collapsed — no cross-tenant existence oracle). |
| 409 FEEDBACK_ALREADY_SUBMITTED | error | Feedback for this resource was already submitted under a different idempotency_key. Reuse the original key for an idempotent replay. |
| 501 PAYMENT_EXECUTION_NOT_AVAILABLE | error | Returned for `resource_type: "payment"` — payment feedback is reserved and not yet available. |
Idempotency semantics
This endpoint follows the standard BillerAPI idempotency contract. The idempotency_key is required (not just recommended) so retries don’t double-count.
- Replay — same
idempotency_key+ identical body: returns the original payload withalready_existed: true. Safe to retry on network failure. - Already submitted (different key, same resource) — a different key for the same resource returns
409 FEEDBACK_ALREADY_SUBMITTED. Reuse the original key to replay.