{
  "openapi": "3.1.0",
  "info": {
    "title": "Lempile Email Finder API",
    "version": "1.0.0",
    "description": "Queue-first B2B email and lightweight identity discovery from LinkedIn profile URLs or public identifiers. Lempile uses owned resolver logic, same-workspace LinkedIn profile identity when a Ready account is available, company/domain evidence, candidate generation, and internal SMTP/MX verification by default. Apify and external contact databases are not used."
  },
  "servers": [
    {
      "url": "https://api.lempile.com",
      "description": "Lempile production API"
    }
  ],
  "tags": [
    {
      "name": "Email Finder",
      "description": "Business-email-only LinkedIn email finder jobs with safe identity, company, domain, pattern, internal SMTP/MX verification, and cache evidence."
    }
  ],
  "paths": {
    "/email-finder": {
      "post": {
        "operationId": "EmailFinder_createJob",
        "summary": "Create email finder job",
        "tags": ["Email Finder"],
        "description": "Creates a queue-first email finder job and returns `202`. Poll the job endpoint until it reaches `Completed` or `Error`. Inputs can be plain LinkedIn URLs/ids or safe identity records with optional name, company, and company-domain hints. Email Finder checks cache, same-workspace LinkedIn profile identity when a Ready account is available, public LinkedIn profile previews, company-site evidence, public emails, verified domain patterns, generated candidates, and internal syntax/domain/MX/SMTP-safe verification. Google Search evidence is optional last-resort evidence only when the Google evidence pool is ready.",
        "security": [{ "ApiKeyAuth": [] }],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "properties": {
                  "linkedin_url_or_ids": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 500,
                    "items": {
                      "$ref": "#/components/schemas/EmailFinderInput"
                    },
                    "description": "LinkedIn profile URLs, public identifiers, or safe identity records. Plain strings remain supported for compatibility."
                  },
                  "records": {
                    "type": "array",
                    "minItems": 1,
                    "maxItems": 500,
                    "items": {
                      "$ref": "#/components/schemas/EmailFinderInputRecord"
                    },
                    "description": "Alternative input field for safe identity records. Use this when you already know names, company, or company domain."
                  },
                  "options": {
                    "type": "object",
                    "additionalProperties": true,
                    "properties": {
                      "business_email_only": {
                        "type": "boolean",
                        "default": true,
                        "description": "Email Finder defaults to business emails only."
                      },
                      "profile_resolution": {
                        "type": "string",
                        "enum": ["workspace_linkedin_accounts", "public_only"],
                        "default": "workspace_linkedin_accounts",
                        "description": "Use controlled same-workspace Ready LinkedIn accounts for visible profile identity resolution, or public/cache/domain evidence only."
                      },
                      "verification_mode": {
                        "type": "string",
                        "enum": ["smtp_only", "mx_only"],
                        "default": "smtp_only",
                        "description": "`smtp_only` is the default internal-owned mode: syntax/domain/MX/SMTP-safe verification only. `mx_only` disables SMTP recipient checks and uses domain/MX evidence only."
                      },
                      "provider_max_candidates_per_item": {
                        "type": "integer",
                        "minimum": 0,
                        "maximum": 20,
                        "default": 0,
                        "description": "Reserved for future external verifier fallback. Current public jobs are internal-only and this value is clamped to `0`."
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "202": {
            "description": "Email finder job accepted.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EmailFinderJob" }
              }
            }
          },
          "400": {
            "description": "Invalid input.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "401": {
            "description": "Missing or invalid API key.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "402": {
            "description": "No active trial or subscription (`subscription_required`), or a trial that used its 1,000 included found emails (`email_finder_trial_limit`).",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "403": {
            "description": "API key lacks `email_finder.write`.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          },
          "429": {
            "description": "Workspace email finder queue is full.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/email-finder/usage": {
      "get": {
        "operationId": "EmailFinder_getUsage",
        "summary": "Get email finder usage",
        "description": "Found emails this billing period and what they cost. Only emails the company's mail server confirmed (status `Found`) count; `Uncertain` and `Not Found` are free. Each period includes 1,000 found emails, then 10 EUR (or GBP) per 1,000. A trial stops at the 1,000 included: new jobs answer 402 `email_finder_trial_limit` until the subscription starts.",
        "tags": ["Email Finder"],
        "security": [{ "ApiKeyAuth": [] }],
        "responses": {
          "200": {
            "description": "This period's usage.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EmailFinderUsage" }
              }
            }
          },
          "403": {
            "description": "API key lacks `email_finder.read`.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/Error" }
              }
            }
          }
        }
      }
    },
    "/email-finder/jobs/{jobId}": {
      "get": {
        "operationId": "EmailFinder_getJob",
        "summary": "Get email finder job",
        "tags": ["Email Finder"],
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/jobId" }],
        "responses": {
          "200": {
            "description": "Completed or failed job.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EmailFinderJob" }
              }
            }
          },
          "202": {
            "description": "Job is still queued or running.",
            "content": {
              "application/json": {
                "schema": { "$ref": "#/components/schemas/EmailFinderJob" }
              }
            }
          }
        }
      }
    },
    "/email-finder/jobs/{jobId}/items": {
      "get": {
        "operationId": "EmailFinder_getItems",
        "summary": "List email finder items",
        "tags": ["Email Finder"],
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/jobId" }],
        "responses": {
          "200": {
            "description": "Item results for the job.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "properties": {
                    "object": { "const": "email_finder_item_list" },
                    "job": { "$ref": "#/components/schemas/EmailFinderJob" },
                    "items": {
                      "type": "array",
                      "items": {
                        "$ref": "#/components/schemas/EmailFinderItem"
                      }
                    }
                  },
                  "required": ["object", "job", "items"]
                }
              }
            }
          }
        }
      }
    },
    "/email-finder/jobs/{jobId}.csv": {
      "get": {
        "operationId": "EmailFinder_getCsv",
        "summary": "Export email finder CSV",
        "tags": ["Email Finder"],
        "security": [{ "ApiKeyAuth": [] }],
        "parameters": [{ "$ref": "#/components/parameters/jobId" }],
        "responses": {
          "200": {
            "description": "CSV export.",
            "content": {
              "text/csv": {
                "schema": { "type": "string" }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "ApiKeyAuth": {
        "type": "apiKey",
        "in": "header",
        "name": "X-API-KEY"
      }
    },
    "parameters": {
      "jobId": {
        "name": "jobId",
        "in": "path",
        "required": true,
        "schema": { "type": "string" }
      }
    },
    "schemas": {
      "EmailFinderInput": {
        "oneOf": [
          {
            "type": "string",
            "description": "LinkedIn profile URL or public identifier."
          },
          { "$ref": "#/components/schemas/EmailFinderInputRecord" }
        ]
      },
      "EmailFinderInputRecord": {
        "type": "object",
        "description": "Safe identity hints for owned evidence resolution. `linkedin_url_or_id` is required; all other fields are optional.",
        "properties": {
          "linkedin_url_or_id": {
            "type": "string",
            "description": "LinkedIn profile URL or public identifier."
          },
          "linkedin_url": {
            "type": "string",
            "description": "Accepted alias for `linkedin_url_or_id`."
          },
          "linkedin_id": {
            "type": "string",
            "description": "Accepted alias for `linkedin_url_or_id`."
          },
          "first_name": { "type": "string" },
          "last_name": { "type": "string" },
          "full_name": { "type": "string" },
          "company": { "type": "string" },
          "company_domain": {
            "type": "string",
            "description": "Company website domain, for example `example.com`."
          },
          "company_url": {
            "type": "string",
            "description": "Optional company website or company page URL evidence."
          }
        },
        "additionalProperties": true
      },
      "EmailFinderJob": {
        "type": "object",
        "properties": {
          "object": { "const": "email_finder_job" },
          "id": { "type": "string" },
          "status": {
            "type": "string",
            "enum": ["Queued", "Running", "Completed", "Error", "Canceled"]
          },
          "status_detail": { "type": ["string", "null"] },
          "counts": {
            "type": "object",
            "properties": {
              "input": { "type": "integer" },
              "completed": { "type": "integer" },
              "found": { "type": "integer" },
              "not_found": { "type": "integer" },
              "uncertain": { "type": "integer" },
              "error": { "type": "integer" },
              "retrying": {
                "type": "integer",
                "description": "Uncertain items still waiting for a scheduled retry. They can become Found after the job is Completed."
              }
            }
          },
          "next_retry_at": {
            "type": "string",
            "description": "When the next scheduled retry runs (UTC). Present only while counts.retrying is above 0."
          },
          "endpoints": {
            "type": "object",
            "properties": {
              "jobEndpoint": { "type": "string", "format": "uri" },
              "itemsEndpoint": { "type": "string", "format": "uri" },
              "csvEndpoint": { "type": "string", "format": "uri" }
            }
          }
        },
        "required": ["object", "id", "status"]
      },
      "EmailFinderItem": {
        "type": "object",
        "properties": {
          "object": { "const": "email_finder_item" },
          "id": { "type": "string" },
          "input": { "type": "string" },
          "linkedin_id": { "type": ["string", "null"] },
          "linkedin_url": { "type": ["string", "null"], "format": "uri" },
          "status": {
            "type": "string",
            "enum": [
              "Queued",
              "Running",
              "Found",
              "Not Found",
              "Uncertain",
              "Suppressed",
              "Error"
            ]
          },
          "first_name": { "type": ["string", "null"] },
          "last_name": { "type": ["string", "null"] },
          "full_name": { "type": ["string", "null"] },
          "title": { "type": ["string", "null"] },
          "headline": { "type": ["string", "null"] },
          "location": { "type": ["string", "null"] },
          "country": { "type": ["string", "null"] },
          "company": { "type": ["string", "null"] },
          "company_domain": { "type": ["string", "null"] },
          "company_url": { "type": ["string", "null"], "format": "uri" },
          "company_source": {
            "type": ["string", "null"],
            "description": "Safe source label for the resolved company field or company URL."
          },
          "email": { "type": ["string", "null"] },
          "confidence": {
            "type": ["integer", "null"],
            "minimum": 0,
            "maximum": 100
          },
          "verification_status": { "type": ["string", "null"] },
          "verification_provider": { "type": ["string", "null"] },
          "verification_result": { "type": ["string", "null"] },
          "verification_sub_status": {
            "type": ["string", "null"],
            "description": "Safe external-provider sub-status when an explicit provider mode is enabled. Raw provider payloads are not returned."
          },
          "identity_source": {
            "type": ["string", "null"],
            "description": "Safe source label for resolved identity fields, for example `input_hints`, `linkedin_profile`, `linkedin_public_preview`, or `google_public_evidence`."
          },
          "identity_confidence": {
            "type": ["integer", "null"],
            "minimum": 0,
            "maximum": 100
          },
          "domain_source": {
            "type": ["string", "null"],
            "description": "Safe source label for the resolved company domain, for example `input_hints`, `company_homepage`, or `google_public_evidence`."
          },
          "pattern_source": {
            "type": ["string", "null"],
            "description": "Safe source label for the email pattern used when returning an uncertain generated candidate."
          },
          "candidate_pattern": {
            "type": ["string", "null"],
            "description": "Pattern used to generate the returned candidate, for example `first.last`."
          },
          "pattern_confidence": {
            "type": ["integer", "null"],
            "minimum": 0,
            "maximum": 100
          },
          "confidence_reason": {
            "type": ["string", "null"],
            "description": "Human-readable reason for the confidence score and final status."
          },
          "confidence_bucket": {
            "type": ["string", "null"],
            "enum": ["likely", "uncertain", "low", null],
            "description": "Derived confidence bucket. `likely` is still returned with API status `Uncertain` unless strict deliverability proof exists."
          },
          "confidence_score": {
            "type": ["integer", "null"],
            "minimum": 0,
            "maximum": 100
          },
          "uncertain_bucket": {
            "type": ["string", "null"],
            "enum": [
              "likely",
              "catch_all",
              "protected",
              "greylisted",
              "timeout",
              "temporary_failure",
              "unknown",
              "low",
              null
            ],
            "description": "Safe reason bucket for `Uncertain` items."
          },
          "likely_reason": {
            "type": ["string", "null"],
            "description": "Short safe reason explaining why a generated candidate is likely."
          },
          "retry_after": {
            "type": ["string", "null"],
            "description": "ISO timestamp for the next SMTP-safe retry when the failure is greylisted, timeout, or temporary."
          },
          "retry_strategy": {
            "type": ["string", "null"],
            "description": "Safe retry policy label, such as `smtp_soft_failure_retry_15m`."
          },
          "unknown_reason": {
            "type": ["string", "null"],
            "description": "Safe explanation when a generated candidate remains lower-confidence or inconclusive."
          },
          "smtp_infra_reason": {
            "type": ["string", "null"],
            "description": "Sanitized SMTP infrastructure reason. Raw SMTP transcripts are never returned."
          },
          "smtp_classification": {
            "type": ["string", "null"],
            "description": "Sanitized SMTP classification such as `catch_all`, `protected`, `greylisted`, `timeout`, or `temporary_failure`."
          },
          "domain_behavior": {
            "type": ["string", "null"],
            "description": "Safe domain behavior label learned from DNS/MX and SMTP-safe checks."
          },
          "source_provider": {
            "type": ["string", "null"],
            "description": "For the owned resolver this is normally `lempile_owned_public_evidence` or null."
          },
          "diagnostics": { "type": "object", "additionalProperties": true }
        },
        "required": ["object", "id", "input", "status"]
      },
      "EmailFinderUsage": {
        "type": "object",
        "required": ["object", "period_start", "found_emails", "billable_found_emails"],
        "properties": {
          "object": { "type": "string", "enum": ["email_finder_usage"] },
          "period_start": { "type": "string", "format": "date-time" },
          "period_end": { "type": ["string", "null"], "format": "date-time" },
          "found_emails": {
            "type": "integer",
            "description": "Items with status `Found` that finished this period."
          },
          "included_found_emails": {
            "type": ["integer", "null"],
            "description": "1,000, or null for a workspace that is never billed."
          },
          "billable_found_emails": {
            "type": "integer",
            "description": "Found emails past the included ones."
          },
          "price_per_1000": {
            "type": ["object", "null"],
            "properties": {
              "amount": { "type": "number" },
              "currency": { "type": "string" }
            }
          },
          "estimated_amount": {
            "type": ["object", "null"],
            "properties": {
              "amount": { "type": "number" },
              "currency": { "type": "string" }
            }
          },
          "billing": {
            "type": ["string", "null"],
            "description": "`free`, or the subscription status (`trialing`, `active`, `past_due`, ...)."
          },
          "note": { "type": "string" }
        }
      },
      "Error": {
        "type": "object",
        "properties": {
          "error": { "type": "string" },
          "message": { "type": "string" }
        },
        "required": ["error"]
      }
    }
  }
}
