# SavItUp SSP Integration Guide (Supply Side — receive insurance leads)

Audience: insurance carriers, agencies, and lead buyers integrating with SavItUp
to RECEIVE consented quote requests ("leads"). This document is written to be
directly actionable by engineers and by LLM agents.

## Overview

- SavItUp members explicitly choose a carrier and consent to sharing the
  fields shown on the consent screen, inside the SavItUp app, before any
  lead exists. A lead carries no phone number and no telephone-contact
  consent — members call carriers themselves, from the app.
- SavItUp delivers each lead to your HTTPS endpoint as a JSON POST — API-only,
  never email.
- You report what happened to each lead back to SavItUp (conversion feedback).
- Billing is a flat fee per delivered lead from a rate card agreed in advance.
  Conversion feedback informs your next rate card — never adjusts invoices.

Onboarding is done with the SavItUp team: contact support@savitup.com. You
provide your endpoint URL, an Authorization header value, your field mapping,
and your eligibility filters. SavItUp issues your API key(s) for feedback calls
and shares your outcome-endpoint base URL.

## 1. Receiving leads (SavItUp → you)

- Transport: HTTPS POST, `content-type: application/json`, to your configured
  endpoint URL.
- Auth: SavItUp sends your configured `Authorization` header value VERBATIM on
  every delivery. Treat it as a shared secret; rotate by telling SavItUp.
- Timeout: your endpoint should respond within 30 seconds; any 2xx counts as
  accepted. Response bodies over 64KB are truncated in our logs.
- Idempotency: the body contains a unique 16-character `leadId` (lowercase
  alphanumeric) at whatever path your mapping placed it. Deduplicate on it —
  retries resend the same lead with the same leadId.
- Retries: on failure (non-2xx, timeout, connection error) SavItUp retries
  roughly every 4 minutes, 5 attempts total (~16 minutes), then marks the lead
  failed and alerts our operators.
- Test leads: during onboarding SavItUp can send a synthetic lead built from
  the sample below through your real endpoint. Test leads always carry a
  top-level `"test": true` — never bill, store, or action them as real.

### Field mapping

Your lead body shape is up to you. During onboarding you provide a mapping of
SavItUp canonical source paths to your destination paths, e.g.:

```json
{
  "contact.firstName": "applicant.first_name",
  "contact.email": "applicant.email",
  "lead.leadId": "external_ref"
}
```

Destination paths use dots for nesting; SavItUp builds your JSON accordingly.

### Canonical lead (sample values, obviously fake — real leads share this shape)

```json
{
  "leadId": "sample0000000001",
  "createdAt": "2026-01-15T12:00:00.000Z",
  "product": "auto",
  "state": "CO",
  "zip": "80202",
  "consent": { "profile": true },
  "contact": { "firstName": "Jamie", "lastName": "Rivera", "email": "jamie.rivera@example.com" },
  "addresses": [{ "line1": "742 Evergreen Ter", "city": "Denver", "state": "CO", "zip": "80202" }],
  "household": { "memberCount": 3 },
  "people": [
    { "firstName": "Jamie", "lastName": "Rivera", "age": 34 },
    { "firstName": "Alex", "lastName": "Rivera", "age": 32 }
  ],
  "cars": [{ "year": 2021, "make": "Toyota", "model": "RAV4" }],
  "currentPolicy": { "carrier": "Old Guard Mutual", "premium": 142.5 },
  "acquisition": { "channel": "sem", "method": "code" }
}
```

`product` is `auto` or `home` today. `acquisition` describes how SavItUp
acquired the member (channel granularity only — it contains no third-party
identifiers).

### Eligibility filters

You can scope which leads you receive with per-field filters, applied before
delivery:

```json
{
  "lead.product": { "type": "single", "value": "auto" },
  "lead.state": { "type": "single", "value": "CO" },
  "people.0.age": { "type": "range", "min": 21, "max": 75 },
  "cars.0.year": "unknown"
}
```

Semantics: `"unknown"` always passes; `single` requires an exact match; `range`
requires min ≤ value ≤ max; a lead MISSING a filtered value fails that filter
(fails closed).

## 2. Conversion feedback (you → SavItUp)

Report each lead's outcome so future rate cards reflect your real results.

- Endpoint: `POST {outcomeBaseUrl}/ssp/leads/{leadId}/outcome`
  (the base URL is shared during onboarding).
- Auth: header `x-savitup-key: <your API key>`. Keys are issued and revocable
  by SavItUp; requests are rate-limited per key.
- Body:

```json
{ "result": "converted", "reason": "optional, max 500 chars" }
```

- `result` values:
  - `converted` — the lead became a customer. FINAL: cannot be changed later.
  - `not_converted` — worked but did not convert. May later be replaced by
    `invalid` (below) within the window.
  - `invalid` — unreachable / bad data / not a real prospect. Accepted only
    within 48 hours (inclusive) of our last delivery attempt for that lead.
- Responses: `200` recorded; `400` bad input; `401` bad/revoked key;
  `404` unknown lead (or a lead not delivered to YOUR provider account);
  `409` outcome transition not allowed (e.g., changing a final `converted`).

Feedback is a quality signal only: it informs your NEXT rate card — it never
re-prices already-issued invoices or statements.

## 3. Billing

- Flat fee per delivered lead, from an effective-dated rate card per provider
  and acquisition-source tier, agreed prospectively.
- The fee is stamped at delivery time; monthly statements are never re-priced.
- Leads marked `invalid` within the 48-hour window are excluded from billing.
- Leads delivered while no rate card is active are billed $0 and flagged.

## 4. Security expectations

- TLS 1.2+ everywhere. Lead submissions inside the SavItUp app are gated by
  hardware device attestation (Apple App Attest), so leads originate from real
  devices and real member consent.
- SavItUp never sends member PII over email — delivery and feedback are
  API-only.
- Keep your Authorization header value and `x-savitup-key` secret; ask for
  rotation any time.

## Contact

support@savitup.com — include "SSP integration" in the subject.
