> ## Documentation Index
> Fetch the complete documentation index at: https://docs.anyformat.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Email lead extraction

> Turn an inbound sales email into a structured lead: sender, company, inquiry type, urgency and topics

This workflow reads an email body and pulls out who wrote it, what they want and how urgent it is. The API takes files only, so the email body goes up as a `.txt` file; the Python SDK does that for you when you pass `text=`.

<div className="af-payoff">
  <div className="af-payoff-pane">
    <div className="af-payoff-cap">The email</div>

    <div className="af-payoff-line">
      <span className="af-mark">
        {'From: Maria Lopez <maria.lopez@acmecorp.example>'}
      </span>
    </div>

    <div className="af-payoff-line">
      {'Subject: Enterprise pricing for Q2 rollout'}
    </div>

    <div className="af-payoff-line">
      <span className="af-mark">
        {'I am the VP of Engineering at Acme Corp.'}
      </span>
    </div>

    <div className="af-payoff-line">
      {'What does the enterprise plan cost at our volume?'}
    </div>

    <div className="af-payoff-line">
      <span className="af-mark">
        {'Our board meets on June 12 …'}
      </span>
    </div>
  </div>

  <div className="af-payoff-arrow">→</div>

  <div className="af-payoff-pane">
    <div className="af-payoff-cap">What comes back</div>
    <div className="af-payoff-line"><span className="af-payoff-key">sender\_name</span><b>{'Maria Lopez'}</b>{'  '}<span className="af-cite">99%</span></div>
    <div className="af-payoff-line"><span className="af-payoff-key">company\_name</span><b>{'Acme Corp'}</b>{'  '}<span className="af-cite">99%</span></div>
    <div className="af-payoff-line"><span className="af-payoff-key">inquiry\_type</span><b>{'pricing'}</b>{'  '}<span className="af-cite">98%</span></div>
    <div className="af-payoff-line"><span className="af-payoff-key">urgency</span><b>{'high'}</b>{'  '}<span className="af-cite">99%</span></div>
    <div className="af-payoff-line"><span className="af-payoff-key">topics</span><b>{'enterprise, pricing, security, integration'}</b></div>

    <div className="af-payoff-line">
      <span className="af-cite">
        {'↳ topics is one comma-separated string'}
      </span>
    </div>
  </div>
</div>

<div className="af-cost">
  <span><b>Nodes</b><strong>Parse → Extract</strong></span>
  <span><b>Credits</b><strong>60 per page</strong>{' (Parse 25 + Extract 35, default tiers); one short email is one page'}</span>
  <span><b>You get</b>{' 7 fields, including two enums and a multi_select'}</span>
</div>

## The graph

