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

# List leads

> Lists the leads of the workspace, most recent first (`created_at`, then `id`). Leads come from the sources `widget`, `lead_ads`, `telephony`, `social` and `crm` (see `LeadSource`). A lead whose agent was deleted is not returned. The `lead.created` webhook event sends the same objects when leads are created.




## OpenAPI

````yaml /openapi/openapi-v1.yaml get /leads
openapi: 3.0.3
info:
  title: Agentova Public API
  version: 1.0.0
  termsOfService: https://agentova.ai/legal/terms-of-use
  license:
    name: Proprietary — Agentova terms of use
    url: https://agentova.ai/legal/terms-of-use
  contact:
    name: Agentova support
    email: contact@agentova.ai
    url: https://docs.agentova.ai
  description: >
    The Agentova Public API lets your own code, Zapier or n8n work with an

    Agentova workspace: read its automations, runs and leads, pause and resume

    its automations, and receive events through signed webhooks.



    ## Base URL and authentication


    All endpoints are served over HTTPS under `https://core-api.agentova.ai/v1`.

    Every request carries an API key, created in the Agentova app

    (Settings → API), in the `Authorization` header:

    `Authorization: Bearer agk_live_…`


    - A key belongs to one workspace and gives access to the whole workspace.
      The key identifies the workspace: no workspace identifier is ever sent.
    - Every key starts with `agk_live_`. There are no test keys and no separate
      test environment: to experiment safely, use a dedicated workspace.
    - A revoked key is refused immediately, on every endpoint.

    - The API is meant to be called from a server. It sends no CORS headers,
      so a web page cannot call it from a browser, and an `OPTIONS` request
      receives an error in the format described under Errors.


    ## Stability


    This is version 1.0.0 of the v1 API, and v1 is stable: no breaking change

    will be made under `/v1`. Additive changes can still happen: new optional

    fields, new event types, new enum values. Your client must ignore the

    fields, event types and enum values it does not know.



    ## Coverage


    - **Automations**: the messaging automations of the workspace on Meta
      networks (`facebook`, `instagram`, `instagram_page`), WhatsApp
      (`whatsapp`) and e-mail (`gmail`, `microsoft_outlook`, `imap`), with the
      types `social_comments`, `social_story`, `social_messages`, `lead_ads`
      and `email_messages`, plus the CRM sources (`crm_source`). They can all
      be read, and paused or resumed with `PATCH`.
    - **Leads**: from the sources `widget`, `lead_ads`, `telephony`, `social`
      and `crm`.
    - **Runs**: see the `Run` schema for the automations that produce runs.

    - **Webhook events**: `lead.created`, `run.completed` and
      `automation.status_changed`.


    ## Conventions


    - **Identifiers** are opaque strings prefixed by their type: `aut_`
      (automation), `agt_` (agent), `run_` (run), `lea_` (lead), `whk_`
      (webhook subscription), `evt_` (event). Store them and send them back
      unchanged; never validate or parse them (length, alphabet). They are not
      UUIDs.
    - **Dates** are ISO 8601, in UTC, with the `Z` suffix.

    - **Unknown query parameters** are ignored, never rejected, so that a
      client written for a later version keeps working.
    - **Request bodies** are JSON, sent with `Content-Type: application/json`,
      and limited to 16 KB. Another content type is refused with HTTP `415`,
      a larger body with HTTP `413`, both with the code `invalid_request`.
    - **Caching**: every response carries `Cache-Control: no-store`, because
      responses contain personal data.


    ## Pagination


    Lists are paginated with an opaque cursor. `limit` takes a value from 1 to

    100 (25 by default); a value out of range is refused with `400`, not

    corrected. A page looks like

    `{ "data": [...], "has_more": true, "next_cursor": "..." }`: to get the

    next page, send `next_cursor` back as `cursor`. A missing or empty

    `cursor`, or the literal word `null`, means the first page.

    `GET /webhooks` is not paginated.



    ## Rate limits


    - Each API key can make about 100 requests per minute. Requests are
      counted per server instance, so this limit is approximate.
    - Responses carry `X-RateLimit-Limit`, `X-RateLimit-Remaining` and
      `X-RateLimit-Reset` (seconds until the window resets, not a timestamp),
      `401` and `403` responses included.
    - Above the limit, the API answers `429 rate_limited` with a `Retry-After`
      header, in seconds.
    - More than 30 authentication failures per minute from the same IP address
      also lead to `429`. This never applies to requests made with a valid
      key.


    ## Errors


    Every 4xx and 5xx response has the same body:

    `{ "error": { "code": "...", "message": "...", "details": {} } }`.


    - `code` is stable and comes from a closed list (`ErrorCode`): branch on
      it.
    - `message` is in English, meant to be shown to a person, and can change
      at any time.
    - `details` is always an object: empty, or giving context such as the
      faulty `parameter` and a `reason`.

    | HTTP status | `code` | Meaning |

    |---|---|---|

    | 400 | `invalid_request` | Invalid query parameter or body
    (`details.parameter`) |

    | 413, 415 | `invalid_request` | Body larger than 16 KB, or unsupported
    content type (`details.reason`) |

    | 401 | `invalid_api_key` | Missing, malformed, unknown or revoked key |

    | 403 | `workspace_access_denied` | The workspace's subscription does not
    allow API access |

    | 404 | `automation_not_found` | No such automation in this workspace |

    | 404 | `webhook_not_found` | No such webhook subscription in this workspace
    |

    | 404 | `route_not_found` | Unknown path or method under `/v1` (never `405`)
    |

    | 422 | `automation_not_controllable` | `PATCH` on an automation in `draft`
    or `error` (`details.status`), or resuming an e-mail automation whose
    mailbox another active agent already uses (`details.reason: account_in_use`)
    |

    | 429 | `rate_limited` | Rate limit exceeded: wait `Retry-After` seconds |

    | 500 | `internal_error` | Unexpected error (generic message) |

    | 503 | `service_unavailable` | The API is temporarily switched off by
    Agentova: retry later |



    ## Webhooks


    Create a subscription with `POST /webhooks`. Agentova then sends every

    event of the subscribed types as a `POST` request to your URL; the body is

    described by the `delivery` callback of `POST /webhooks`.


    - **Request**: a `POST` with `Content-Type: application/json` and
      `User-Agent: Agentova-Webhooks/1.0`.
    - **Signature**: each delivery carries
      `X-Agentova-Signature: t=<unix timestamp>,v1=<hex digest>`. The digest
      is the HMAC-SHA256 of the string `{t}.{raw request body}`, keyed with the
      complete subscription `secret`, `whsec_` prefix included. Compare digests
      in constant time, and reject a delivery whose `t` is more than 5 minutes
      away from your clock (recommended tolerance). The official Zapier app
      accepts 1 hour, because Zapier can process hooks late and the hook URL
      is itself a secret.
    - **Success** is a 2xx status received within 10 seconds (a deadline for
      the whole response). The response body is ignored. Redirects are not
      followed: a 3xx is a failure.
    - **Retries**: 8 attempts spread over about 27 hours 35 minutes. The first
      attempt is immediate; after each failure, the next one comes 5 seconds,
      5 minutes, 30 minutes, 2 hours, 5 hours, 10 hours and finally 10 hours
      later. When your endpoint answers `429` or `503` with a `Retry-After`
      header, the next attempt waits at least that long, up to 12 hours. No
      attempt is ever scheduled more than 36 hours after the event was
      created: when the next one would be, the delivery is abandoned.
    - **`410 Gone`** deletes the subscription immediately, without any
      notification: answer `410` to unsubscribe (this is how Zapier reports a
      deleted Zap).
    - **Disabling**: a delivery is abandoned after its 8th failed attempt, or
      earlier when it reaches the 36-hour limit. The
      subscription is then disabled if it has at least 3 abandoned deliveries
      in a row and has been failing without interruption for at least 72
      hours. It is also disabled at once, without any delivery, if its URL no
      longer passes the address checks (an IP address that became forbidden).
      Workspace admins are notified in the app and by push
      notification. A disabled subscription receives nothing and cannot be
      re-enabled: delete it and create a new one (Zapier: turn the Zap off and
      on again; n8n: deactivate and reactivate the workflow). Disabled
      subscriptions are deleted automatically 30 days after being disabled.
    - **At least once, in no guaranteed order**: the same event can be
      delivered more than once, and events can arrive out of order.
      Deduplicate on the event `id` (`evt_…`): it is the same on every attempt
      and for every subscription that receives the event; keep the ids you
      have seen for at least 48 hours: retries are always scheduled within 36
      hours of the event's creation. To order events,
      use the timestamp of the resource in `data` (`updated_at` of an
      automation, `created_at` of a lead, `occurred_at` of a run), not the
      arrival order.
    - **Ownership**: subscriptions belong to the workspace; workspace admins
      see and delete them in Settings → API. Revoking the key that created a
      subscription deletes the subscription as well, unless the admin unticks
      that option in Settings → API. A key is also revoked, with its
      subscriptions, when the admin who created it leaves the workspace,
      loses the admin role or deletes their account.
    - **Retention**: the webhook delivery log is kept for 30 days, and runs
      for 90 days.
    - **While the API is switched off** (`503 service_unavailable`), no
      delivery is created for the events that happen in the meantime, and the
      deliveries already in progress are suspended: they resume when the API
      is back. Catch up on the missed events with the list endpoints.


    ## Support


    Documentation: https://docs.agentova.ai. Contact: contact@agentova.ai.
