{
  "openapi": "3.0.0",
  "paths": {
    "/v1/platforms": {
      "get": {
        "operationId": "PlatformsController_list",
        "summary": "List every platform that can be named in a lookup",
        "description": "Every slug accepted by the `platforms` parameter, with the query types that can reach it and whether it answers on the instant path. A platform whose `supports` does not include the input you send is skipped rather than rejected.",
        "parameters": [],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PlatformListDto"
                }
              }
            }
          }
        },
        "tags": [
          "Lookup"
        ],
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/lookup": {
      "post": {
        "operationId": "LookupController_createLookup",
        "summary": "Run a lookup",
        "description": "Send one of `query` (auto-detected), `email`, or `phone`. Returns immediately with a `lookupId` to poll, or pass `?wait=true` to block until the full result is ready.",
        "parameters": [
          {
            "name": "wait",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ],
              "example": "true"
            },
            "description": "Set to `true` to wait for the full result."
          },
          {
            "name": "instant",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ],
              "example": "true"
            },
            "description": "Set to `true` for a latency-bounded answer (default ceiling 900ms). Runs a high-signal subset of platforms and returns whatever answered in time; anything still in flight is omitted, and `platformsPending` says how much. Always synchronous - `wait` is not needed. Use it when you are fanning out to several APIs per transaction and cannot absorb a slow one."
          },
          {
            "name": "deep",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ],
              "example": "true"
            },
            "description": "Set to `true` to check the full platform set rather than the default band. Slower and unbounded - use it when coverage matters more than latency. The async path (POST without `wait`) already runs at this depth."
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/LookupRequestDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "With `?wait=true`, the full result. Without it, a job handle to poll.",
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/LookupResponseDto"
                    },
                    {
                      "$ref": "#/components/schemas/PendingLookupDto"
                    }
                  ]
                }
              }
            }
          },
          "402": {
            "description": "Free trial used up, or monthly plan limit reached.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorDto"
                }
              }
            }
          },
          "422": {
            "description": "No query, email, or phone was sent.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorDto"
                }
              }
            }
          },
          "429": {
            "description": "Rate limit exceeded.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorDto"
                }
              }
            }
          }
        },
        "tags": [
          "Lookup"
        ],
        "security": [
          {
            "apiKey": []
          }
        ]
      },
      "get": {
        "operationId": "LookupController_listLookups",
        "summary": "List your lookups",
        "description": "Most recent first. Scoped to the account the API key belongs to.",
        "parameters": [
          {
            "name": "limit",
            "required": false,
            "in": "query",
            "description": "Default 20, max 100.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "offset",
            "required": false,
            "in": "query",
            "description": "Default 0.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The lookups this account has run.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "array",
                  "items": {
                    "$ref": "#/components/schemas/LookupHistoryEntryDto"
                  }
                }
              }
            }
          },
          "429": {
            "description": "Read rate limit exceeded (20/min).",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorDto"
                }
              }
            }
          }
        },
        "tags": [
          "Lookup"
        ],
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/lookup/batch": {
      "post": {
        "operationId": "LookupController_batchLookup",
        "summary": "Queue up to 50 lookups",
        "description": "Each item is billed and queued as its own lookup and gets its own lookupId. Results are read back one at a time with GET /v1/lookup/{id}. A malformed item is rejected up front and nothing is billed. Once accepted, items are billed and queued independently, so one item running out of credit never voids the rest.",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/BatchLookupDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/BatchResponseDto"
                }
              }
            }
          },
          "400": {
            "description": "Empty batch, more than 50 items, or a malformed item."
          },
          "429": {
            "description": "Batch rate limit exceeded."
          }
        },
        "tags": [
          "Lookup"
        ],
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/lookup/stream": {
      "get": {
        "operationId": "LookupController_streamLookup",
        "summary": "Stream a lookup",
        "description": "Server-sent events. Each platform result is pushed as it resolves, so the first results arrive in well under a second rather than after the full run. Event types: `platform_result`, `breach_result`, `web_mentions_result`, `phone_details_result`, `done`.",
        "parameters": [
          {
            "name": "query",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "email",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "phone",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "ip",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "deep",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "instant",
            "required": false,
            "in": "query",
            "schema": {
              "type": "string",
              "enum": [
                "true"
              ],
              "example": "true"
            },
            "description": "Set to `true` for a latency-bounded stream. Runs the same high-signal subset as the batch instant path and closes at the ceiling, emitting `done` with `instant`, `deadlineMs` and `platformsPending`. Results still arrive as each check resolves - the ceiling only decides when the stream ends."
          },
          {
            "name": "platforms",
            "required": true,
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": ""
          }
        },
        "tags": [
          "Lookup"
        ],
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/lookup/{id}": {
      "get": {
        "operationId": "LookupController_getLookup",
        "summary": "Get one lookup",
        "description": "Returns the full result, or `status: \"processing\"` if the job has not finished yet. A lookup belonging to another account returns 404, identically to an id that does not exist.",
        "parameters": [
          {
            "name": "id",
            "required": true,
            "in": "path",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LookupResponseDto"
                }
              }
            }
          },
          "404": {
            "description": "No lookup with that id on your account.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ApiErrorDto"
                }
              }
            }
          }
        },
        "tags": [
          "Lookup"
        ],
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/email": {
      "post": {
        "operationId": "FacetController_checkEmail",
        "summary": "Check one email address",
        "description": "Mailbox-level facts about an address: whether it is deliverable, disposable, consumer webmail, on a catch-all domain, and how old its domain is. Skips the platform sweep, so it answers in about a second. Billed as one lookup.",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/EmailCheckDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/EmailCheckResponseDto"
                }
              }
            }
          },
          "402": {
            "description": "Out of credits."
          },
          "422": {
            "description": "Invalid email address."
          }
        },
        "tags": [
          "Checks"
        ],
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/phone": {
      "post": {
        "operationId": "FacetController_checkPhone",
        "summary": "Check one phone number",
        "description": "Line-level facts about a number: validity, carrier, line type, and whether it is VOIP - including whether that VOIP line is fixed (a business line) or non-fixed (a number minted in an app). Skips the platform sweep. Billed as one lookup.",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/PhoneCheckDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/PhoneCheckResponseDto"
                }
              }
            }
          },
          "402": {
            "description": "Out of credits."
          },
          "422": {
            "description": "Invalid phone number."
          }
        },
        "tags": [
          "Checks"
        ],
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    },
    "/v1/ip": {
      "post": {
        "operationId": "FacetController_checkIp",
        "summary": "Check one IP address",
        "description": "Risk and network facts about an address: a 0-100 risk score, VPN/proxy/Tor/datacenter flags, the VPN operator where known, plus geolocation and network owner. The fastest endpoint here - one call and a local database read. Billed as one lookup.",
        "parameters": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/IpCheckDto"
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/IpCheckResponseDto"
                }
              }
            }
          },
          "402": {
            "description": "Out of credits."
          },
          "422": {
            "description": "Invalid IP address."
          }
        },
        "tags": [
          "Checks"
        ],
        "security": [
          {
            "apiKey": []
          }
        ]
      }
    }
  },
  "info": {
    "title": "Digital Footprint API (v1)",
    "description": "Given an email address, phone number, or username, find where that identity is registered across the internet, plus breach history and public web associations.\n\nEvery platform result is one of three states: registered, not registered, or omitted. A platform we could not get a confident answer for is left out of the response entirely - it is never reported as \"not registered\". Likewise `breaches` and `webMentions` are null when a source is not configured, which is different from an empty array meaning we checked and found nothing.",
    "version": "1.0",
    "contact": {}
  },
  "tags": [
    {
      "name": "Lookup",
      "description": "Identity lookups across 800+ platforms"
    }
  ],
  "servers": [
    {
      "url": "https://api.digifootprint.dev"
    }
  ],
  "components": {
    "securitySchemes": {
      "apiKey": {
        "scheme": "bearer",
        "bearerFormat": "dfp_...",
        "type": "http",
        "description": "Your API key."
      }
    },
    "schemas": {
      "PlatformSummaryDto": {
        "type": "object",
        "properties": {
          "platform": {
            "type": "string",
            "example": "github"
          },
          "supports": {
            "example": [
              "email",
              "username"
            ],
            "description": "Query types that can reach this platform. Anything else is skipped, not an error.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "instant": {
            "example": [
              "email"
            ],
            "description": "Query types for which this platform answers on the `?instant=true` path.",
            "type": "array",
            "items": {
              "type": "string"
            }
          }
        },
        "required": [
          "platform",
          "supports",
          "instant"
        ]
      },
      "PlatformListDto": {
        "type": "object",
        "properties": {
          "total": {
            "type": "number",
            "example": 1243
          },
          "platforms": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlatformSummaryDto"
            }
          }
        },
        "required": [
          "total",
          "platforms"
        ]
      },
      "PlatformResultDto": {
        "type": "object",
        "properties": {
          "platform": {
            "type": "string",
            "example": "github",
            "description": "Platform identifier."
          },
          "registered": {
            "type": "boolean",
            "example": true,
            "description": "Whether an account exists. A platform we could not confirm is omitted from `results` entirely rather than returned as false."
          },
          "method": {
            "type": "string",
            "example": "public_api",
            "enum": [
              "public_api",
              "account_enumeration",
              "breach_record",
              "domain_derived"
            ],
            "description": "How this was established."
          },
          "icon": {
            "type": "string",
            "example": "https://digifootprint.dev/icons/github.svg",
            "nullable": true
          },
          "checkedAt": {
            "type": "string",
            "example": "2026-08-26T11:40:55.902Z",
            "description": "When this fact was actually established - not when you asked. Cached results keep their original timestamp."
          },
          "mailboxProvider": {
            "type": "boolean",
            "example": true,
            "description": "Present when this account came with the email address rather than being signed up for (the mailbox provider). Excluded from socialMatches."
          }
        },
        "required": [
          "platform",
          "registered",
          "method",
          "icon",
          "checkedAt"
        ]
      },
      "BreachRecordDto": {
        "type": "object",
        "properties": {
          "name": {
            "type": "string",
            "example": "Adobe"
          },
          "date": {
            "type": "string",
            "example": "2013-10-04",
            "description": "ISO date the breach occurred."
          }
        },
        "required": [
          "name",
          "date"
        ]
      },
      "WebMentionDto": {
        "type": "object",
        "properties": {
          "title": {
            "type": "string",
            "example": "Someone (@someone) on a public profile"
          },
          "url": {
            "type": "string",
            "example": "https://example.com/profile"
          },
          "snippet": {
            "type": "string",
            "example": "Public page referencing the query."
          },
          "matchConfidence": {
            "type": "string",
            "example": "confirmed",
            "enum": [
              "confirmed",
              "unconfirmed"
            ],
            "description": "`confirmed` means the exact query string appears on the page."
          }
        },
        "required": [
          "title",
          "url",
          "snippet",
          "matchConfidence"
        ]
      },
      "PhoneDetailsDto": {
        "type": "object",
        "properties": {
          "valid": {
            "type": "boolean",
            "example": true
          },
          "carrier": {
            "type": "string",
            "example": "T-Mobile USA",
            "nullable": true
          },
          "lineType": {
            "type": "string",
            "example": "mobile",
            "nullable": true
          },
          "countryCode": {
            "type": "string",
            "example": "US",
            "nullable": true
          },
          "voipClass": {
            "type": "string",
            "enum": [
              "fixed",
              "nonFixed"
            ],
            "nullable": true,
            "example": "nonFixed",
            "description": "For a VOIP line: `fixed` is a business line tied to an address, `nonFixed` is a number anyone can create in an app. null when the line is not VOIP, or when it could not be classified that precisely."
          },
          "isVoip": {
            "type": "boolean",
            "example": true,
            "nullable": true,
            "description": "Any VOIP line. See voipClass for which kind."
          }
        },
        "required": [
          "valid",
          "carrier",
          "lineType",
          "countryCode"
        ]
      },
      "EmailDetailsDto": {
        "type": "object",
        "properties": {
          "deliverable": {
            "type": "boolean",
            "example": true,
            "nullable": true,
            "description": "null on a catch-all domain, where mailbox existence cannot be established."
          },
          "disposable": {
            "type": "boolean",
            "example": false,
            "nullable": true
          },
          "freeProvider": {
            "type": "boolean",
            "example": true,
            "nullable": true,
            "description": "Consumer webmail rather than a custom domain."
          },
          "acceptAll": {
            "type": "boolean",
            "example": false,
            "nullable": true,
            "description": "The domain accepts every address, so `deliverable` cannot be answered."
          },
          "domainAgeDays": {
            "type": "number",
            "example": null,
            "nullable": true,
            "description": "Age of a custom domain. null for consumer webmail - see domain.ageDays."
          },
          "domainRegisteredAt": {
            "type": "string",
            "example": null,
            "nullable": true
          }
        },
        "required": [
          "deliverable",
          "disposable",
          "freeProvider",
          "acceptAll",
          "domainAgeDays",
          "domainRegisteredAt"
        ]
      },
      "IpDetailsDto": {
        "type": "object",
        "properties": {
          "ip": {
            "type": "string",
            "example": "8.8.8.8"
          },
          "riskScore": {
            "type": "number",
            "example": 0,
            "nullable": true,
            "description": "0-100. null when only geo was available."
          },
          "confidence": {
            "type": "number",
            "example": 100,
            "nullable": true,
            "description": "How sure the source is of the score."
          },
          "isProxy": {
            "type": "boolean",
            "example": false,
            "nullable": true
          },
          "isVpn": {
            "type": "boolean",
            "example": false,
            "nullable": true
          },
          "isTor": {
            "type": "boolean",
            "example": false,
            "nullable": true
          },
          "isHosting": {
            "type": "boolean",
            "example": true,
            "nullable": true,
            "description": "Datacenter-hosted."
          },
          "isScraper": {
            "type": "boolean",
            "example": false,
            "nullable": true
          },
          "isCompromised": {
            "type": "boolean",
            "example": false,
            "nullable": true,
            "description": "Believed compromised (botnet/malware) - a victim, not a deliberate proxy."
          },
          "operator": {
            "type": "string",
            "example": null,
            "nullable": true,
            "description": "VPN brand where known."
          },
          "asn": {
            "type": "string",
            "example": "AS15169",
            "nullable": true
          },
          "isp": {
            "type": "string",
            "example": "Google LLC",
            "nullable": true,
            "description": "Network operator, not always the retail ISP brand."
          },
          "connectionType": {
            "type": "string",
            "example": "Hosting",
            "nullable": true,
            "description": "Business | Residential | Hosting | Wireless."
          },
          "countryCode": {
            "type": "string",
            "example": "US",
            "nullable": true
          },
          "regionName": {
            "type": "string",
            "example": "California",
            "nullable": true
          },
          "cityName": {
            "type": "string",
            "example": "Mountain View",
            "nullable": true
          },
          "latitude": {
            "type": "number",
            "example": 37.4056,
            "nullable": true
          },
          "longitude": {
            "type": "number",
            "example": -122.0775,
            "nullable": true
          },
          "timezone": {
            "type": "string",
            "example": "America/Los_Angeles",
            "nullable": true
          }
        },
        "required": [
          "ip",
          "riskScore",
          "confidence",
          "isProxy",
          "isVpn",
          "isTor",
          "isHosting",
          "isScraper",
          "isCompromised",
          "operator",
          "asn",
          "isp",
          "connectionType",
          "countryCode",
          "regionName",
          "cityName",
          "latitude",
          "longitude",
          "timezone"
        ]
      },
      "LookupResponseDto": {
        "type": "object",
        "properties": {
          "lookupId": {
            "type": "string",
            "example": "8f2c1d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f"
          },
          "query": {
            "type": "string",
            "example": "someone@example.com"
          },
          "platformsChecked": {
            "type": "number",
            "example": 844,
            "description": "How many platforms were checked."
          },
          "matches": {
            "type": "number",
            "example": 3,
            "description": "Every platform that came back registered."
          },
          "socialMatches": {
            "type": "number",
            "example": 2,
            "description": "`matches` minus accounts that came with the email address (see mailboxProvider). Usually the number you want for risk scoring."
          },
          "hits": {
            "example": [
              "google",
              "github",
              "spotify"
            ],
            "description": "The platforms in `results` that came back registered, as a flat list. Always the same length as `matches` - derived-handle hits are excluded, because those say a handle exists rather than that this person owns it.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "servedFromCache": {
            "type": "boolean",
            "example": false,
            "description": "True if any result was reused from cache."
          },
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/PlatformResultDto"
            }
          },
          "breaches": {
            "nullable": true,
            "description": "null means not checked. An empty array means checked and nothing found.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BreachRecordDto"
            }
          },
          "webMentions": {
            "nullable": true,
            "description": "null means not checked.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/WebMentionDto"
            }
          },
          "phoneDetails": {
            "nullable": true,
            "description": "Phone queries only.",
            "allOf": [
              {
                "$ref": "#/components/schemas/PhoneDetailsDto"
              }
            ]
          },
          "emailDetails": {
            "nullable": true,
            "description": "Email queries only. null means not checked.",
            "allOf": [
              {
                "$ref": "#/components/schemas/EmailDetailsDto"
              }
            ]
          },
          "ipDetails": {
            "nullable": true,
            "description": "Only when the request carried an `ip` and asked for the ip module. null means not checked.",
            "allOf": [
              {
                "$ref": "#/components/schemas/IpDetailsDto"
              }
            ]
          }
        },
        "required": [
          "lookupId",
          "query",
          "platformsChecked",
          "matches",
          "socialMatches",
          "hits",
          "servedFromCache",
          "results",
          "breaches",
          "webMentions",
          "phoneDetails",
          "emailDetails",
          "ipDetails"
        ]
      },
      "PendingLookupDto": {
        "type": "object",
        "properties": {
          "lookupId": {
            "type": "string",
            "example": "8f2c1d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f"
          },
          "status": {
            "type": "string",
            "example": "processing",
            "enum": [
              "processing"
            ]
          }
        },
        "required": [
          "lookupId",
          "status"
        ]
      },
      "LookupHistoryEntryDto": {
        "type": "object",
        "properties": {
          "id": {
            "type": "string",
            "example": "8f2c1d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f"
          },
          "query": {
            "type": "string",
            "example": "someone@example.com"
          },
          "queryType": {
            "type": "string",
            "example": "email",
            "enum": [
              "email",
              "phone",
              "username"
            ]
          },
          "platformsChecked": {
            "type": "number",
            "example": 844
          },
          "matches": {
            "type": "number",
            "example": 3
          },
          "socialMatches": {
            "type": "number",
            "example": 2
          },
          "createdAt": {
            "type": "string",
            "example": "2026-08-26T11:40:55.902Z"
          }
        },
        "required": [
          "id",
          "query",
          "queryType",
          "platformsChecked",
          "matches",
          "socialMatches",
          "createdAt"
        ]
      },
      "ApiErrorDto": {
        "type": "object",
        "properties": {
          "error": {
            "type": "object",
            "example": {
              "code": "quota_exceeded",
              "message": "You have used all 15 free lookups."
            },
            "description": "Match on `code`, never on `message` - messages get reworded, codes do not."
          }
        },
        "required": [
          "error"
        ]
      },
      "LookupRequestDto": {
        "type": "object",
        "properties": {
          "query": {
            "type": "string",
            "description": "An email address, phone number, or username - auto-detected. This is the field to use if you are looking someone up by username, since there is no separate `username` field. On v2 it also detects an IP address, and combines with the other fields: whatever it detects is filled into that field.",
            "example": "someone@example.com"
          },
          "email": {
            "type": "string",
            "description": "An email address, when you already know it is one. Skips auto-detection.",
            "example": "someone@example.com",
            "format": "email"
          },
          "phone": {
            "type": "string",
            "description": "A phone number in E.164 format, when you already know it is one.",
            "example": "+14155552671"
          },
          "platforms": {
            "example": [
              "google",
              "github"
            ],
            "description": "Check only these platforms instead of the default band. Overrides `deep`/`instant` selection - `instant` then still applies its latency ceiling. An unknown slug is a 422; a known platform that cannot be reached from the input you sent is skipped. Discover valid values from GET /v1/platforms.",
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "ip": {
            "type": "string",
            "example": "8.8.8.8",
            "description": "Optional. An IPv4 or IPv6 address to assess. Can be sent on its own or alongside an email/phone - it is scored independently of the platform sweep."
          },
          "checks": {
            "type": "object",
            "additionalProperties": true,
            "example": {
              "email": true,
              "ip": true
            },
            "description": "Optional. Which modules to run. The platform footprint runs by default; the email, phone and IP modules are opt-in: `{\"email\": true}` runs the email module for the `email` you sent. `{\"footprint\": false}` skips the platform sweep."
          }
        }
      },
      "BatchItemDto": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "example": "someone@example.com"
          },
          "phone": {
            "type": "string",
            "example": "+14155552671"
          },
          "ip": {
            "type": "string",
            "example": "8.8.8.8"
          },
          "query": {
            "type": "string",
            "example": "someone"
          },
          "checks": {
            "type": "object",
            "additionalProperties": true,
            "example": {
              "email": true
            },
            "description": "Same as `checks` on POST /v1/lookup."
          }
        }
      },
      "BatchLookupDto": {
        "type": "object",
        "properties": {
          "items": {
            "description": "Up to 50 items. Each is billed as its own lookup and gets its own lookupId, so one bad item never voids the rest.",
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BatchItemDto"
            }
          }
        },
        "required": [
          "items"
        ]
      },
      "BatchResultDto": {
        "type": "object",
        "properties": {
          "index": {
            "type": "number",
            "example": 0,
            "description": "Position in the items array you sent."
          },
          "lookupId": {
            "type": "string",
            "example": "8f2c1d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f"
          },
          "status": {
            "type": "string",
            "enum": [
              "queued",
              "error"
            ],
            "example": "queued"
          },
          "error": {
            "type": "object",
            "example": {
              "code": "insufficient_credits",
              "message": "Top up to continue."
            }
          }
        },
        "required": [
          "index",
          "status"
        ]
      },
      "BatchResponseDto": {
        "type": "object",
        "properties": {
          "results": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/BatchResultDto"
            }
          }
        },
        "required": [
          "results"
        ]
      },
      "EmailCheckDto": {
        "type": "object",
        "properties": {
          "email": {
            "type": "string",
            "example": "someone@example.com"
          }
        },
        "required": [
          "email"
        ]
      },
      "EmailCheckResponseDto": {
        "type": "object",
        "properties": {
          "lookupId": {
            "type": "string",
            "example": "8f2c1d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f"
          },
          "query": {
            "type": "string",
            "example": "someone@example.com"
          },
          "emailDetails": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/EmailDetailsDto"
              }
            ]
          }
        },
        "required": [
          "lookupId",
          "query",
          "emailDetails"
        ]
      },
      "PhoneCheckDto": {
        "type": "object",
        "properties": {
          "phone": {
            "type": "string",
            "example": "+14155552671",
            "description": "E.164 recommended."
          }
        },
        "required": [
          "phone"
        ]
      },
      "PhoneCheckResponseDto": {
        "type": "object",
        "properties": {
          "lookupId": {
            "type": "string",
            "example": "8f2c1d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f"
          },
          "query": {
            "type": "string",
            "example": "+14155552671"
          },
          "phoneDetails": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/PhoneDetailsDto"
              }
            ]
          }
        },
        "required": [
          "lookupId",
          "query",
          "phoneDetails"
        ]
      },
      "IpCheckDto": {
        "type": "object",
        "properties": {
          "ip": {
            "type": "string",
            "example": "8.8.8.8"
          }
        },
        "required": [
          "ip"
        ]
      },
      "IpCheckResponseDto": {
        "type": "object",
        "properties": {
          "lookupId": {
            "type": "string",
            "example": "8f2c1d3e-4a5b-6c7d-8e9f-0a1b2c3d4e5f"
          },
          "query": {
            "type": "string",
            "example": "8.8.8.8"
          },
          "ipDetails": {
            "nullable": true,
            "allOf": [
              {
                "$ref": "#/components/schemas/IpDetailsDto"
              }
            ]
          }
        },
        "required": [
          "lookupId",
          "query",
          "ipDetails"
        ]
      }
    }
  }
}