---
title: "Poll a dedupe scan or merge job (groups / totals when completed)"
method: GET
path: "/business/contacts/dedupe-jobs/{jobId}"
tags: ["Contacts"]
---

# Poll a dedupe scan or merge job (groups / totals when completed)

`GET /business/contacts/dedupe-jobs/{jobId}`

## Request

- Base URL: `https://api.tryfuse.ai/api/v1`
- URL: `https://api.tryfuse.ai/api/v1/business/contacts/dedupe-jobs/{jobId}`
- Auth: HTTP basic

Polls a dedupe job started by `POST /business/contacts/dedupe-scan` or `POST /business/contacts/dedupe-merge`; `kind` says which of the two the id names. `status` moves pending > processing > completed or failed; poll every few seconds until it is terminal. On a completed scan, `scanResult` carries one PAGE of the duplicate groups (`pageNum`/`limit`, default 50 per page, largest groups first; `scanResult.pagination` says how many pages there are — page through once `status` is completed rather than re-fetching every group on every poll). Each group carries its `contactIds` (every member), `matchedValues` (the normalized values it matched on), the `lists` its members live in, a preview of up to 25 member `contacts`, and a `mergePreview` of the contact the group would merge into — its surviving `winnerContactId`, filled fields, and the combined `emails`/`phones` unions (first 10 entries each; `emailCount`/`phoneCount` are the full totals). Each group's `groupId` is what `POST /business/contacts/dedupe-merge` selects by. `totalGroups` counts every group the scan FOUND and can exceed the stored 200 (`truncated` says so); `pagination.totalRecords` counts the stored, pageable groups. On a completed merge, `mergeResult` carries `mergedGroups`, `archivedContacts` and `skippedGroups` (groups that no longer had two live members when the merge ran, or whose members' identities conflict). On `failed`, `error` is `DEDUPE_FAILED` — start a new job. Scan results expire with the job (about 24 hours); export jobs are not served here (poll GET /business/exports/{jobId}). Serves only the token owner's own jobs. Rate limit: 120 requests per minute, 10,000 per day on the contacts bucket, which every /business/contacts endpoint shares.

Errors:
- `404` `JOB_NOT_FOUND` — No dedupe job with that id (it may have expired, belong to another workspace, or be an export job).
- `422` `VALIDATION_FAILED` — jobId is not a 24-hex id.
- `429` `RATE_LIMITED` — More than 120 requests per minute or 10,000 per day on the contacts bucket, which every /business/contacts endpoint shares; retry after Retry-After.

Requires one of the following token scopes: contacts.

## Path parameters

- `jobId` string, required: 24-character hex id of an async job, as returned with the 202 that started it (POST /business/lists/{listId}/export).

## Query parameters

- `pageNum` integer: 1-based page number over the scan's stored groups (default 1); groups are ordered largest first.
- `limit` integer: Page size: how many duplicate groups to return per page (1-200, default 50). A scan stores at most 200 groups, so limit=200 returns the whole result in one page.

## Response `200`

Success

- object
  - `dedupeJob` object, required
    - `jobId` string, required
    - `kind` 'scan' | 'merge', required
    - `status` 'pending' | 'processing' | 'completed' | 'failed', required
    - `scanResult` object, nullable, required: Set only when a SCAN job is completed; null otherwise.
      - `matchFields` string[]
      - `totalGroups` integer
      - `totalDuplicateContacts` integer
      - `totalContactsScanned` integer
      - `truncated` boolean: True when more than 200 groups were found and only the first 200 are stored.
      - `skippedOversizedGroups` integer: Groups skipped for exceeding 50 contacts (almost certainly a too-weak field selection).
      - `pagination` object: Paging over the STORED groups (at most 200). totalRecords counts pageable groups; totalGroups above counts every group found.
        - `pageNum` integer
        - `totalPages` integer
        - `totalRecords` integer
      - `groups` object[]
        - `groupId` string: Stable 16-hex id — what POST /business/contacts/dedupe-merge selects by.
        - `matchedValues` object: The normalized values the group matched on, keyed by match field.
        - `groupSize` integer
        - `contactIds` string[]
        - `lists` object[]: Lists the members live in (first 10, alphabetical); listCount is the full total.
          - `id` string
          - `name` string
        - `listCount` integer
        - `mergePreview` object: What the surviving contact will look like after the merge.
          - `winnerContactId` string
          - `firstName` string, nullable
          - `lastName` string, nullable
          - `jobTitle` string, nullable
          - `linkedinUrl` string, nullable
          - `industry` string, nullable
          - `kind` 'canonical' | 'custom'
          - `companyName` string, nullable
          - `companyDomain` string, nullable
          - `location` string, nullable
          - `primaryEmail` string, nullable
          - `emailCount` integer
          - `emails` object[]
            - `email` string
            - `status` string, nullable
            - `isPersonal` boolean
          - `primaryPhone` string, nullable
          - `phoneCount` integer
          - `phones` object[]
            - `phoneNumber` string
            - `status` string, nullable
        - `contacts` object[]: Preview of the first 25 members (contactIds carries every member).
          - `contactId` string
          - `firstName` string, nullable
          - `lastName` string, nullable
          - `jobTitle` string, nullable
          - `linkedinUrl` string, nullable
          - `kind` 'canonical' | 'custom'
          - `companyName` string, nullable
          - `companyDomain` string, nullable
          - `createdAt` string, nullable
          - `location` string, nullable
          - `primaryEmail` string, nullable
          - `emailCount` integer
          - `emails` object[]
          - `primaryPhone` string, nullable
          - `phoneCount` integer
          - `phones` object[]
    - `mergeResult` object, nullable, required: Set only when a MERGE job is completed; null otherwise.
      - `mergedGroups` integer
      - `archivedContacts` integer
      - `skippedGroups` integer: Groups that merged nothing: fewer than two live members when the merge ran, or conflicting Fuse identities.
    - `error` 'DEDUPE_FAILED' | 'null', nullable, required

## Other responses

- `400`: Error
- `401`: Error
- `402`: Error
- `403`: Error
- `404`: Error
- `409`: Error
- `422`: Error
- `429`: Error
- `500`: Error

---

[API](https://skmtc.dev/fuseai/apis/fuse-business-api.md) · [All operations](https://skmtc.dev/fuseai/apis/fuse-business-api/llms.txt) · [OpenAPI document](https://skmtc.dev/fuseai/apis/fuse-business-api/revisions/50d95e0536f4?raw)
