# Affiliate API overview

> Three calls: search with a lead, poll for results, submit the result the consumer chose.

## The three legs

1. **Search.** `POST /api/v1/engine/search` with the lead, `icon_campaign_id` and `icon_affiliate_id`. ICON searches every active offer on that campaign (its own offers and the partner offers it calls), runs its duplicate, cap, eligibility and requirement checks, and answers with `icon_lead_id` (your search id) and the first results.
2. **Results.** `GET /api/v1/engine/results/{icon_lead_id}` until `processing_done` is `true`. More results arrive while partner offers answer; wait `poll_after_ms` (about a second) between polls. A search stays open for up to 45 seconds.
3. **Submit.** `POST /api/v1/engine/leads` with the `icon_result_id` the consumer chose, `search_lead_id`, the program, the answers to the result's `form_fields` and the consumer's consent. No need to send the lead again: ICON builds the submit on the lead of the search.

| Leg | Endpoint | Page |
| --- | --- | --- |
| 1. Search | `POST /api/v1/engine/search` | [Search](https://developers.iconroute.io/api/search/) |
| 2. Results | `GET /api/v1/engine/results/{icon_lead_id}` | [Results](https://developers.iconroute.io/api/results/) |
| 3. Submit | `POST /api/v1/engine/leads` | [Submit a lead](https://developers.iconroute.io/api/leads/) |
| Optional pre-check | `POST /api/v1/engine/dupe-check` | [Duplicate pre-check](https://developers.iconroute.io/api/dupe-check/) |

```text
your server                                 ICON
    │  POST /engine/search (lead, campaign) ──▶  searches every active offer on the campaign
    │  ◀── icon_lead_id + first results ──────   duplicate, cap, eligibility, consent checks
    │  GET /engine/results/{icon_lead_id} ────▶  (repeat until processing_done)
    │  ◀── all results so far ────────────────
    │  POST /engine/leads (icon_result_id) ───▶  full checks again, then delivery
    │  ◀── status, reason, code, payout ──────
```

## What every result carries

The same core fields, whether the result is an ICON-hosted offer or a partner offer ICON calls: the result id to submit, the offer and its school (name and logo), the consent statement to show, the programs, the questions to ask (`form_fields`), your payout and an expiry. The full schema is on [Results](https://developers.iconroute.io/api/results/#the-result-entry).

## Your payout

`payout` is **your own** payout for the result, per program where programs differ (`programs[].payout`). ICON never returns the offer's own price or anyone else's terms.

- No payout is configured for your campaign: the `payout` key is absent.
- A refused submit answers `payout: 0`.
- An accepted submit answers the payout booked for that lead.

## Exclusivity

| `offer.offer_type` | What an accepted submit means |
| --- | --- |
| `shared` | The lead can be sold to up to `max_accepted_submissions` offers (3 today). Submit to several shared results if the consumer chose several. |
| `exclusive` | Sold as an exclusive offer for its school. Unlike true exclusive, it does not end the lead's other sales. |
| `true_exclusive` | An accepted submit ends the lead's other sales: later submits for the same search are refused. |

Every search and results answer also carries `accepted_count`, `max_accepted_submissions` and `true_exclusive_accepted` so you can tell what can still be sold.

## Reposts and corrections

- Sending a lead to an offer that already accepted it answers `DUPLICATE` (`Duplicate - Offer`). This is recognised when the repost carries the same `search_lead_id` (or, for a direct post, the `icon_lead_id` ICON returned).
- Once a lead has been submitted, a request with your key that would change its data is refused (`403`, `FORBIDDEN`) and nothing is saved.
- To correct a submitted lead, contact your ICON contact with its `icon_lead_id`.

## Conventions

- JSON in, JSON out, UTF-8. Send `Content-Type: application/json`.
- Ids are UUIDs unless stated. Times are ISO 8601 in UTC.
- **Additive changes:** ICON adds fields to answers without notice. Ignore keys you do not use. Removals and renames are announced in the [changelog](https://developers.iconroute.io/changelog/).
- Every answer carries `status`, `reason` and `code` ([Standard responses](https://developers.iconroute.io/responses/)).
