Scraping Run Feedback
Submit client-side feedback on a scraping run’s output. Feedback drives the agent self-improvement loop and per-biller observability.
Related guide: Submitting Scraping Feedback · Error catalog: Feedback errors
(client_id, biller_id) tuple. Excess returns 429 FEEDBACK_RATE_LIMITED.The Feedback object
Response attributes
| Field | Type | Description |
|---|---|---|
| agent_type* | string | Echoes `recording_agent` (the single agent type this endpoint covers today). |
| run_id* | string | Echoes the path parameter. |
| client_id* | string | Your client_id (echoed for correlation). |
| biller_id* | string | Echoes the biller_id from the request body. |
| signal* | string | Echoes the submitted signal. |
| by_user_id | string | Echoes the submitted by_user_id (if provided). |
| notes | string | Echoes the submitted notes (if provided). |
| submitted_at* | string (ISO 8601) | The server timestamp of the original submission. On idempotent replay this is the ORIGINAL timestamp, not the replay timestamp. |
| idempotency_key* | string | Echoes the Idempotency-Key header from the request. |
| already_existed* | boolean | True when this exact submission was already recorded (idempotent replay). False when this submission caused a new row to be written. |
Example Feedback response
{
"agent_type": "recording_agent",
"run_id": "run_abc123",
"client_id": "7ae2cae3-4587-4750-941b-13a6696289a8",
"biller_id": "biller_verizon",
"signal": "data_incorrect",
"by_user_id": "user_42",
"notes": "Bill shows $0 but my paper bill says $84.32. Acct ending 1234.",
"submitted_at": "2026-05-30T18:00:00Z",
"idempotency_key": "idem_2026-05-30T18:00:00Z_run_abc123",
"already_existed": false
}/v1/scraping-runs/:run_id/feedbackSubmit feedback on a scraping run. The :run_id path parameter matches the run identifier returned by the scraping flow. Idempotent: send the same Idempotency-Key within 24 hours to retry safely.
Request body (application/json)
| Field | Type | Description |
|---|---|---|
| signal* | string (enum) | One of: `succeeded`, `login_failed`, `bill_missing`, `data_incorrect`. The subset of the implicit signal vocabulary that clients can meaningfully observe (system-only signals like `timeout`, `two_factor_required`, `extraction_empty` are not in this enum). |
| biller_id* | string | Biller identifier the scraping run targeted. Drives per-biller feedback aggregation + per-(client × biller) rate limit keying. Max 128 chars. |
| by_user_id | string | The end-user identifier within your domain who saw the result. Optional — clients without a user concept can omit it. Useful for audit + per-user dedup downstream. Max 256 chars. |
| notes | string | Free-text notes. Never echoed back in metrics; PII-safe storage required by client. Max 4096 chars. |
Returns: The feedback object on 201 Created with already_existed: false. On idempotent replay, returns the original payload (same submitted_at) with already_existed: true.
curl -X POST "https://sandbox.api.billerapi.com/v1/scraping-runs/run_abc123/feedback" \
-H "X-Client-ID: your_client_id" \
-H "X-Client-Secret: bb_test_xxxxxxxx" \
-H "Idempotency-Key: idem_$(date +%s)_run_abc123" \
-H "Content-Type: application/json" \
-d '{
"signal": "data_incorrect",
"biller_id": "biller_verizon",
"by_user_id": "user_42",
"notes": "Bill shows $0 but my paper bill says $84.32."
}'Idempotency semantics
This endpoint follows the standard BillerAPI idempotency contract. The Idempotency-Key header is required (not just recommended) so retries don’t double-count.
- Replay — same
Idempotency-Key+ identical body within 24h: returns the original payload withalready_existed: trueand the originalsubmitted_at. Safe to retry on network failure. - Already submitted (different key, same run) — a different Idempotency-Key submitting for the same
(client_id, run_id)returns409 FEEDBACK_ALREADY_SUBMITTED. Each scraping run accepts exactly one feedback submission.
Rate-limit headers
Every successful response includes standard rate-limit headers. The limit is per (client_id, biller_id) tuple — separate billers consume separate quotas.