servers:
  - url: https://core-api.agentova.ai/v1
    description: Production, the only environment (there is no separate test environment)
security:
  - bearerAuth: []
paths:
  /leads:
    get:
      summary: List leads
      description: >
        Lists the leads of the workspace, most recent first (`created_at`, then
        `id`). Leads come from the sources `widget`, `lead_ads`, `telephony`,
        `social` and `crm` (see `LeadSource`). A lead whose agent was deleted is
        not returned. The `lead.created` webhook event sends the same objects
        when leads are created.
      operationId: listLeads
      parameters:
        - name: source
          in: query
          description: >
            Only the leads from this source. Any other value is refused with
            `400 invalid_request`.
          schema:
            $ref: '#/components/schemas/LeadSource'
        - name: agent_id
          in: query
          description: >
            Only the leads of this agent, given as an opaque `agt_…` identifier.
            A value that does not look like an identifier issued by the API
            returns an empty page.
          schema:
            type: string
        - $ref: '#/components/parameters/Limit'
        - $ref: '#/components/parameters/Cursor'
      responses:
        '200':
          description: A page of leads
          headers:
            Cache-Control:
              $ref: '#/components/headers/Cache-Control'
            X-RateLimit-Limit:
              $ref: '#/components/headers/X-RateLimit-Limit'
            X-RateLimit-Remaining:
              $ref: '#/components/headers/X-RateLimit-Remaining'
            X-RateLimit-Reset:
              $ref: '#/components/headers/X-RateLimit-Reset'
          content:
            application/json:
              schema:
                allOf:
                  - $ref: '#/components/schemas/PaginatedResponse'
                  - type: object
                    required:
                      - data
                    properties:
                      data:
                        type: array
                        items:
                          $ref: '#/components/schemas/Lead'
              example:
                data:
                  - id: lea_01HZX5M2N3P4Q5R6S7T8V9W0X1
                    source: lead_ads
                    agent_id: agt_01HZX3Q8K5N7P2R4S6T8V0W2Y5
                    name: Jane Doe
                    email: jane.doe@example.com
                    phone: '+33612345678'
                    custom_fields:
                      budget: 5000-10000
                    created_at: '2026-08-20T09:20:00Z'
                has_more: false
                next_cursor: null
        '400':
          $ref: '#/components/responses/BadRequest'
        '401':
          $ref: '#/components/responses/Unauthorized'
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/RateLimited'
        '503':
          $ref: '#/components/responses/ServiceUnavailable'
