# Submitting Feedback

When a bill or account connection looks right or wrong, surface a “is this correct?” affordance and submit the answer through [`POST /v1/feedback`](/docs/api/feedback). You report on the **resource**, not on any internal run — BillerAPI attributes the feedback to the agent run that produced it and folds it into the self-improvement loop, so extraction quality compounds per biller.

## Pick a signal

The valid `signal` depends on the `resource_type`. Each type has one positive signal plus the specific ways it can be wrong. The full list lives in the [API reference](/docs/api/feedback); the common ones:

| resource_type | signal | Use when… |
| --- | --- | --- |
| `bill` | `bill_correct` | The bill is accurate. Affirmative feedback is just as valuable — it auto-promotes winning prompt versions. |
| `bill` | `wrong_amount` | The amount due is wrong. Put specifics in `notes`. |
| `bill` | `wrong_due_date` | The due date is wrong. |
| `bill` | `not_my_bill` | The bill doesn’t belong to the account (also: `duplicate_bill`, `wrong_status`, `missing_line_items`, `stale_data`). |
| `account_connection` | `accounts_correct` | The discovered accounts are correct. |
| `account_connection` | `missing_account` | An account that should have been discovered is missing (also: `wrong_account`, `wrong_biller_matched`, `duplicate_account`). |

## 1. Hold onto the resource id

You already have it: the `id` of the bill you render, or the account-connection (account link) id. That’s the `resource_id` — no run id, no biller id to track. BillerAPI resolves the producing run on its side.

## 2. POST the feedback

On submission, POST with an `idempotency_key` so a network retry doesn’t double-count. Generate it **once at the moment the user clicks submit** and re-send it on retry. Recommended format: `idem_<timestamp>_<resource_id>`.

**Minimal client-side submission**

**Node**

```javascript
async function submitFeedback({ resourceType, resourceId, signal, notes }) {
  // Generated ONCE here. If the network errors and you retry, reuse this same key.
  const idempotencyKey = `idem_${Date.now()}_${resourceId}`;

  const res = await fetch('https://sandbox.api.billerapi.com/v1/feedback', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer $BILLERAPI_API_KEY',
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      resource_type: resourceType,   // 'bill' | 'account_connection'
      resource_id: resourceId,
      signal,                        // valid values depend on resource_type
      idempotency_key: idempotencyKey,
      notes,                         // optional, recommended when reporting a problem
    }),
  });

  if (!res.ok) {
    const error = await res.json();
    throw new Error(`feedback failed: ${error.error_code} — ${error.error_message}`);
  }
  return await res.json();  // { resource_type, resource_id, agent_type, signal, attributable, run_id?, already_existed }
}
```

## 3. Handle the response

Success is `202 Accepted`. Two fields on the response are worth reading; the full error catalog is at [Feedback errors](/docs/concepts/errors/feedback-errors).

- `attributable: false` — recorded, but BillerAPI couldn’t locate the producing run (unknown or aged out). This is normal and not an error; the feedback is still stored.
- `already_existed: true` — idempotent replay; nothing new was written. Safe.
- `400 FEEDBACK_INVALID_SIGNAL` — the signal isn’t valid for the resource_type (or the key is missing). Fix client-side validation.
- `404 FEEDBACK_RUN_NOT_FOUND` — the resource doesn’t exist or isn’t owned by your client. Don’t expose to the user; log + investigate.
- `409 FEEDBACK_ALREADY_SUBMITTED` — a different key already submitted for this resource. Reuse the original key to replay, or treat as success.

## What happens to your feedback

Attributed feedback enters the per-biller signal pipeline:

- **Positive signals** (`bill_correct`, `accounts_correct`) — count toward auto-promote thresholds for the prompt version that ran. Affirmative feedback is what lets winning versions graduate.
- **Everything-wrong signals** — count toward auto-rollback thresholds. A cluster on the same biller crosses the threshold and reverts the agent to its prior prompt version without human intervention.

**This is why high-quality feedback matters.** A noisy stream of mis-categorized signals or empty notes dilutes the pipeline. Gate on user confirmation, populate `notes` with the specifics, and the per-biller signal compounds.

## Best practices

- **Submit on success too.** Most clients only file negative feedback. That biases the signal. `bill_correct` submissions are what let us auto-promote new prompt versions safely.
- **Gate on user confirmation.** Don’t auto-submit on every output you don’t recognize. False-positive feedback poisons the signal.
- **One `idempotency_key` per submission** — not per user session. Reuse it only to retry the same submission.
- **Use `notes` for specifics.** “Amount is wrong” is noise; “$0 returned but statement shows $84.32” is actionable. Keep PII out.
- **Don’t block your UI on `attributable`.** A `false` value still means “recorded” — show the user their report landed.
