{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/dealboard/json-schema/dealboard-lead-schema.json",
  "title": "Lead",
  "x-generated": "2026-11-12",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/dealboard-openapi.json#/components/schemas/Lead",
  "type": "object",
  "required": [
    "id",
    "boardId",
    "triageStatus",
    "source",
    "dealId",
    "contact",
    "enrichment",
    "createdAt",
    "contactedAt",
    "promotedAt",
    "junkedAt"
  ],
  "properties": {
    "id": {
      "type": "string",
      "format": "uuid"
    },
    "boardId": {
      "type": "string",
      "format": "uuid"
    },
    "triageStatus": {
      "type": "string",
      "enum": [
        "new",
        "contacted",
        "promoted",
        "junk",
        "bad_fit",
        "stale"
      ],
      "description": "Lifecycle marker for the Lead inbox. `new` → `contacted` is recorded when a reply goes out; `promoted`/`junk`/`bad_fit` are terminal triage outcomes; `stale` is set by a daily cron if a Lead sits in `new` too long."
    },
    "source": {
      "type": "string",
      "enum": [
        "form",
        "email"
      ],
      "description": "How the Lead arrived: `form` (website/hosted form) or `email` (inbound email)."
    },
    "dealId": {
      "type": [
        "string",
        "null"
      ],
      "format": "uuid",
      "description": "Set once the Lead has been promoted to a Deal via POST /v1/leads/{id}/promote. Null otherwise."
    },
    "contact": {
      "type": "object",
      "required": [
        "firstName",
        "lastName",
        "email",
        "phone",
        "companyName",
        "companyWebsite",
        "message"
      ],
      "description": "Submitted form values verbatim. Same shape as the form fields the workspace admin configured.",
      "properties": {
        "firstName": {
          "type": [
            "string",
            "null"
          ]
        },
        "lastName": {
          "type": [
            "string",
            "null"
          ]
        },
        "email": {
          "type": [
            "string",
            "null"
          ],
          "format": "email"
        },
        "phone": {
          "type": [
            "string",
            "null"
          ]
        },
        "companyName": {
          "type": [
            "string",
            "null"
          ]
        },
        "companyWebsite": {
          "type": [
            "string",
            "null"
          ]
        },
        "message": {
          "type": [
            "string",
            "null"
          ]
        },
        "howHeard": {
          "type": [
            "string",
            "null"
          ],
          "description": "\"How did you hear about us?\" answer, when collected."
        },
        "consent": {
          "type": [
            "boolean",
            "null"
          ],
          "description": "Marketing/SMS consent given on the form (null = not asked)."
        }
      }
    },
    "enrichment": {
      "$ref": "#/$defs/LeadEnrichment"
    },
    "createdAt": {
      "type": "string",
      "format": "date-time"
    },
    "contactedAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "promotedAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    },
    "junkedAt": {
      "type": [
        "string",
        "null"
      ],
      "format": "date-time"
    }
  },
  "$defs": {
    "LeadEnrichment": {
      "type": "object",
      "additionalProperties": true,
      "description": "Free-form qualifier payload. Common fields shown below — additional keys are preserved verbatim. `history` is server-managed; PATCH bodies cannot overwrite it.",
      "properties": {
        "status": {
          "type": "string",
          "enum": [
            "queued",
            "in_progress",
            "complete",
            "skipped"
          ],
          "description": "Async lifecycle. Send `in_progress` when your tool claims the lead (scoring can take minutes); the inbox shows \"Scoring…\". Then `complete` (or `skipped`) with the result."
        },
        "score": {
          "type": "number",
          "minimum": 0,
          "maximum": 100,
          "description": "Normalized 0–100 fit/ICP score. Drives the inbox badge color + sort. Send the human-readable original (e.g. \"9/10\") in `scoreDisplay`."
        },
        "scoreDisplay": {
          "type": "string",
          "description": "Human-readable score as your tool renders it, e.g. \"9/10\" or \"B+\". Display-only."
        },
        "verdict": {
          "type": "string",
          "description": "Short free-form recommendation/label, e.g. \"Reach out and chat\", \"Ignore\". Shown next to the score."
        },
        "body": {
          "type": "string",
          "description": "Rich writeup (markdown — bold/bullets/emoji), shown on the lead detail."
        },
        "provider": {
          "type": "string",
          "description": "Which tool produced this (e.g. \"hazel\", \"clearbit\")."
        },
        "contact": {
          "type": "object",
          "additionalProperties": false,
          "description": "Enriched CONTACT — the person behind the lead. Generic across every qualifier (mirrors a deal’s own contact fields). When the lead is promoted to a deal, these GAP-FILL the deal’s primary contact (the lead’s own values win). Recommended to populate — especially `linkedin`, which lands in the deal’s LinkedIn field just like phone/email/title.",
          "properties": {
            "name": {
              "type": "string",
              "description": "Full name of the contact."
            },
            "title": {
              "type": "string",
              "description": "Job title, e.g. \"VP of Product\"."
            },
            "email": {
              "type": "string",
              "format": "email"
            },
            "phone": {
              "type": "string"
            },
            "linkedin": {
              "type": "string",
              "format": "uri",
              "description": "Full https URL to the contact’s LinkedIn profile, e.g. \"https://www.linkedin.com/in/jane-doe\". Strongly recommended — it populates the promoted deal’s LinkedIn field. (If omitted, Dealboard also picks up the first linkedin.com/in/ URL it finds in `body`.)"
            },
            "website": {
              "type": "string",
              "format": "uri"
            }
          }
        },
        "screenshot": {
          "type": "string",
          "description": "A website/homepage screenshot for the enrichment card. PATCH a public https URL; Dealboard downloads the image and stores it permanently, so it survives even if your original URL later expires. On read, this is a durable Dealboard-hosted URL."
        },
        "screenshotSource": {
          "type": "string",
          "format": "uri",
          "description": "Read-only. The original provider URL the stored `screenshot` was ingested from."
        },
        "summary": {
          "type": "string"
        },
        "notes": {
          "type": "string"
        },
        "attachments": {
          "type": "array",
          "items": {
            "type": "object",
            "required": [
              "url"
            ],
            "properties": {
              "url": {
                "type": "string",
                "description": "Image URL. PATCH a public https URL; image attachments are downloaded and stored permanently (on read, `url` is a durable Dealboard-hosted URL)."
              },
              "filename": {
                "type": "string"
              },
              "kind": {
                "type": "string"
              },
              "sourceUrl": {
                "type": "string",
                "format": "uri",
                "description": "Read-only. The original provider URL this stored image was ingested from."
              }
            }
          }
        },
        "history": {
          "type": "array",
          "items": {
            "$ref": "#/$defs/LeadEnrichmentHistoryEntry"
          },
          "description": "Append-only audit trail of every PATCH /v1/leads/{id} call. Server-managed."
        }
      }
    },
    "LeadEnrichmentHistoryEntry": {
      "type": "object",
      "required": [
        "at",
        "by",
        "payload"
      ],
      "properties": {
        "at": {
          "type": "string",
          "format": "date-time"
        },
        "by": {
          "type": "string",
          "description": "API key id that wrote the enrichment patch."
        },
        "payload": {
          "type": "object",
          "additionalProperties": true,
          "description": "The keys that were merged at this point in time."
        }
      }
    }
  }
}