# Changelog

> Changes to what the API accepts and answers, newest first.

ICON adds fields to answers without notice (ignore keys you do not use). Anything that removes or changes a field is listed here first.

## 2026-09-29: The consumer's IP, result-id-only submit, general refusals

- A result submit needs no lead: send `icon_result_id`, `search_lead_id`, your campaign and affiliate ids, the program, the answers and consent; ICON builds it on the lead of the search. A result that is not yours answers 404. Sending the full lead still works.
- Every cap answers `CAP_REACHED`, with no level in the code, reason or message: the per-level cap codes are no longer sent, and cap answers no longer carry `level`, `cap_level`, `cap_message` or `source`. Eligibility refusals always answer `NOT_ELIGIBLE`.
- `tracking.ip_address` is required on every search and direct post: the consumer's own public IP address. ICON records it on the lead and in the consent record, and never replaces it with the address your call comes from. A result submit uses the search's.
- Private, shared and reserved addresses are refused. Without a valid one, ICON records your server's address and marks it as such.

## 2026-09-28: Developer Center

- These docs are public, generated from the API definitions ICON runs on. Every page is also available as Markdown, with an llms.txt index and an OpenAPI description.

## 2026-09-26: Duplicate pre-check, standardized buyer reasons

- New optional endpoint `POST /api/v1/engine/dupe-check`: answers `duplicate_likely` for raw or hashed lead fields. Its own rate limit (30 per minute per key).
- A buyer's refusal is translated into a standardized reason; the buyer's own answer is never passed through.
- Reposting a lead (same search or same lead id) to an offer that already accepted it answers `DUPLICATE`.

## 2026-09-25: Affiliate API: search → results → submit

- Every answer carries `status` (one lower-case set) and a sentence-case `reason` next to `code`.
- Every result carries the same core: `icon_result_id`, `campaign_id`, offer, school, `tcpa_text`, `programs`, `form_fields`, `payout`, `expires_at`.
- `payout` is your own payout for the offer. The submit answer reports the payout booked for the lead.
- Results arrive incrementally: poll until `processing_done`, waiting `poll_after_ms`.
- Per-key rate limits with HTTP 429, `RATE_LIMITED` and `Retry-After`.
