{
  "openapi": "3.1.0",
  "info": {
    "title": "LeadGennie API",
    "version": "1.0.0",
    "summary": "Workspace lead API and public intake endpoints for LeadGennie.",
    "description": "LeadGennie is the AI context layer for GTM teams. This API lets a client look up and capture leads in a workspace and accept public form and demo submissions. It never sends email: campaign launch and reply sending require an explicit human action in the app. All responses are JSON with an `ok` boolean; failures use the Error schema with a stable `code` and a `hint`.",
    "contact": {
      "name": "LeadGennie support",
      "email": "support@leadgennie.ai",
      "url": "https://leadgennie.com/contact"
    },
    "termsOfService": "https://leadgennie.com/terms"
  },
  "servers": [
    {
      "url": "https://leadgennie.com",
      "description": "Production"
    }
  ],
  "externalDocs": {
    "description": "Developer and agent guide",
    "url": "https://leadgennie.com/docs"
  },
  "tags": [
    {
      "name": "Leads",
      "description": "Read and capture leads in the token's workspace."
    },
    {
      "name": "Session",
      "description": "Inspect the current token."
    },
    {
      "name": "Public",
      "description": "Unauthenticated intake endpoints."
    }
  ],
  "paths": {
    "/api/extension/me": {
      "get": {
        "operationId": "getCurrentConnection",
        "tags": [
          "Session"
        ],
        "summary": "Get the current connection",
        "description": "Returns who the bearer token belongs to, the workspace, granted scopes and enabled features. Use it to test a token.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "responses": {
          "200": {
            "description": "The connection.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectionInfo"
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED — missing or invalid bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED — see the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR — retry later and quote request_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/extension/leads": {
      "get": {
        "operationId": "listLeads",
        "tags": [
          "Leads"
        ],
        "summary": "List recent leads",
        "description": "Returns the workspace's most recent leads, optionally filtered by a text query. Requires the leads:read scope.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "required": false,
            "description": "Maximum leads to return (1–50, default 20).",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 50,
              "default": 20
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "description": "Free-text filter over lead name, company and email.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Leads and the total count.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LeadList"
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED — missing or invalid bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "FORBIDDEN — token scope or workspace role does not allow this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED — see the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR — retry later and quote request_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      },
      "post": {
        "operationId": "captureLead",
        "tags": [
          "Leads"
        ],
        "summary": "Capture a lead",
        "description": "Saves a lead in the workspace. If the person is already a lead nothing is created and the existing lead is returned with created=false (HTTP 200). Requires the leads:create scope (workspace role member or higher).",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "lead"
                ],
                "properties": {
                  "lead": {
                    "$ref": "#/components/schemas/LeadDraft"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Lead already existed; nothing was created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaptureResult"
                }
              }
            }
          },
          "201": {
            "description": "Lead created.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CaptureResult"
                }
              }
            }
          },
          "400": {
            "description": "BAD_REQUEST — malformed request or JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED — missing or invalid bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "FORBIDDEN — token scope or workspace role does not allow this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "VALIDATION_ERROR — see details for the failing fields.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED — see the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR — retry later and quote request_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/extension/leads/lookup": {
      "get": {
        "operationId": "lookupLead",
        "tags": [
          "Leads"
        ],
        "summary": "Check whether a person is already a lead",
        "description": "Finds an existing lead by LinkedIn URL, email, or name and company. Read-only. Requires the leads:read scope.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "linkedin_url",
            "in": "query",
            "required": false,
            "description": "LinkedIn profile URL.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "email",
            "in": "query",
            "required": false,
            "description": "Email address.",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "name",
            "in": "query",
            "required": false,
            "description": "Full name (use with company).",
            "schema": {
              "type": "string"
            }
          },
          {
            "name": "company",
            "in": "query",
            "required": false,
            "description": "Company name (use with name).",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Lookup result.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/LeadLookup"
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED — missing or invalid bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "FORBIDDEN — token scope or workspace role does not allow this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED — see the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR — retry later and quote request_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/extension/leads/{id}": {
      "get": {
        "operationId": "getLead",
        "tags": [
          "Leads"
        ],
        "summary": "Get one lead",
        "description": "Returns a lead's current stage, research status and ICP score. Requires the leads:read scope.",
        "security": [
          {
            "bearerAuth": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "path",
            "required": true,
            "description": "Numeric lead id.",
            "schema": {
              "type": "integer",
              "minimum": 1
            }
          }
        ],
        "responses": {
          "200": {
            "description": "The lead.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "lead"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "lead": {
                      "$ref": "#/components/schemas/Lead"
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "BAD_REQUEST — malformed request or JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "401": {
            "description": "UNAUTHENTICATED — missing or invalid bearer token.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "403": {
            "description": "FORBIDDEN — token scope or workspace role does not allow this.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND — no such lead in this workspace.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "429": {
            "description": "RATE_LIMITED — see the Retry-After header.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR — retry later and quote request_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/book-demo": {
      "post": {
        "operationId": "requestDemo",
        "tags": [
          "Public"
        ],
        "summary": "Request a demo or early access",
        "description": "Public endpoint behind the website's demo form. Records the request and, when email is configured, sends a confirmation. No authentication.",
        "security": [],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "name",
                  "email",
                  "company"
                ],
                "properties": {
                  "name": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 120,
                    "description": "Requester's full name."
                  },
                  "email": {
                    "type": "string",
                    "format": "email",
                    "maxLength": 254,
                    "description": "Work email address."
                  },
                  "company": {
                    "type": "string",
                    "minLength": 1,
                    "maxLength": 160,
                    "description": "Company name."
                  },
                  "companySize": {
                    "type": "string",
                    "maxLength": 60,
                    "description": "Company size band."
                  },
                  "challenges": {
                    "type": "array",
                    "maxItems": 20,
                    "items": {
                      "type": "string",
                      "maxLength": 120
                    },
                    "description": "Outbound challenges."
                  },
                  "challengesOther": {
                    "type": "string",
                    "maxLength": 300,
                    "description": "Required when challenges includes 'Other'."
                  },
                  "crmUsed": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "CRM in use."
                  },
                  "crmOther": {
                    "type": "string",
                    "maxLength": 120,
                    "description": "Required when crmUsed is 'Other'."
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Recorded.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "confirmationSent"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "confirmationSent": {
                      "type": "boolean",
                      "description": "True only if a confirmation email was sent."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "BAD_REQUEST — malformed request or JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "VALIDATION_ERROR — see details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR — retry later and quote request_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/forms/{embedKey}/submit": {
      "post": {
        "operationId": "submitHostedForm",
        "tags": [
          "Public"
        ],
        "summary": "Submit a hosted lead form",
        "description": "Public endpoint behind embeddable forms. The submission is stored, then checked against Do Not Contact; any lead change is proposed for human approval, never applied directly. CORS is open.",
        "security": [],
        "parameters": [
          {
            "name": "embedKey",
            "in": "path",
            "required": true,
            "description": "The form's public embed key.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "fields"
                ],
                "properties": {
                  "fields": {
                    "type": "object",
                    "maxProperties": 50,
                    "additionalProperties": {
                      "type": "string",
                      "maxLength": 5000
                    },
                    "description": "Form field values keyed by field name (e.g. email)."
                  },
                  "consentGiven": {
                    "type": "boolean",
                    "description": "Whether the submitter consented."
                  },
                  "pageUrl": {
                    "type": "string",
                    "maxLength": 2000,
                    "description": "Page the form was on."
                  },
                  "utm": {
                    "type": "object",
                    "description": "UTM parameters.",
                    "properties": {
                      "source": {
                        "type": "string"
                      },
                      "medium": {
                        "type": "string"
                      },
                      "campaign": {
                        "type": "string"
                      },
                      "term": {
                        "type": "string"
                      },
                      "content": {
                        "type": "string"
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Stored.",
            "content": {
              "application/json": {
                "schema": {
                  "type": "object",
                  "required": [
                    "ok",
                    "id"
                  ],
                  "properties": {
                    "ok": {
                      "const": true
                    },
                    "id": {
                      "type": "integer",
                      "description": "Submission id."
                    }
                  }
                }
              }
            }
          },
          "400": {
            "description": "BAD_REQUEST — malformed request or JSON.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "404": {
            "description": "NOT_FOUND — unknown or inactive form.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "422": {
            "description": "VALIDATION_ERROR — see details.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          },
          "500": {
            "description": "INTERNAL_ERROR — retry later and quote request_id.",
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/Error"
                }
              }
            }
          }
        }
      }
    },
    "/api/unsubscribe": {
      "post": {
        "operationId": "unsubscribeRecipient",
        "tags": [
          "Public"
        ],
        "summary": "One-click unsubscribe (RFC 8058)",
        "description": "Suppresses an address across the workspace using the signed link from a LeadGennie email's List-Unsubscribe header. The signed values w, e and t must be copied unchanged from that link. Responds with plain text, not JSON.",
        "security": [],
        "parameters": [
          {
            "name": "w",
            "in": "query",
            "required": true,
            "description": "Workspace id from the signed link.",
            "schema": {
              "type": "integer"
            }
          },
          {
            "name": "e",
            "in": "query",
            "required": true,
            "description": "Recipient email from the signed link.",
            "schema": {
              "type": "string",
              "format": "email"
            }
          },
          {
            "name": "t",
            "in": "query",
            "required": true,
            "description": "Signature token from the signed link.",
            "schema": {
              "type": "string"
            }
          }
        ],
        "requestBody": {
          "required": false,
          "content": {
            "application/x-www-form-urlencoded": {
              "schema": {
                "type": "object",
                "properties": {
                  "List-Unsubscribe": {
                    "type": "string",
                    "const": "One-Click"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Unsubscribed.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "description": "Invalid or expired link.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "500": {
            "description": "Could not process; retry.",
            "content": {
              "text/plain": {
                "schema": {
                  "type": "string"
                }
              }
            }
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "bearerAuth": {
        "type": "http",
        "scheme": "bearer",
        "description": "Workspace API token from Dashboard → API credentials, or a session token (lgx_…) issued by the browser-extension connect flow."
      }
    },
    "schemas": {
      "Error": {
        "type": "object",
        "required": [
          "ok",
          "error",
          "code",
          "hint"
        ],
        "properties": {
          "ok": {
            "const": false
          },
          "error": {
            "type": "string",
            "description": "Human-readable message."
          },
          "code": {
            "type": "string",
            "enum": [
              "BAD_REQUEST",
              "UNAUTHENTICATED",
              "FORBIDDEN",
              "NOT_FOUND",
              "CONFLICT",
              "VALIDATION_ERROR",
              "RATE_LIMITED",
              "QUOTA_EXCEEDED",
              "PROVIDER_ERROR",
              "NOT_CONFIGURED",
              "INTERNAL_ERROR"
            ],
            "description": "Stable machine-readable code."
          },
          "hint": {
            "type": "string",
            "description": "What to do next."
          },
          "request_id": {
            "type": "string",
            "description": "Correlation id (also in the x-request-id header)."
          },
          "details": {
            "type": "array",
            "description": "Per-field problems for VALIDATION_ERROR.",
            "items": {
              "type": "object",
              "required": [
                "message"
              ],
              "properties": {
                "path": {
                  "type": "string"
                },
                "message": {
                  "type": "string"
                }
              }
            }
          }
        }
      },
      "Lead": {
        "type": "object",
        "required": [
          "id",
          "fullName",
          "emailStatus",
          "stage",
          "researchStatus",
          "createdAt"
        ],
        "properties": {
          "id": {
            "type": "integer"
          },
          "fullName": {
            "type": "string"
          },
          "jobTitle": {
            "type": [
              "string",
              "null"
            ]
          },
          "company": {
            "type": [
              "string",
              "null"
            ]
          },
          "companyDomain": {
            "type": [
              "string",
              "null"
            ]
          },
          "email": {
            "type": [
              "string",
              "null"
            ]
          },
          "emailStatus": {
            "type": "string"
          },
          "linkedinUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "stage": {
            "type": "string",
            "description": "Pipeline stage."
          },
          "researchStatus": {
            "type": "string",
            "description": "State of Gennie research for this lead ('none' if never researched)."
          },
          "icpScore": {
            "type": [
              "integer",
              "null"
            ],
            "description": "ICP fit score, null until scored."
          },
          "qualified": {
            "type": [
              "boolean",
              "null"
            ]
          },
          "createdAt": {
            "type": "string",
            "format": "date-time"
          }
        }
      },
      "LeadList": {
        "type": "object",
        "required": [
          "ok",
          "leads",
          "total"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "leads": {
            "type": "array",
            "items": {
              "$ref": "#/components/schemas/Lead"
            }
          },
          "total": {
            "type": "integer"
          }
        }
      },
      "LeadLookup": {
        "type": "object",
        "required": [
          "ok",
          "found",
          "lead"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "found": {
            "type": "boolean"
          },
          "lead": {
            "anyOf": [
              {
                "$ref": "#/components/schemas/Lead"
              },
              {
                "type": "null"
              }
            ]
          }
        }
      },
      "LeadDraft": {
        "type": "object",
        "required": [
          "full_name"
        ],
        "properties": {
          "full_name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 200
          },
          "job_title": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "company": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 200
          },
          "company_domain": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 253
          },
          "email": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 254
          },
          "phone": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 50
          },
          "linkedin_url": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 500
          },
          "source_url": {
            "type": [
              "string",
              "null"
            ],
            "maxLength": 2000
          },
          "email_guessed": {
            "type": "boolean",
            "default": false,
            "description": "True if the email is a guess rather than a verified fact."
          }
        }
      },
      "CaptureResult": {
        "type": "object",
        "required": [
          "ok",
          "created",
          "lead"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "created": {
            "type": "boolean"
          },
          "lead": {
            "$ref": "#/components/schemas/Lead"
          }
        }
      },
      "ConnectionInfo": {
        "type": "object",
        "required": [
          "ok",
          "workspace",
          "scopes"
        ],
        "properties": {
          "ok": {
            "const": true
          },
          "kind": {
            "type": "string",
            "description": "Token kind."
          },
          "user": {
            "type": [
              "object",
              "null"
            ],
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              },
              "role": {
                "type": "string"
              }
            }
          },
          "workspace": {
            "type": "object",
            "required": [
              "id",
              "name"
            ],
            "properties": {
              "id": {
                "type": "integer"
              },
              "name": {
                "type": "string"
              }
            }
          },
          "scopes": {
            "type": "array",
            "items": {
              "type": "string"
            }
          },
          "features": {
            "type": "object",
            "additionalProperties": true,
            "description": "Enabled server features."
          }
        }
      }
    }
  }
}