<CodeGroup>
  ```json Graph theme={null}
  {
    "name": "Email lead extractor",
    "nodes": [
      { "id": "parse_1", "type": "parse" },
      {
        "id": "extract_1",
        "type": "extract",
        "extraction_schema": {
          "fields": [
            { "name": "sender_name",  "description": "Full name of the person who sent the email", "data_type": "string" },
            { "name": "sender_email", "description": "Email address of the sender", "data_type": "string" },
            { "name": "company_name", "description": "Company or organization the sender represents", "data_type": "string" },
            {
              "name": "inquiry_type",
              "description": "The primary category of this inquiry",
              "data_type": "enum",
              "enum_options": [
                { "name": "pricing",      "description": "Pricing or cost inquiry" },
                { "name": "demo_request", "description": "Request for a product demo" },
                { "name": "support",      "description": "Technical support or help request" },
                { "name": "partnership",  "description": "Partnership or integration inquiry" },
                { "name": "other",        "description": "Other inquiry type" }
              ]
            },
            {
              "name": "urgency",
              "description": "How urgent this request appears based on language and deadlines mentioned",
              "data_type": "enum",
              "enum_options": [
                { "name": "low",    "description": "No urgency signals" },
                { "name": "medium", "description": "Moderate urgency or soft deadline" },
                { "name": "high",   "description": "Explicit deadline or urgent language" }
              ]
            },
            {
              "name": "topics",
              "description": "Products or areas of interest mentioned in the email",
              "data_type": "multi_select",
              "enum_options": [
                { "name": "enterprise",  "description": "Enterprise plan or features" },
                { "name": "integration", "description": "Integration or API capabilities" },
                { "name": "pricing",     "description": "Pricing or billing" },
                { "name": "security",    "description": "Security or compliance" }
              ]
            },
            { "name": "summary", "description": "A one-sentence summary of what the sender is asking for", "data_type": "string" }
          ]
        }
      }
    ],
    "edges": [{ "source": "parse_1", "target": "extract_1" }]
  }
  ```

  ```python Python theme={null}
  import os
  from anyformat.sdk import Client
  from anyformat.workflow import Schema

  client = Client(api_key=os.environ["ANYFORMAT_API_KEY"])

  workflow = (
      client.workflow("Email lead extractor")
      .parse()
      .extract([
          Schema.string("sender_name",  "Full name of the person who sent the email"),
          Schema.string("sender_email", "Email address of the sender"),
          Schema.string("company_name", "Company or organization the sender represents"),
          Schema.enum("inquiry_type", "The primary category of this inquiry", options=[
              Schema.option("pricing",      "Pricing or cost inquiry"),
              Schema.option("demo_request", "Request for a product demo"),
              Schema.option("support",      "Technical support or help request"),
              Schema.option("partnership",  "Partnership or integration inquiry"),
              Schema.option("other",        "Other inquiry type"),
          ]),
          Schema.enum("urgency", "How urgent this request appears based on language and deadlines mentioned", options=[
              Schema.option("low",    "No urgency signals"),
              Schema.option("medium", "Moderate urgency or soft deadline"),
              Schema.option("high",   "Explicit deadline or urgent language"),
          ]),
          Schema.multi_select("topics", "Products or areas of interest mentioned in the email", options=[
              Schema.option("enterprise",  "Enterprise plan or features"),
              Schema.option("integration", "Integration or API capabilities"),
              Schema.option("pricing",     "Pricing or billing"),
              Schema.option("security",    "Security or compliance"),
          ]),
          Schema.string("summary", "A one-sentence summary of what the sender is asking for"),
      ])
      .create()
  )
  ```

  ```typescript TypeScript theme={null}
  import { Anyformat, Schema } from "@anyformat/sdk";

  const af = new Anyformat({ apiKey: process.env.ANYFORMAT_API_KEY! });

  const workflow = await af
    .workflow("Email lead extractor")
    .parse()
    .extract([
      Schema.string("sender_name",  "Full name of the person who sent the email"),
      Schema.string("sender_email", "Email address of the sender"),
      Schema.string("company_name", "Company or organization the sender represents"),
      Schema.enum("inquiry_type", "The primary category of this inquiry", [
        Schema.option("pricing",      "Pricing or cost inquiry"),
        Schema.option("demo_request", "Request for a product demo"),
        Schema.option("support",      "Technical support or help request"),
        Schema.option("partnership",  "Partnership or integration inquiry"),
        Schema.option("other",        "Other inquiry type"),
      ]),
      Schema.enum("urgency", "How urgent this request appears based on language and deadlines mentioned", [
        Schema.option("low",    "No urgency signals"),
        Schema.option("medium", "Moderate urgency or soft deadline"),
        Schema.option("high",   "Explicit deadline or urgent language"),
      ]),
      Schema.multiSelect("topics", "Products or areas of interest mentioned in the email", [
        Schema.option("enterprise",  "Enterprise plan or features"),
        Schema.option("integration", "Integration or API capabilities"),
        Schema.option("pricing",     "Pricing or billing"),
        Schema.option("security",    "Security or compliance"),
      ]),
      Schema.string("summary", "A one-sentence summary of what the sender is asking for"),
    ])
    .create();
  ```
</CodeGroup>

## Run and read

Send the email body as a text file. Any `.txt` works; a saved `.eml` works too and keeps the headers.

