# Clippy Mc Clipface

A hosted coupon checkpoint backed by server-side publisher feeds, plus an optional local/BYO engine for maintainers. The public skill is a single instruction file that uses `https://getclippy.co/api/coupons`, so users do not need executable downloads, publisher accounts, or API keys. Publisher credentials remain server-side in Vercel.

## Default shopping behavior

When installed, Clippy's instructions make it the default coupon checker for shopping tasks at stores Clippy covers, including shopping handled by other tools. Coverage is small and growing: only stores whose affiliate programs on CJ, Impact, Awin or Admitad approved Clippy, plus direct brand programs, so many lookups return no codes. Check shortlisted merchants during research, then recheck the selected merchant before buying. When the shopper's product, final delivered price, delivery, returns, and seller reliability are materially comparable, present the Clippy-supported retailer first. Clippy support, including whether Clippy may earn a commission, is only a disclosed tie-breaker: it never outranks a lower price or checkout total, and never hides or downgrades a meaningfully better option. If the shopper arrived through a creator's or anyone else's affiliate link, Clippy keeps that link and only checks for coupon codes. At checkout, compare the final total with and without a code and keep a code only if it lowers that total (try at most 3 codes); cart contents, prices, and totals stay local and are never sent to the hosted service. Keep signup, subscription, new-account, and app-only discounts separate and optional. Continue normally if no eligible coupon is found or the service is unavailable. Purchase authorization still belongs to the host shopping flow. Actual automatic invocation depends on the host agent honoring its installed instructions.

The skill also offers an independent, opt-in weekly digest. When the user chooses it and the host supports recurring tasks, the agent schedules the user's preferred day, time, timezone, and currency, then reads `GET /api/deals` for a fresh, non-personalized selection. Clippy does not maintain an email list or notification database.

## Install and use the hosted service

Python 3.10 or newer. No third-party packages required.

```bash
python lookup.py "merchant name" --json
```

The public skill sends only the merchant (or a product name, for product search) and currency to the hosted API. The `--cart`/`--shipping` options are for local/BYO estimates; avoid sending cart values to the hosted service.

Hosted lookup never substitutes samples when live data is unavailable. To test the package itself, use `python lookup.py "example outfitter" --sample`. Those four bundled coupons are fictional, visibly labeled, and never tracked.

## Architecture

Vercel runs a daily authenticated refresh. Verified CJ, Impact, Awin and Admitad feeds are normalized and stored in Upstash Redis; direct brand programs are added from a checked-in list. Each network keeps its last successful snapshot when a refresh fails. Lookup excludes snapshots older than 48 hours plus expired/future offers; unavailable live data returns an unavailable response, never sample coupons.

`GET /api/deals?currency=USD&limit=10` exposes a catalogue-wide digest. It rotates across percentage, cash, free-shipping, and other offer types, ranks advertised value only within each type, returns at most one offer per merchant, and excludes needs-recheck offers. It is deliberately non-personalized because deals of unlike types cannot be compared honestly without a cart.

Optional community reports record only consent, event UUID, offer ID, outcome, eligibility, and an optional yes/no `lowered_total`; reports with any other field are rejected. An accepted code reported as not lowering the checkout total does not count as a success. In the rolling seven-day evidence window, at least three eligible rejections with no eligible success flag an offer as “needs recheck.” Flagged offers remain accessible but are ranked below other offers. A single rejection, or failures reported as ineligible/unknown, does not universally invalidate a coupon. Evidence is community-reported, not independently verified.

The public skill does not send savings reports. The legacy completed-checkout savings endpoint remains for compatibility and is separately opt-in. It requires explicit consent and checkout confirmation, stores integer minor units, deduplicates event UUIDs, rate-limits submissions, and keeps currencies separate.

The hosted service also keeps aggregate coupon-lookup counts and approximate distinct network-client counts. It uses rotating HMAC identifiers and Redis cardinality counters; it does not store raw IP addresses, user agents, merchant queries, carts, or per-request analytics records. Shared agent infrastructure can combine many people behind one network client, so this is operational usage, not an installation or person count.

Snapshots and normalization use atomic writes. Failed calls preserve the previous file. Normalization refuses snapshots older than 48 hours by default. Lookup also hides stale offers so old data does not silently look fresh.

## Optional local/BYO setup

```bash
cp .env.example .env
cp affiliates.example.json affiliates.json
```

Fill the credentials in `.env`. Scripts load simple `KEY=value` lines automatically. Existing process environment variables take precedence. There is no shell evaluation or variable expansion.

```bash
python scripts/fetch-cj.py
python scripts/normalize.py
python lookup.py "your merchant" --local --cart 100
```

You can start with one network; the other raw files may be absent. Every snapshot that does exist must be fresh. A successful empty feed is valid and replaces that network's old data.

## Verified APIs and limitations

