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

# List call logs

> Returns the team's call history, newest first, with filters, sorting and pagination. Read-only.

`page`, `limit` and `sort` are top-level query parameters. Every filter goes under `filters[...]`; a filter sent without that prefix, such as `search=Acme`, is ignored and the unfiltered history comes back. Send list filters as `filters[name][]=value`, once per value; a single value can also be sent as `filters[name]=value`. A value that does not fit its filter, such as an unknown `outcome` or a date that does not parse, fails with 400.

Date filters compare against `createdAt`, the time the call was logged, and include both ends. With `filters[search]`, results are ordered by relevance and `sort` is ignored. The `pagination` object is `{ total, page, limit, next }` when no filter is sent and `{ total, page, limit, pages }` when any filter is.

Calls from every team member are included, as are calls with `is_completed: false`, which analytics leave out. Use [Get a call log](/api-reference/endpoint/get-call-log-by-id) for one call, [List dispositions](/api-reference/endpoint/get-dispositions) and [List CRM sequences](/api-reference/endpoint/get-sequences) for filter values, and [Get the current team](/api-reference/endpoint/get-team) for user IDs. Rate limit: the heavy-reads bucket, 120 requests per minute per team (see [Rate limits](/api-reference/rate-limits)).

### How to send filters

`page`, `limit` and `sort` are top-level query parameters. Every filter is nested under
`filters[...]`. A filter sent without the prefix, such as `search=Acme`, is ignored, and the
unfiltered history comes back.

| Kind | How to send it | Example |
| - | - | - |
| Single value | `filters[name]=value` | `filters[outcome]=answered` |
| List | `filters[name][]=value`, once per value. One value can also go without `[]` | `filters[user_ids][]=64d2b3f2c4e3a6b8f2d9e1a7` |
| Boolean | `true` or `false` | `filters[has_recording]=true` |
| Date | ISO 8601 timestamp | `filters[start_date]=2026-09-01T00:00:00.000Z` |

`curl` reads square brackets in a URL as a range, so pass `--globoff`, or percent-encode them as
`%5B` and `%5D`.

### Examples

```bash theme={"system"}
# Calls logged in September 2026 (UTC)
curl --globoff 'https://client-api.salesfinity.co/v1/call-log?filters[start_date]=2026-09-01T00:00:00.000Z&filters[end_date]=2026-09-30T23:59:59.999Z' \
  --header 'x-api-key: YOUR_API_KEY'

# One rep's answered calls of a minute or longer, longest first
curl --globoff 'https://client-api.salesfinity.co/v1/call-log?filters[user_ids][]=64d2b3f2c4e3a6b8f2d9e1a7&filters[outcome]=answered&filters[min_duration]=60&sort=-duration' \
  --header 'x-api-key: YOUR_API_KEY'

# Meetings set and referrals that have a recording
curl --globoff 'https://client-api.salesfinity.co/v1/call-log?filters[disposition_ids][]=1&filters[disposition_ids][]=4&filters[has_recording]=true' \
  --header 'x-api-key: YOUR_API_KEY'

# Every call to one CRM record
curl --globoff 'https://client-api.salesfinity.co/v1/call-log?filters[crm_ids][]=00Q5f000001abcXYZ' \
  --header 'x-api-key: YOUR_API_KEY'

# Calls in one CRM sequence
curl --globoff 'https://client-api.salesfinity.co/v1/call-log?filters[sequences][]=4821' \
  --header 'x-api-key: YOUR_API_KEY'

# Free-text search, 50 per page
curl --globoff 'https://client-api.salesfinity.co/v1/call-log?filters[search]=Acme&limit=50' \
  --header 'x-api-key: YOUR_API_KEY'
```

### Gotchas

* **Dates are inclusive and filter on `createdAt`**, the time the call was logged. A date with no
  time means midnight UTC, so `filters[end_date]=2026-09-30` leaves out 30 September. Send
  `2026-09-30T23:59:59.999Z` to include the whole day.
* **`filters[exclude_negative_dispositions]` is on whenever it is present**, even as `=false`.
  Omit it to include every disposition, and do not combine it with `filters[disposition_ids]`.
* **Search sets the order.** With `filters[search]`, results come back by relevance and `sort` is
  ignored.
* **`pagination` has two shapes.** Without filters it is `{ total, page, next }`. With any filter it
  is `{ total, page, limit, pages }`. Both carry `total` and `page`, so you can always work out the
  page count as `total / limit`, rounded up.
