Universal Lead Schema (ULS) guide

ULS v1.0 is the canonical envelope every Switchly request and webhook payload uses. Nine blocks, top-level keys on the JSON body:

lead_metadata | consumer | product_specific | compliance | scoring
routing | delivery | fraud | custom

lead_metadata, consumer, and compliance are required on ingest. The other six are optional and Switchly fills them in if you don't.

lead_metadata

The envelope — who, what, when.

field type required notes
lead_id UUID v4 string Your platform's UUID. Switchly will mint its own to use internally and return it as lead_id in the response.
timestamp ISO 8601 When the consumer actually generated the lead on your platform, not when you're calling Switchly.
product_line enum One of auto, home, renters, life, medicare, final_expense, commercial, health.
source_platform string Your platform slug. Switchly fills it in from your auth context if omitted.
source_campaign string Ad group, landing page slug, keyword — whatever you want to roll up by.
lead_type enum form_fill, inbound_call, warm_transfer, aged. Drives the scoring engine's intent calculation.
intent_level enum high / medium / low. Coarse human override.
priority 1–10 1 is highest. Used as a tiebreaker.
tags string[] Free-form. Shows up in audit logs.

consumer

Identity + reachability. Switchly never charges for unreachable leads, but the more you provide, the better the contactability score.

{
  "first_name": "Jordan",
  "last_name": "Rivers",
  "dob": "1958-04-12",
  "gender": "F",
  "email": "jordan@example.test",
  "phone": "+1-555-0100",
  "alt_phone": "+1-555-0101",
  "address": {
    "street": "123 Pine St",
    "city": "Indianapolis",
    "state": "IN",
    "zip": "46202"
  }
}

dob is mandatory for Medicare and Life routing; recommended everywhere. address.state is the primary routing key — Switchly matches it against each agent's active state licenses.

product_specific

Free-form per-product data. Switchly stores it as-is and surfaces it on the delivery payload so the receiving agent sees what you collected. Examples:

// medicare
"product_specific": {
  "current_plan": "Original Medicare",
  "dual_eligible": false,
  "special_enrollment": true
}

// auto
"product_specific": {
  "vehicles": [{"year": 2021, "make": "Toyota", "model": "Camry"}],
  "drivers": [{"age": 34, "accidents": 0}]
}

No schema is enforced here — that's deliberate. Add or remove fields without coordinating a Switchly schema change.

compliance

TCPA + fraud-detection signals. tcpa_consent is the only required field, but routing engines treat leads without timestamps and source URLs as second-class.

{
  "tcpa_consent": true,
  "tcpa_timestamp": "2026-05-22T14:59:30Z",
  "tcpa_source_url": "https://example.test/quote",
  "recording_url": null,
  "ip_address": "198.51.100.42",
  "user_agent": "Mozilla/5.0 ...",
  "language": "en"
}

The ip_address field also powers velocity-burst fraud detection — five+ leads from the same IP within an hour trips a lead.flagged_fraud event.

scoring

The five canonical 0–1 floats:

field what it measures
quality_score Overall lead quality (composite).
intent_score Buyer intent.
contactability_score Likelihood the agent can reach the consumer.
fraud_score Risk of fraud (higher = worse).
confidence How complete the data is.

If you ship these, Switchly trusts them. If you omit the block (or any field within it) Switchly auto-scores via its deterministic engine after ingest. See the RoutingScore formula for how each component is weighted to rank candidate agents.

routing

Optional hints from your platform. Switchly uses these as preferences, not hard rules — agent eligibility + scoring still apply.

{
  "preferred_agents": [42, 17],
  "excluded_agents": [],
  "geo_rules": {
    "zip": ["46202", "462"],
    "state": ["IN", "OH"],
    "radius_miles": 50
  },
  "delivery_method": "webhook",
  "max_retries": 5,
  "retry_delay_seconds": 30
}

delivery, fraud

These are derived at emit time — you should never need to send them. They appear on GET /api/v1/leads/{id}/status and on outbound webhooks.

delivery aggregates per-attempt rows from lead_assignments:

{
  "status": "assigned",
  "attempts": 1,
  "last_attempt": "2026-05-22T15:01:14Z",
  "delivered_to": ["7"],
  "errors": []
}

fraud aggregates the append-only lead_fraud_signals table:

{
  "duplicate_within_network": true,
  "duplicate_ids": ["abc…", "def…"],
  "velocity_flags": [],
  "data_mismatch_flags": [],
  "blacklist_flags": []
}

custom

Anything you want. Switchly persists it and round-trips it. Use this for platform-specific extensions before requesting a ULS field promotion.

{
  "internal_ref": "MAH-2026-Q2-1234",
  "rep_id": "kelly-l",
  "experimental_flag": true
}