components:
  schemas:
    LeadSource:
      type: string
      description: |
        - `widget`: the chat widget of a website, or a lead entered manually in
          the Agentova app.
        - `lead_ads`: a Meta Lead Ads form.
        - `telephony`: a phone call handled by an Agentova telephony agent.
        - `social`: an Instagram or Facebook prospect who commented a post and
          then sent their contact details (email or phone) in a private
          message to the comment automation of an Agentova agent.
        - `crm`: an incoming contact of a connected CRM (HubSpot, Monday,
          Customer.io…) for which an automation of an Agentova agent ran.
      enum:
        - widget
        - social
        - telephony
        - lead_ads
        - crm
    PaginatedResponse:
      type: object
      required:
        - has_more
        - next_cursor
      properties:
        has_more:
          type: boolean
          description: Whether another page follows.
        next_cursor:
          type: string
          nullable: true
          description: >
            Opaque cursor of the next page, to send back as `cursor`; `null`
            when there is no next page.
    Lead:
      type: object
      description: A contact captured by one of the workspace's agents.
      required:
        - id
        - source
        - agent_id
        - name
        - email
        - phone
        - custom_fields
        - created_at
      properties:
        id:
          type: string
          description: Opaque identifier prefixed with `lea_`.
        source:
          $ref: '#/components/schemas/LeadSource'
        agent_id:
          type: string
          description: >
            Opaque identifier, prefixed with `agt_`, of the agent that captured
            the lead (telephony agents included).
        name:
          type: string
          nullable: true
          description: Name as captured, or `null`.
        email:
          type: string
          nullable: true
          description: E-mail address as captured, or `null`.
        phone:
          type: string
          nullable: true
          description: >
            Phone number exactly as entered or captured, or `null`. It is not
            normalized: its format varies from one source to another.
        custom_fields:
          type: object
          description: >
            Additional data captured with the lead (for example the answers to a
            Lead Ads form), as key-value pairs. Keys that start with `_` are
            internal to Agentova and are never returned.
        created_at:
          type: string
          format: date-time
          description: When the lead was created in Agentova.
    ErrorResponse:
      type: object
      required:
        - error
      properties:
        error:
          type: object
          required:
            - code
            - message
            - details
          properties:
            code:
              $ref: '#/components/schemas/ErrorCode'
            message:
              type: string
              description: >
                In English, meant to be displayed; it can change without notice
                (branch on `code`).
            details:
              type: object
              description: >
                Empty (`{}`) or context: `parameter`, `allowed`, `max`,
                `reason`, `status`, `retry_after_seconds`…
    ErrorCode:
      type: string
      description: >
        Stable error codes. `invalid_request` (400; also 413 when the body is
        larger than 16 KB and 415 for an unsupported content type: the HTTP
        status is kept and the cause is in `details.reason`) · `invalid_api_key`
        (401) · `workspace_access_denied` (403) · `automation_not_found` (404) ·
        `webhook_not_found` (404) · `route_not_found` (404: unknown path or
        method) · `automation_not_controllable` (422: automation in `draft` or
        `error`, see `details.status`) · `rate_limited` (429) · `internal_error`
        (500, generic message) · `service_unavailable` (503: the API is
        temporarily switched off by Agentova; retry later).
      enum:
        - invalid_request
        - invalid_api_key
        - workspace_access_denied
        - automation_not_found
        - webhook_not_found
        - route_not_found
        - automation_not_controllable
        - rate_limited
        - internal_error
        - service_unavailable
  parameters:
    Limit:
      name: limit
      in: query
      description: >
        Integer from 1 to 100 (default 25). Out of range or not an integer: `400
        invalid_request`.
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 25
    Cursor:
      name: cursor
      in: query
      description: >
        Opaque cursor received in `next_cursor`. Missing, empty or equal to the
        literal word `null`: first page. Unreadable: `400 invalid_request`.
      schema:
        type: string
        nullable: true
  headers:
    Cache-Control:
      description: Always `no-store` (personal data, no intermediate cache)
      schema:
        type: string
        enum:
          - no-store
    X-RateLimit-Limit:
      description: >
        Number of requests allowed per window (per key; per IP address when the
        429 comes from repeated authentication failures)
      schema:
        type: integer
    X-RateLimit-Remaining:
      description: Requests left in the current window
      schema:
        type: integer
    X-RateLimit-Reset:
      description: Seconds left before the window resets (NOT a timestamp)
      schema:
        type: integer
  responses:
    BadRequest:
      description: Invalid query parameter or body (`details.parameter` names it)
      headers:
        Cache-Control:
          $ref: '#/components/headers/Cache-Control'
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: invalid_request
              message: The limit parameter must be an integer between 1 and 100
              details:
                parameter: limit
                max: 100
    Unauthorized:
      description: >-
        Missing, malformed, unknown or revoked key (deliberately
        indistinguishable)
      headers:
        Cache-Control:
          $ref: '#/components/headers/Cache-Control'
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: invalid_api_key
              message: Invalid or revoked key — create one in Settings → API
              details: {}
    Forbidden:
      description: >
        Valid key, but the workspace does not have access to the service (or no
        longer has it): its subscription is blocked (unpaid, paused, or initial
        payment never completed). A workspace without a subscription is on the
        free plan and is not affected. This is not a per-key permission: v1 has
        no per-key permissions.
      headers:
        Cache-Control:
          $ref: '#/components/headers/Cache-Control'
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: workspace_access_denied
              message: This workspace's subscription does not allow access to the API
              details: {}
    RateLimited:
      description: >
        Rate limit exceeded: too many requests with this key, or too many
        authentication failures from this IP address
      headers:
        Cache-Control:
          $ref: '#/components/headers/Cache-Control'
        Retry-After:
          description: Seconds to wait before retrying
          schema:
            type: integer
        X-RateLimit-Limit:
          $ref: '#/components/headers/X-RateLimit-Limit'
        X-RateLimit-Remaining:
          $ref: '#/components/headers/X-RateLimit-Remaining'
        X-RateLimit-Reset:
          $ref: '#/components/headers/X-RateLimit-Reset'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: rate_limited
              message: Rate limit exceeded — retry in a few seconds
              details:
                retry_after_seconds: 42
    ServiceUnavailable:
      description: >
        The API is temporarily switched off by Agentova (`service_unavailable`):
        retry later. This response may come without the `X-RateLimit-*` headers.
        While the API is switched off, no webhook delivery is created for the
        events that happen in the meantime.
      headers:
        Cache-Control:
          $ref: '#/components/headers/Cache-Control'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/ErrorResponse'
          example:
            error:
              code: service_unavailable
              message: The API is temporarily unavailable — retry later
              details: {}
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: agk_live_<32 characters>
      description: >
        API key created in the Agentova app (Settings → API), sent as
        `Authorization: Bearer agk_live_…`. One key = one workspace: the key
        determines the workspace, and no workspace identifier ever travels in
        requests. The `Bearer` scheme is case-insensitive. A revoked key is
        refused immediately.

````

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