* **Unfinished calls are included.** An attempt the rep moved on from to dial another of the
  contact's numbers, and an inbound call answered in the app that the rep has not logged yet, both
  have `is_completed: false`. Analytics leave these out; add `filters[is_completed]=true` to do the
  same.
* **Invalid IDs are dropped silently.** In `filters[user_ids][]` and `filters[contact_list_ids][]`,
  values that are not 24-character hex IDs are skipped. If none is valid, the filter is not applied
  and every call comes back.
* **`connected` and `conversation` differ from analytics.** `filters[status][]` uses simpler rules
  than the metrics of the same names on [Get analytics overview](/api-reference/endpoint/analytics-overview),
  so the counts will not match.
* **`crm_id` is filterable but not returned.** `filters[crm_ids][]` matches the contact's `crm_id`,
  which the `contact` object in the response leaves out.

### Paging through everything

This list counts toward the heavy-reads [rate limit](/api-reference/rate-limits) of 120 requests per
minute, so use the largest page size and stop on the last page:

```js theme={"system"}
// pairs, for example: [["filters[start_date]", "2026-09-01T00:00:00.000Z"], ["filters[user_ids][]", "64d2b3f2c4e3a6b8f2d9e1a7"]]
async function allCalls(pairs) {
  const calls = [];
  for (let page = 1; ; page++) {
    const params = new URLSearchParams([...pairs, ["page", String(page)], ["limit", "100"]]);
    const res = await fetch(`https://client-api.salesfinity.co/v1/call-log?${params}`, {
      headers: { "x-api-key": process.env.SALESFINITY_API_KEY },
    });
    if (!res.ok) throw new Error(`Salesfinity ${res.status}: ${await res.text()}`);
    const { data, pagination } = await res.json();
    calls.push(...data);
    if (data.length === 0 || page * 100 >= pagination.total) return calls;
  }
}
```

### `contact_list` and `source`

<Note>
  Each call log includes both `contact_list` and `source`. `contact_list` is the **dialing-queue**
  list the call was placed from (its `_id` is the queue ID, the value `filters[contact_list_ids][]`
  matches). `source` identifies the **original source list** the call came from. For CSV lists,
  `source.id` is the CSV list ID, which differs from the queue ID. `source.id` and `source.name` are
  `null` for non-CSV lists. `contact_list` is unchanged and remains fully backward compatible.
</Note>


## OpenAPI

````yaml GET /v1/call-log
openapi: 3.0.1
info:
  title: Salesfinity API
  description: >-
    The Salesfinity API is a JSON-over-HTTPS REST API for the Salesfinity
    parallel dialer.

    Use it to manage contact lists and notes, run the Sequencer, read call logs
    and AI call

    scores, pull team and SDR analytics, and enrich phone numbers and email
    addresses. Webhook

    subscriptions are managed in the Salesfinity dashboard rather than through
    this API.


    ## Base URL


    All requests go to `https://client-api.salesfinity.co`. There is no separate
    sandbox host;

    see the developer portal for the recommended way to test safely.


    ## Authentication


    Every operation requires an API key sent in the `x-api-key` request header.
    Generate a key

    in the Salesfinity dashboard under **Settings → Connections → API &
    Webhooks**. A key is scoped to a

    single team, and every response is filtered to that team.


    ```bash

    curl https://client-api.salesfinity.co/v1/team \
      --header 'x-api-key: YOUR_API_KEY'
    ```


    ## Errors


    Every error the API returns is JSON with the same four fields, so a client
    can parse a

    failure the same way on every endpoint:


    ```json

    {
      "message": "Forbidden resource",
      "error": "Forbidden",
      "statusCode": 403,
      "request_id": "0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10"
    }

    ```


    `statusCode` matches the HTTP status. `error` is the stable reason phrase to
    branch on.

    `message` is human readable, and is an array of strings when request
    validation fails.

    `request_id` identifies the request in Salesfinity's logs; include it when
    you contact

    support. Note that a missing or invalid API key returns **403**, not 401.


    ## Request IDs


    Every response carries an `X-Request-Id` header, and an error body repeats
    it as

    `request_id`. To trace a request end to end, send your own `X-Request-Id`: 1
    to 128

    characters of letters, digits, `.`, `_`, `:` and `-`. Otherwise Salesfinity
    generates one.


    ## Request bodies


    Bodies are JSON. A field the operation does not define is rejected with
    **400**

    (`property <name> should not exist`). A body may be up to 10 MB on
    `/v1/contact-lists` and

    `/v2/sequencer`, and up to 1 MB everywhere else; a larger one gets **413**.


    Writes that record a person take a `user_id`: the team member the call acts
    as. On

    contact lists and notes, a `user_id` that is not on the team gets **400**.
    On the Sequencer

    it must be an active member, or the call gets **403**.


    ## Pagination


    List endpoints accept `page` (1-based) and `limit` query parameters.
    Defaults, maximums and

    the response envelope differ by endpoint family, so read the `limit`
    parameter and the

    response schema on the operation itself. Scored calls and every paged
    `/v2/sequencer`

    list return `{ data, pagination: { total, page, limit, pages } }`. Call logs
    return that shape

    when a `filters[...]` parameter is sent, and `{ data, pagination: { total,
    page, next } }`

    otherwise.


    ## Rate limits


    Limits are per team: every API key on a team shares them. Requests are
    counted per bucket in

    fixed one-minute windows that start on the minute.


    | Bucket | Routes | Requests per minute |

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

    | Sequencer reads | `GET /v2/sequencer/*` | 600 |

    | Sequencer writes | `POST`, `PATCH`, `PUT` and `DELETE` on
    `/v2/sequencer/*` | 300 |

    | Heavy reads | `GET /v1/call-log` and `GET /v1/scored-calls` (the lists),
    `/v1/analytics/*`, `/v1/sequences` and `/v1/sequences/{id}` | 120 |

    | Everything else | All other endpoints | 1200 |


    Every response to an authenticated request carries `X-RateLimit-Limit`,

    `X-RateLimit-Remaining` and `X-RateLimit-Reset` (seconds until the window
    resets). Over the

    limit the API returns **429** with a `Retry-After` header giving the seconds
    to wait. These

    are the current limits; Salesfinity gives notice before lowering them.


    ## Idempotency


    `POST` endpoints outside the Sequencer are not idempotent: cap retries, and
    confirm state with

    a `GET` before retrying a create. Every `POST /v2/sequencer/*` endpoint
    except the three

    `/preview` endpoints accepts an `Idempotency-Key` header. Keys are kept for
    24 hours per team.

    A retry with the same key and the same request replays the original
    successful response. The

    same key with a different body or path returns **409**, and so does a retry
    while the first

    request is still running, which is safe to repeat after a moment. A request
    that fails frees

    its key, so a retry after a 5xx runs the write again: confirm state with a
    `GET` before

    retrying a create that failed with a 5xx.
  version: 1.0.0
  termsOfService: https://salesfinity.ai/terms
  contact:
    name: Salesfinity Support
    email: hello@salesfinity.co
    url: https://support.salesfinity.ai
  license:
    name: MIT
    url: https://opensource.org/licenses/MIT