<CodeGroup>
  ```bash curl theme={null}
  # The body of the email, saved as a file
  curl -X POST "https://api.anyformat.ai/v3/workflows/$WORKFLOW_ID/upload/run/" \
    -H "Authorization: Bearer $ANYFORMAT_API_KEY" \
    -F 'files=@lead-email.txt;type=text/plain'
  # -> 202 {"run_id": "...", "status": "queued"}

  # Read the run. Repeat until "status" is "processed".
  curl "https://api.anyformat.ai/v3/runs/$RUN_ID/" \
    -H "Authorization: Bearer $ANYFORMAT_API_KEY"
  ```

  ```python Python theme={null}
  email_text = """From: Maria Lopez <maria.lopez@acmecorp.example>
  Subject: Enterprise pricing for Q2 rollout

  Hi, I am the VP of Engineering at Acme Corp. We process about 40,000 supplier
  invoices a month ... Our board meets on June 12 and I need a quote and a
  security overview before then. ..."""

  # text= uploads the string as a .txt file for you
  result = workflow.run(text=email_text).wait()

  lead = {name: field.value for name, field in result.fields.items()}
  if lead["urgency"] == "high":
      print(f"HIGH PRIORITY: {lead['sender_name']} from {lead['company_name']}")
  ```

  ```typescript TypeScript theme={null}
  import type { ExtractedField } from "@anyformat/sdk";

  const emailText = `From: Maria Lopez <maria.lopez@acmecorp.example>
  Subject: Enterprise pricing for Q2 rollout

  Hi, I am the VP of Engineering at Acme Corp. ...`;

  const file = new File([emailText], "lead-email.txt", { type: "text/plain" });
  const run = await workflow.run(file);
  const result = await run.wait();

  // field() can return a scalar or rows; every field here is a scalar
  const field = (name: string) => result.field(name) as ExtractedField<string> | undefined;
  if (field("urgency")?.value === "high") {
    console.log(`HIGH PRIORITY: ${field("sender_name")?.value} from ${field("company_name")?.value}`);
  }
  ```
</CodeGroup>

## What comes back

The run above, trimmed. It took 10 seconds.

```json Response highlight={8,9} theme={null}
{
  "results": {
    "extractions": [{ "fields": {
      "sender_name":  { "value": "Maria Lopez", "confidence": 99.0, "evidence": [{ "text": "From: Maria Lopez <maria.lopez@acmecorp.example>", "page_number": 1 }] },
      "company_name": { "value": "Acme Corp", "confidence": 99.0, "evidence": [{ "text": "I am the VP of Engineering at Acme Corp.", "page_number": 1 }] },
      "inquiry_type": { "value": "pricing", "confidence": 98.0, "evidence": [{ "text": "1. What does the enterprise plan cost at our volume?", "page_number": 1 }] },
      "urgency":      { "value": "high", "confidence": 99.0, "evidence": [{ "text": "Our board meets on June 12 and I need a quote and a security overview before then.", "page_number": 1 }] },
      "topics":       { "value": "enterprise, pricing, security, integration", "confidence": 98.0, "evidence": ["…"] },
      "summary":      { "value": "Maria Lopez is requesting an enterprise-volume quote, a security overview and ERP API integration information before June 12.", "confidence": 4.0, "evidence": ["…"] }
    } }]
  }
}
```

Every field also carries `verification_status` and `value_override`. [Runs and results](/concepts/runs-and-results) has the full envelope, section by section.

## When it goes wrong

**Your topics field looks like a single topic.** A `multi_select` value arrives as one comma-separated string: `"enterprise, pricing, security, integration"`. Split on `", "` to get the list back.

**A good lead is rejected on a low confidence.** The `summary` above scored 4%. A generated sentence is not quoted from the email, so it has no source phrase to score against, and it scores low even when it is right. Gate on the extracted fields, and never on the summary.

**Two inquiry types keep swapping.** `pricing` and `demo_request` read alike to a model given only their names. The `enum_options` descriptions are what separate them, so write the description that names the close case: "Pricing or cost inquiry" against "Request for a product demo".

**`parse.blocks` is empty and `parse_confidence` is `null`.** A `.txt` upload has no layout to ground, so there are no blocks and no parse score. That is expected for text input. Gate a text pipeline on the extracted fields instead.

**A mailbox backs up.** Create the workflow once and call upload and run per email. The submission limit is 60 requests per minute, so pace the queue rather than firing the whole inbox at once.

## Next steps

<CardGroup cols={2}>
  <Card title="Extract node" icon="table-list" href="/guides/nodes/extract">
    Field types, modes and smart lookup
  </Card>

  <Card title="Upload and run" icon="play" href="/api-reference-v3/workflows/upload-and-run">
    Multipart fields, conflicts and idempotency
  </Card>

  <Card title="Classify node" icon="tags" href="/guides/nodes/classify">
    Route each email type to its own Extract node
  </Card>

  <Card title="Field types" icon="list" href="/concepts/field-types">
    Every data type a field can take
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.