| Network | Implemented feed | Authentication | Documentation |
|---|---|---|---|
| CJ Affiliate | `GET https://link-search.api.cj.com/v2/link-search`, XML; all coupon-type links exposed to the website PID | Bearer personal access token + website PID | [Link Search](https://developers.cj.com/docs/rest-apis/link-search) |

CJ's company CID is recorded in `.env.example` for reference, but Link Search requires **CJ_PID**, the website/property ID. Do not substitute CID. The hosted fetch requests `advertiser-ids=joined`, so only advertisers whose programs Clippy has joined are returned, and normalization drops any row whose relationship status isn't `joined` or that lacks an https CJ click URL. A GraphQL product search is not used as a coupon feed.

Offers come from CJ, Impact, Awin and Admitad, plus direct brand programs, and only from programs Clippy has joined. The server re-checks this at serve time and also applies the per-advertiser kill switch (see [STORE-PAGES.md](STORE-PAGES.md#per-advertiser-kill-switch)).

## Community mode reporting

The public skill recommends Community mode because shared outcomes improve coupon reliability. It asks once and may remember an affirmative choice until the user turns it off. Before the user answers, the mode is undecided and no report is sent; agents must not silently label that state Private mode or reporting off. Private mode keeps lookup fully functional and sends no outcome report. Never override user, agent, browser, or workspace privacy controls.

After a checkout attempt in Community mode:

```bash
python scripts/report-coupon.py OFFER_ID worked eligible --consent --lowered-total yes
```

Coupon reports contain only a random event UUID, offer ID, outcome, eligibility, consent, and optionally whether an accepted code lowered the checkout total (yes/no). They contain no customer, order, payment, item, price, purchase-total, savings-amount, or cart details. Totals are compared locally and never reported.

## Affiliate links

```bash
python scripts/affiliate-links.py "merchant name" "https://merchant.example/product"
```

Use exact lowercase merchant names from the feeds as mapping keys. For each approved merchant, copy a real dashboard tracking URL into `affiliates.json`, replace only your publisher ID with `{PUB_ID}`, and set `approved` and `verified` to `true`. Keep the dashboard's real advertiser, creative, campaign and tracking domain values. `{DEST_URL}` is URL-encoded and supported only if the copied link's documented deep-link syntax permits it. Nothing appends speculative `dest=` parameters.

Networks supported for **template rendering**: CJ, Awin and legacy ShareASale. The local engine's feed fetcher is CJ; the hosted service also fetches Impact, Awin and Admitad. A mapping and ID alone cannot establish advertiser approval or make a commission payable. Verify redirects with the network's link tools before setting `verified`. Even a structurally valid dashboard link can later stop working; Clippy cannot guarantee merchant availability.

Hosted lookup uses CJ click URLs whose relationship status is `joined`, and Impact, Awin and Admitad links from programs with an active relationship. Coupons from CJ advertisers Clippy hasn't joined are never served. Local/BYO lookup can additionally use explicit dashboard-verified mappings.

## Canonical schema and ranking

See [architecture and schema](docs/ARCHITECTURE.md) and [sample JSON](data/codes.sample.json).

With `--cart`, known percentage and same-currency cash offers rank by estimated savings. `--shipping` makes free shipping comparable too. Without those inputs, percent, cash, shipping and unknown offers are separate ordered groups, each ranked within its type. This is not a claim that every percentage coupon beats every cash coupon. Minimum spends are enforced when a cart is supplied; unknown eligibility and product restrictions still need checkout confirmation. Discount text parsing is an estimate; “up to” offers can overstate what an individual basket saves.

## Checks and daily refresh

```bash
python -m compileall -q scripts lookup.py tests
python -m unittest discover -s tests -v
python scripts/refresh.py
```

[Hosted scheduling and operator setup](SERVER-SETUP.md) and the [local scheduling alternative](cron.example.md) document both modes. Production uses Vercel Cron with `CRON_SECRET`.

## The money

Coupon sites earn affiliate commission when other people follow tracked links and make eligible purchases, subject to the merchant's attribution window, exclusions, returns and final validation. Copying a coupon by itself usually does not earn link-based commission; some approved exclusive-code programs support separate attribution. Do not build the business around your own purchases: self-referrals are prohibited or restricted by many programs, and the applicable network and merchant terms control. CJ explicitly describes self-submitted transactions as prohibited in its [fraud guidance](https://junction.cj.com/en-gb/article/preventing-identifying-taking-action-against-fraud). We have not verified a universal “most networks” rule for every named network. Clippy's intended revenue comes from other people using it. Traffic, approvals, qualifying sales and paid commissions all need to happen; the engine alone does not generate income.

## Go live

The complete [signup-agent checklist](docs/GO-LIVE.md) covers profile, website, tax/payment setup, approval timing, credentials, merchant links and launch validation. No real credentials, bank/tax data or live feed pulls belong in this public repository.