servers:
  - url: https://client-api.salesfinity.co
    description: Production. The only public Salesfinity API host.
security:
  - ApiKeyAuth: []
tags:
  - name: Contact Lists
    description: >-
      Create, read, merge, and delete CSV contact lists, add or remove the
      contacts inside them, and put a list's contacts in its dialing queue.
  - name: Teams
    description: >-
      Read the team that owns the API key, including its members, licenses, and
      statuses.
  - name: Dispositions
    description: Read the default and custom call outcomes configured for the team.
  - name: Sequences
    description: >-
      Read the external CRM sequences that appear on the team's call logs, with
      call counts and first and last use. Not the Salesfinity Sequencer, which
      is the Sequencer Sequences group.
  - name: Call Logs
    description: >-
      Read the raw call history for the team, with filtering, sorting, and
      pagination.
  - name: Analytics
    description: Aggregated call metrics for the team, by contact list, and by SDR.
  - name: Snoozed Contacts
    description: Read and manage contacts that have been snoozed out of the dialing queue.
  - name: Follow-up Tasks
    description: Read follow-up tasks created from call outcomes.
  - name: Custom Fields
    description: Read the custom field mappings configured for the team.
  - name: Scored Calls
    description: Read AI-scored calls with per-facet scoring and coaching recommendations.
  - name: Notes
    description: >-
      Attach freeform notes to a person or company, list them, and pin the ones
      that matter.
  - name: Enrichment
    description: >-
      Look up phone numbers and work or personal email addresses for a LinkedIn
      profile, and check the team's remaining enrichment credits.
  - name: Sequencer Sequences
    description: >-
      Create, configure, enable and duplicate Sequencer cadences, and read their
      analytics. Distinct from the Sequences group, which lists sequence names
      observed on call logs from an external CRM.
  - name: Sequencer Steps
    description: The ordered steps that make up a cadence.
  - name: Sequencer Templates
    description: Reusable email subjects and bodies that email steps send from.
  - name: Sequencer Enrollments
    description: >-
      Contacts inside a sequence: where they are in the cadence, and the verbs
      that move them. Bulk verbs take up to 500 enrollments.
  - name: Sequencer Enrolment Runs
    description: >-
      Background enrollment of up to 5,000 contacts at a time, and the preview
      that shows what a batch would do.
  - name: Sequencer Tasks
    description: The rep task queue a cadence produces, plus standalone one-off tasks.
  - name: Sequencer Suppression
    description: >-
      The team do-not-contact list, checked when someone is enrolled and again
      before every email is sent.
  - name: Sequencer Policies
    description: 'Rules of engagement: what may be enrolled, and what happens at each step.'
  - name: Sequencer Settings
    description: Team-level sequencer settings and sending mailboxes. Read-only.
externalDocs:
  description: Salesfinity API documentation
  url: https://docs.salesfinity.ai
paths:
  /v1/call-log:
    get:
      tags:
        - Call Logs
      summary: List call logs
      description: >-
        Returns the team's call history, newest first, with filters, sorting and
        pagination. Read-only.


        `page`, `limit` and `sort` are top-level query parameters. Every filter
        goes under `filters[...]`; a filter sent without that prefix, such as
        `search=Acme`, is ignored and the unfiltered history comes back. Send
        list filters as `filters[name][]=value`, once per value; a single value
        can also be sent as `filters[name]=value`. A value that does not fit its
        filter, such as an unknown `outcome` or a date that does not parse,
        fails with 400.


        Date filters compare against `createdAt`, the time the call was logged,
        and include both ends. With `filters[search]`, results are ordered by
        relevance and `sort` is ignored. The `pagination` object is `{ total,
        page, limit, next }` when no filter is sent and `{ total, page, limit,
        pages }` when any filter is.


        Calls from every team member are included, as are calls with
        `is_completed: false`, which analytics leave out. Use [Get a call
        log](/api-reference/endpoint/get-call-log-by-id) for one call, [List
        dispositions](/api-reference/endpoint/get-dispositions) and [List CRM
        sequences](/api-reference/endpoint/get-sequences) for filter values, and
        [Get the current team](/api-reference/endpoint/get-team) for user IDs.
        Rate limit: the heavy-reads bucket, 120 requests per minute per team
        (see [Rate limits](/api-reference/rate-limits)).
      operationId: get-call-logs
      parameters:
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 100
            default: 10
          description: Items per page. Default 10. Above 100 is treated as 100.
        - name: page
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            default: 1
          description: Page number, 1-based.
        - name: sort
          in: query
          required: false
          schema:
            type: string
          description: >-
            Comma-separated field names; prefix `-` for descending. Default:
            `-createdAt`. Ignored when `filters[search]` is set.
          example: '-duration'
        - name: filters[start_date]
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: >-
            Earliest `createdAt` to include, as an ISO 8601 timestamp. A date
            with no time means midnight UTC.
          example: '2026-09-01T00:00:00.000Z'
        - name: filters[end_date]
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: >-
            Latest `createdAt` to include, as an ISO 8601 timestamp. A date with
            no time means midnight UTC, so `2026-09-30` stops at the start of
            that day; send `2026-09-30T23:59:59.999Z` to include all of it.
          example: '2026-09-30T23:59:59.999Z'
        - name: filters[outcome]
          in: query
          required: false
          schema:
            type: string
            enum:
              - answered
              - no-answer
              - cancelled
          description: Only calls that ended this way.
        - name: filters[direction]
          in: query
          required: false
          schema:
            type: string
            enum:
              - inbound
              - outbound
          description: Only outbound or only inbound calls.
        - name: filters[min_duration]
          in: query
          required: false
          schema:
            type: number
            minimum: 0
          description: Shortest call to include, in seconds, inclusive.
        - name: filters[max_duration]
          in: query
          required: false
          schema:
            type: number
            minimum: 0
          description: Longest call to include, in seconds, inclusive.
        - name: filters[has_recording]
          in: query
          required: false
          schema:
            type: boolean
          description: >-
            `true` for calls with a `recording_url`, `false` for calls without
            one.
        - name: filters[answered_by]
          in: query
          required: false
          schema:
            type: string
            enum:
              - human
              - machine_start
          description: >-
            Only calls answered by a person (`human`) or by voicemail or an
            answering machine (`machine_start`).
        - name: filters[is_completed]
          in: query
          required: false
          schema:
            type: boolean
          description: >-
            `true` leaves out calls that do not count as finished: attempts the
            rep moved on from to dial another of the contact's numbers, and
            inbound calls answered in the app that are not logged yet. `false`
            returns only those.
        - name: filters[disposition_ids]
          in: query
          required: false
          schema:
            type: array
            items:
              type: integer
          description: >-
            Disposition `id` values from [List
            dispositions](/api-reference/endpoint/get-dispositions), matched
            against `disposition.internal_id`, so a default's custom variants
            are included. Send each value as its own `filters[...][]=`
            parameter; one value can also be sent without `[]`.
          example:
            - 1
            - 4
        - name: filters[exclude_negative_dispositions]
          in: query
          required: false
          schema:
            type: boolean
          description: >-
            Returns only calls with a positive disposition: IDs 1, 4, 6, 7 and 8
            (Meeting Set, Referral, Call back later, Reach out in 6 months, Send
            an email). `false`, or leaving it out, includes every disposition.
            Do not combine it with `filters[disposition_ids]`.
        - name: filters[user_ids]
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            Reps' user `_id` values from [Get the current
            team](/api-reference/endpoint/get-team). Send each value as its own
            `filters[...][]=` parameter; one value can also be sent without
            `[]`. Values that are not 24-character hex IDs are dropped, and if
            none is valid the filter is not applied.
          example:
            - 64d2b3f2c4e3a6b8f2d9e1a7
        - name: filters[contact_list_ids]
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            List IDs, matched against `contact_list._id` (the dialing-queue ID,
            which for CSV lists differs from `source.id`). Send each value as
            its own `filters[...][]=` parameter; one value can also be sent
            without `[]`. Values that are not 24-character hex IDs are dropped,
            and if none is valid the filter is not applied.
        - name: filters[contact_ids]
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            Contact IDs, matched against `contact._id`. Send each value as its
            own `filters[...][]=` parameter; one value can also be sent without
            `[]`.
        - name: filters[crm_ids]
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            CRM record IDs, matched against the contact's `crm_id`, which the
            response does not include. Send each value as its own
            `filters[...][]=` parameter; one value can also be sent without
            `[]`.
          example:
            - 00Q5f000001abcXYZ
        - name: filters[sequences]
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            Sequence `id` values from [List CRM
            sequences](/api-reference/endpoint/get-sequences), matched exactly
            against `contact.external_relations.sequence.id`. Send each value as
            its own `filters[...][]=` parameter; one value can also be sent
            without `[]`.
        - name: filters[from]
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            Numbers the calls were placed from, matched exactly as stored in
            `from`. Send each value as its own `filters[...][]=` parameter; one
            value can also be sent without `[]`.
        - name: filters[to]
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            Numbers called, matched exactly as stored in `to`. Send each value
            as its own `filters[...][]=` parameter; one value can also be sent
            without `[]`.
        - name: filters[search]
          in: query
          required: false
          schema:
            type: string
          description: >-
            Case-insensitive search across the contact's name, company, account,
            title and email, the to/from and dialed numbers, and the call notes,
            summary and disposition, call purpose and call sentiment names. Call
            transcripts are not searched. Results are ordered by relevance.
          example: Acme
        - name: filters[ids]
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
          description: >-
            Call log `_id` values to fetch. Send each value as its own
            `filters[...][]=` parameter; one value can also be sent without
            `[]`. A value that is not a valid ID fails with 400.
        - name: filters[status]
          in: query
          required: false
          schema:
            type: array
            items:
              type: string
              enum:
                - connected
                - conversation
          description: >-
            `connected`: answered, by a person or with no answering-machine
            result, with a disposition ID below 10. `conversation`: answered, by
            a person or with no answering-machine result, and lasting at least
            60 seconds. Send both for either. These rules differ from the
            analytics metrics of the same names. Do not combine `conversation`
            with the duration filters. Send each value as its own
            `filters[...][]=` parameter; one value can also be sent without
            `[]`.
      responses:
        '200':
          description: >-
            One page of call logs, newest first unless `sort` or
            `filters[search]` says otherwise.
          headers:
            X-Request-Id:
              $ref: '#/components/headers/X-Request-Id'
            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/CallLogListResponse'
              example:
                data:
                  - _id: 6703f1c2a9e4b0a1d2c3f4a5
                    call_id: call_abc123
                    outcome: answered
                    direction: outbound
                    answered_by: human
                    duration: 184
                    to: '+14155550123'
                    from: '+14155550987'
                    disposition:
                      internal_id: 1
                      external_id: meeting_set
                      external_name: Meeting Set
                    contact:
                      _id: 66f0a1b2c3d4e5f6a7b8c9d0
                      first_name: John
                      last_name: Doe
                      email: john@acme.com
                      title: VP of Sales
                      company: Acme Corp
                      phone_numbers:
                        - type: mobile
                          number: '+14155550123'
                      linkedin: https://www.linkedin.com/in/johndoe
                      timezone: America/Los_Angeles
                      source: hubspot
                      external_relations:
                        sequence:
                          id: '4821'
                          name: Q3 Enterprise Outbound
                    contact_list:
                      _id: 66e9b8a7c6d5e4f3a2b1c0d9
                      name: Q3 Enterprise Prospects
                    source:
                      type: hubspot
                      id: null
                      name: null
                    user:
                      _id: 64d2b3f2c4e3a6b8f2d9e1a7
                      first_name: Jane
                      last_name: Smith
                      email: jane@acme.com
                    phone_number:
                      type: mobile
                      number: '+14155550123'
                    recording_url: https://recordings.example.com/call_abc123.mp3
                    notes: Booked a demo for Tuesday.
                    summary: John agreed to a 30-minute demo on Tuesday at 10am PT.
                    is_completed: true
                    started_at: '2026-09-30T17:02:11.000Z'
                    ended_at: '2026-09-30T17:05:15.000Z'
                    createdAt: '2026-09-30T17:05:20.000Z'
                    updatedAt: '2026-09-30T17:05:20.000Z'
                pagination:
                  total: 150
                  page: 1
                  limit: 10
                  pages: 15
        '403':
          $ref: '#/components/responses/Forbidden'
        '429':
          $ref: '#/components/responses/TooManyRequests'
        '500':
          $ref: '#/components/responses/InternalServerError'
components:
  headers:
    X-Request-Id:
      description: >-
        Identifies the request in Salesfinity's logs, and matches `request_id`
        in an error body. Echoes the `X-Request-Id` you sent when it is 1 to 128
        characters of letters, digits, `.`, `_`, `:` and `-`; otherwise
        Salesfinity generates one.
      schema:
        type: string
      example: 0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10
    X-RateLimit-Limit:
      description: >-
        Requests allowed per minute in the rate-limit bucket this request
        counted against. Limits are per team: every API key on the team shares
        them.
      schema:
        type: integer
      example: 300
    X-RateLimit-Remaining:
      description: Requests left in the current one-minute window for this bucket.
      schema:
        type: integer
        minimum: 0
      example: 0
    X-RateLimit-Reset:
      description: >-
        Seconds until the current window ends and the bucket resets. At least 1
        and at most 60.
      schema:
        type: integer
        minimum: 0
      example: 12
    Retry-After:
      description: >-
        Seconds to wait before retrying: the time left in the current one-minute
        window. At least 1 and at most 60.
      schema:
        type: integer
        minimum: 0
      example: 12
  schemas:
    CallLogListResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/CallLogDTO'
        pagination:
          type: object
          properties:
            total:
              type: integer
              description: Call logs matching the request.
            page:
              type: integer
              description: Current page.
            limit:
              type: integer
              description: >-
                Page size. Returned only when at least one `filters[...]`
                parameter is sent.
            pages:
              type: integer
              description: >-
                Total pages. Returned only when at least one `filters[...]`
                parameter is sent.
            next:
              type: integer
              nullable: true
              description: >-
                Next page number, or `null` on the last page. Returned only when
                no `filters[...]` parameter is sent.
          description: >-
            Paging details. Without any `filters[...]` parameter the object is
            `{ total, page, next }`; with one or more it is `{ total, page,
            limit, pages }`.
    CallLogDTO:
      type: object
      properties:
        _id:
          type: string
          description: Call log ID.
        call_id:
          type: string
          description: Identifier of the phone call this log records.
        outcome:
          type: string
          enum:
            - answered
            - no-answer
            - cancelled
          description: 'How the call ended: `answered`, `no-answer` or `cancelled`.'
        direction:
          type: string
          enum:
            - inbound
            - outbound
          description: >-
            `outbound` for a call the rep placed, `inbound` for one they
            received.
        answered_by:
          type: string
          enum:
            - human
            - machine_start
          description: >-
            Who picked up, from answering-machine detection: `human`, or
            `machine_start` for voicemail or an answering machine. May be
            absent.
        duration:
          type: number
          description: Call length in seconds.
        to:
          type: string
          description: The number called.
        from:
          type: string
          description: The number the call was placed from.
        disposition:
          type: object
          properties:
            internal_id:
              type: integer
              description: >-
                Disposition `id`. The value `filters[disposition_ids][]`
                matches.
            external_id:
              type: string
              description: Identifier stored with the disposition.
            external_name:
              type: string
              description: Label stored with the disposition.
          description: >-
            The outcome the rep logged. `internal_id` is the Salesfinity
            disposition `id` from [List
            dispositions](/api-reference/endpoint/get-dispositions); custom
            dispositions carry the ID of the default they are based on.
        contact:
          type: object
          description: >-
            The contact as it was when the call was logged. `crm_id` is not
            returned, but `filters[crm_ids][]` still matches it.
            `external_relations` holds the record's links in the platform it
            came from, such as `sequence.id` and `sequence.name`, which
            `filters[sequences][]` matches.
          properties:
            _id:
              type: string
              description: Contact ID. The value `filters[contact_ids][]` matches.
            first_name:
              type: string
              description: First name.
            last_name:
              type: string
              description: Last name.
            email:
              type: string
              description: Email address.
            title:
              type: string
              description: Job title.
            company:
              type: string
              description: Company name.
            phone_numbers:
              type: array
              description: The contact's phone numbers.
              items:
                type: object
                properties:
                  type:
                    type: string
                    description: '`mobile`, `direct` or `office`.'
                  number:
                    type: string
                    description: Phone number.
                  country_code:
                    type: string
                    description: Country code, when stored.
                  extension:
                    type: string
                    description: Extension, when stored.
            linkedin:
              type: string
              description: LinkedIn profile URL.
            website:
              type: string
              description: Company website.
            timezone:
              type: string
              description: IANA time zone, for example `America/New_York`.
            source:
              type: string
              description: >-
                Integration the contact came from, for example `csv`, `hubspot`
                or `salesforce`.
            url:
              type: string
              description: Link to the record in the platform it came from.
            external_relations:
              type: object
              additionalProperties: true
              description: >-
                Links to the record in its source platform, such as `contact`,
                `task` and `sequence`. The keys depend on the platform.
        contact_list:
          type: object
          description: >-
            The dialing-queue list (ContactList) the call was placed from. Its
            `_id` is the queue ID; for CSV lists that differs from `source.id`.
            Unchanged for backward compatibility. Absent, or `null`, when the
            call has no list or the list was deleted.
          properties:
            _id:
              type: string
              description: List ID. The value `filters[contact_list_ids][]` matches.
            name:
              type: string
              description: List name.
        source:
          type: object
          description: >-
            The original source list the call came from. Additive;
            `contact_list` is left untouched. `type` falls back to the contact's
            integration when the list is not a CSV list.
          properties:
            type:
              type: string
              description: >-
                Integration source type (e.g. csv, hubspot, salesforce). Null if
                unknown.
              nullable: true
            id:
              type: string
              description: >-
                The original source list id. For CSV lists this is the CSV list
                id (CsvContacts._id). Null for non-CSV lists (no external list
                id is stored).
              nullable: true
            name:
              type: string
              description: The source list name. Null when unresolved.
              nullable: true
        user:
          $ref: '#/components/schemas/UserDTO'
        phone_number:
          type: object
          description: >-
            The contact number that was dialed, with its type. Analytics
            `data_quality` counts calls to `mobile` and `direct` numbers.
          properties:
            type:
              type: string
              description: '`mobile`, `direct` or `office`.'
            number:
              type: string
              description: Phone number.
        recording_url:
          type: string
          description: Link to the call recording. Absent when the call was not recorded.
        notes:
          type: string
          description: Notes the rep logged.
        transcription:
          type: string
          description: Plain-text transcript, when the call was transcribed.
        summary:
          type: string
          description: AI summary of the call, when one was generated.
        is_completed:
          type: boolean
          description: >-
            `false` marks a call that does not count as finished: an attempt the
            rep moved on from to dial another of the contact's numbers, or an
            inbound call answered in the app that the rep has not logged yet.
            Analytics count only calls where this is not `false`.
        started_at:
          type: string
          format: date-time
          description: When the call started.
        ended_at:
          type: string
          format: date-time
          description: When the call ended.
        createdAt:
          type: string
          format: date-time
          description: >-
            When the call was logged. Date filters and the default sort use this
            field.
        updatedAt:
          type: string
          format: date-time
          description: When the log last changed.
    Error:
      type: object
      title: Error
      description: >-
        The error envelope returned by every Salesfinity API error response. The
        shape is identical across all status codes and all endpoints, and
        `request_id` is present on every error the API returns.
      required:
        - message
        - error
        - statusCode
        - request_id
      properties:
        message:
          description: >-
            Human-readable description of the failure. A string for most errors,
            and an array of strings when request validation fails, with one
            entry per field that did not validate.
          oneOf:
            - type: string
            - type: array
              items:
                type: string
          example: Forbidden resource
        error:
          type: string
          description: >-
            Stable HTTP reason phrase for the status code, such as `Forbidden`.
            One exception: a 403 from a `/v2/sequencer` write whose `user_id`
            has no active plan or seat carries `NO_ACTIVE_PLAN` here. Branch on
            this rather than on `message`.
          example: Forbidden
        statusCode:
          type: integer
          format: int32
          description: >-
            HTTP status code, repeated in the body so it survives transports
            that drop it.
          example: 403
        request_id:
          type: string
          description: >-
            Identifies the request in Salesfinity's logs; the same value as the
            `X-Request-Id` response header. Include it when you contact support.
          example: 0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10
        errors:
          type: array
          items:
            type: string
          description: >-
            Present on a few Sequencer errors, such as a rules-of-engagement
            policy whose rules fail the catalog check: one entry per problem.
            Other fields an upstream service sends are passed through unchanged
            too.
      example:
        message: Forbidden resource
        error: Forbidden
        statusCode: 403
        request_id: 0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10
    UserDTO:
      type: object
      properties:
        _id:
          type: string
        email:
          type: string
        first_name:
          type: string
        last_name:
          type: string
      required:
        - _id
        - email
        - first_name
        - last_name
  responses:
    Forbidden:
      description: >-
        Forbidden. The `x-api-key` header is missing, malformed, revoked, or
        belongs to another team. Send a valid key in the `x-api-key` header.
        Salesfinity returns 403 (not 401) for a missing, malformed, revoked, or
        wrong-team key. On an operation that takes a `user_id`, 403 can also
        mean that team member may not do this, for example a Sequencer call
        acting as an inactive member; `message` then says why. A `/v2/sequencer`
        write whose `user_id` has no active plan or seat gets `error` set to
        `NO_ACTIVE_PLAN` instead of `Forbidden`; assign the member a seat.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Forbidden resource
            error: Forbidden
            statusCode: 403
            request_id: 0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10
    TooManyRequests:
      description: >-
        Too Many Requests. The team has used its per-minute budget for this
        rate-limit bucket; the limit is shared by every API key on the team.
        Wait the number of seconds in the `Retry-After` header (never more than
        60), then retry. The request was refused before it ran, so retrying it
        is safe even for a write. Pace bulk work with the
        `X-RateLimit-Remaining` and `X-RateLimit-Reset` headers. See
        /api-reference/rate-limits.
      headers:
        Retry-After:
          $ref: '#/components/headers/Retry-After'
        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'
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Rate limit of 300 requests per minute exceeded. Retry in 12s.
            error: Too Many Requests
            statusCode: 429
            request_id: 0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10
    InternalServerError:
      description: >-
        Internal Server Error. Something went wrong on the Salesfinity side.
        Retry with exponential backoff. If it persists, contact
        hello@salesfinity.co with the `request_id` from the body, the request
        path and the timestamp. The contact-list, snoozed-contact and note
        routes do not validate an ID in the path, and the analytics routes do
        not validate `user_ids`: an ID that is not a 24-character hexadecimal
        string returns 500 there, and retrying does not help, so check the ID
        first.
      headers:
        X-Request-Id:
          $ref: '#/components/headers/X-Request-Id'
      content:
        application/json:
          schema:
            $ref: '#/components/schemas/Error'
          example:
            message: Internal server error
            error: Internal Server Error
            statusCode: 500
            request_id: 0f6d3c52-9f3e-4c1a-a4b8-2b1f7e5d9c10
  securitySchemes:
    ApiKeyAuth:
      type: apiKey
      in: header
      name: x-api-key
      description: >-
        Team-scoped API key generated in the Salesfinity dashboard under
        Settings → Connections → API & Webhooks. A missing or invalid key
        returns HTTP 403.

````

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