---
title: "Copy rows from one list into another"
method: POST
path: "/business/lists/save-to-list"
tags: ["Lists"]
---

# Copy rows from one list into another

`POST /business/lists/save-to-list`

## Request

- Base URL: `https://api.tryfuse.ai/api/v1`
- URL: `https://api.tryfuse.ai/api/v1/business/lists/save-to-list`
- Auth: HTTP basic

Copies up to `limit` rows from `sourceListId` (a contact list) into a contact list named by `listId` or `listName` (create-or-reuse by exact, case-insensitive name; a name shared by two lists is 409 LIST_NAME_AMBIGUOUS, a reserved system title is 422 LIST_NAME_RESERVED). A `listId` destination that is not the token owner's answers 404 LIST_NOT_FOUND before anything is dispatched. `filters` narrows the copied rows in the same row-filter language as `GET /business/lists/{listId}/rows`. Async: 202 means the copy started and `listIds[0]` is the destination; poll `GET /business/lists/{listId}` until its `completionStatus` is `complete`. The source list is not validated up front — a bad source surfaces as a job that never fills the destination.

Errors:
- `404` `LIST_NOT_FOUND` — The destination listId is not a list visible to the token owner.
- `409` `LIST_NAME_TAKEN` — Creating the destination by listName collided with an existing name.
- `409` `LIST_NAME_AMBIGUOUS` — More than one list carries listName — reference the destination by id.
- `422` `LIST_NAME_RESERVED` — listName is a system/dynamic list title and cannot be a destination.
- `422` `VALIDATION_FAILED` — Missing sourceListId/limit, both or neither of listId/listName, or limit outside 1-10000.
- `429` `RATE_LIMITED` — The lists rate bucket is exhausted.

Requires one of the following token scopes: lists.

## Request body

- object
  - `sourceListId` string, required: 24-character hex id of the contact list whose rows are copied.
  - `limit` integer, required: Maximum number of rows to copy (1-10000).
  - `filters` object: Optional row-filter object narrowing which source rows are copied, in the same language as the filters query of GET /business/lists/{listId}/rows. Text keys (name, title, dept, level, industryName, country, state, personalLocation, companyName, companyIndustry, companyHeadQuartersCountry, companyLocation, website, campaignEngagement, otherLists) take an array of accepted values, e.g. {"companyName":["pomelo"]} or {"country":["argentina"]}, or {"include":[...],"exclude":[...]} — otherLists takes list NAMES, not ids; numberOfEmployees takes headcount ranges the same way but as STRINGS ("51-200", "10001+" or a bare head count "500" — an unquoted JSON number answers 500) and revenue a plain number; keywords (a wildcard match on the job title only) and linkedinUrl take one string; enrichedEmail, enrichedPhone and emailValidity take {"include":[...]} of their statuses; icpMatch takes ["yes"] or ["no"]; customColumns maps a column id to {"dataType":"string","include":[...]}, {"dataType":"boolean","value":true} or {"dataType":"number","rangeMin":1,"rangeMax":10}. Company lists honour companyName, companyIndustry, companyHeadQuartersCountry, website, numberOfEmployees, campaignEngagement and customColumns. Every key is optional; an unknown key or a wrong value shape is rejected rather than ignored. Values inside one key are OR-joined and different keys are AND-ed. VALUES ARE NOT VALIDATED: level, industryName, country, state, website and companyHeadQuartersCountry are matched as whole values and campaignEngagement, icpMatch and enum cells as exact strings, so a value in the wrong vocabulary is accepted and silently matches nothing — each key's own description names its vocabulary and the endpoint that serves it. A rejected filter answers 422 VALIDATION_FAILED.
    - `name` union: Contact full name (contact lists). Free text: each value is matched as a case-insensitive substring of firstName + space + lastName, so Ana matches Ana Silva.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `title` union: Job title (contact lists). Free text: each value is matched as a case-insensitive substring of the stored job title, so vp also matches VP of Sales. There is no fixed vocabulary — `GET /business/prospects/autocomplete?field=title&q=` suggests the titles the enrichment provider knows.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `dept` union: Department (contact lists). Matched as a case-insensitive substring of the contact's department, whose values come from the provider's job_title_role vocabulary — take them from `GET /business/prospects/filter-options` (field `job_title_role`: sales, engineering, human_resources, …). A contact with several roles stores them comma-joined, which a substring match still finds. Any other text is accepted and matches only rows that literally contain it.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `level` union: Seniority level (contact lists). Matched EXACTLY (case-insensitive, whole value) against the contact's level, whose values come from the provider's job_title_levels vocabulary — take them from `GET /business/prospects/filter-options` (field `job_title_levels`): cxo, director, entry, manager, owner, partner, senior, training, unpaid, vp. A contact carrying several levels stores them comma-joined (cxo, owner) and an exact match on one level does NOT find it; any other value is accepted and matches nothing.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `industryName` union: Contact's industry (contact lists). Matched EXACTLY (case-insensitive, whole value) against the industry on the contact, which the enrichment provider fills from its own 147-value list — take the values from `GET /business/prospects/filter-options` (field `industry`, e.g. computer software, hospital & health care). A near-miss such as Software is accepted and matches nothing.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `country` union: Contact's country (contact lists). Matched EXACTLY (case-insensitive, whole value) against the country of the contact's address, stored as the provider spells it (united states, not US) — take the values from `GET /business/prospects/filter-options` (field `location_country`). An ISO code or another spelling is accepted and matches nothing.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `state` union: Contact's state or region (contact lists). Matched EXACTLY (case-insensitive, whole value) against the state of the contact's address — the provider's region name, e.g. california. There is no enum; `GET /business/prospects/autocomplete?field=region&q=` suggests from the same region vocabulary. A spelling no row carries is accepted and matches nothing.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `personalLocation` union: Contact's location text (contact lists). Each value is matched as a case-insensitive substring against the contact's city, state AND country separately, and the row matches when any one of the three contains it — so pass ONE component (buenos aires, or california, or united states), never a whole line like san francisco, california, united states, which matches no component. No endpoint serves the city names: read the stored components off the rows themselves, where `GET /business/lists/{listId}/rows` returns each row's `location` as {city, state, country}. For the other two components, `GET /business/prospects/autocomplete?field=region&q=` suggests the state/region names and `GET /business/prospects/filter-options` (field `location_country`) lists the country names. Do NOT feed this key from `GET /business/prospects/autocomplete?field=location`: that endpoint returns whole city, state, country lines, which are exactly the shape that matches no component.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `companyName` union: Company name (both list kinds). Free text: each value is matched as a case-insensitive substring of the company name. On a company list, `GET /business/lists/{listId}/filter-values?field=name` returns the exact names that list holds.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `companyIndustry` union: Company industry (both list kinds). Matched as a case-insensitive substring of the company's industry, filled by the enrichment provider from its own list — take the values from `GET /business/prospects/filter-options` (field `job_company_industry`). Do not use the company-search vocabulary from /business/companies/filter-options: that is a different provider's list and its values are not what these rows store.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `companyHeadQuartersCountry` union: Company headquarters country (both list kinds). Matched EXACTLY (case-insensitive, whole value), but each value is first expanded to every spelling of that country (full name, ISO-3166 alpha-2 and alpha-3), because the field mixes both vocabularies — so the full country name alone is enough. Take the names from `GET /business/prospects/filter-options` (field `location_country`); a value that resolves to no country is matched verbatim.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `companyLocation` union: Company location text (contact lists). Each value is matched as a case-insensitive substring against the company address's city, state AND country separately, and the row matches when any one of the three contains it, so pass ONE component. Many companies are stored with a country and nothing else, in which case a city or state value is accepted and matches nothing; `GET /business/prospects/filter-options` (field `location_country`) lists the country names.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `website` union: Company web domain (both list kinds). Matched EXACTLY (case-insensitive, whole value) against the stored domain, so pass the BARE domain — brightpay.com. A full URL (https://brightpay.com/), a www. prefix or a partial domain is accepted and matches nothing. On a company list, `GET /business/lists/{listId}/filter-values?field=domain` returns the exact domains that list holds.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `numberOfEmployees` union: Company size (both list kinds). Each value is parsed as a headcount range and matched against the company's LinkedIn head count; accepted values are a range ("51-200"), an open top end ("10001+") or a bare head count ("500", meaning exactly 500). Send every value as a JSON STRING: an unquoted JSON number passes validation and then throws while the range is parsed, so the call answers 500 UNEXPECTED_ERROR instead of filtering. The provider's own labels — 1-10, 11-50, 51-200, 201-500, 501-1000, 1001-5000, 5001-10000, 10001+, served by `GET /business/prospects/filter-options` (field `job_company_size`) — all parse. Anything else is read with parseInt, so a value that STARTS with digits silently narrows to that one number ("51 to 200" matches companies with exactly 51 employees, it is not ignored) and a value that does not ("Enterprise") is silently dropped; if every value is dropped nothing is left to match and the call answers zero rows rather than ignoring the filter, and companies with no stored head count never match.
      - union[]: Accepted range labels, OR-joined: a row matches when any one of them matches (at most 200).
        - union
          - string
          - number
      - object
        - `include` union[]: Range labels to keep, OR-joined: a row matches when any one of them matches (at most 200).
          - union
            - string
            - number
        - `exclude` union[]: Range labels to drop: a row is dropped when it matches any one of them (at most 200).
          - union
            - string
            - number
    - `revenue` union: Company revenue (contact lists). Accepted values: a plain number, matched for exact equality against the company's stored revenue. The enrichment provider stores revenue as a LABEL (for example $10m-$25m), and a label is coerced to a number before matching, so a label — or any other non-numeric value — is accepted and matches nothing. Use numberOfEmployees to narrow by company size instead.
      - union[]: Accepted range labels, OR-joined: a row matches when any one of them matches (at most 200).
        - union
          - string
          - number
      - object
        - `include` union[]: Range labels to keep, OR-joined: a row matches when any one of them matches (at most 200).
          - union
            - string
            - number
        - `exclude` union[]: Range labels to drop: a row is dropped when it matches any one of them (at most 200).
          - union
            - string
            - number
    - `keywords` string: Wildcard search over the contact's job title only (contact lists) — it matches no other field. Free text: every character is matched literally except the two wildcards, `*` for any run of characters and `?` for any single one, the match is case-insensitive and unanchored (vp matches VP of Sales), and the regex characters . ^ $ + ( ) [ ] { } | \ are rejected with 422.
    - `linkedinUrl` string: LinkedIn URL (contact lists). One value, matched as a case-insensitive substring of the stored URL, which is kept scheme-less and www-less as linkedin.com/in/<slug> — so pass the slug or linkedin.com/in/<slug>. A full https://www.linkedin.com/in/<slug> is longer than the stored value, so it is accepted and matches nothing.
    - `enrichedEmail` object: Email enrichment state (contact lists). One of exactly: enriched (an email was found), not_enriched (never attempted) or no_emails_found (attempted, nothing found).
      - `include` string[]: Statuses to keep: enriched, not_enriched, no_emails_found. A contact is kept when it is in any one of the listed states (OR-joined), so listing every state keeps every row.
      - `exclude` string[]: Statuses to drop: enriched, not_enriched, no_emails_found. The states are OR-joined: a contact in any one of them is dropped.
    - `enrichedPhone` object: Phone enrichment state (contact lists). One of exactly: enriched (a phone was found) or not_enriched (no phone yet).
      - `include` string[]: Statuses to keep: enriched, not_enriched. A contact is kept when it is in any one of the listed states (OR-joined), so listing both states keeps every row.
      - `exclude` string[]: Statuses to drop: enriched, not_enriched. The states are OR-joined: a contact in any one of them is dropped.
    - `emailValidity` object: Validation status of the contact's emails (contact lists). One of exactly: valid, invalid, catch-all, unknown, do_not_mail, personal, unverified — valid means a deliverable WORK address (personal ones are excluded from it), personal means a deliverable personal address, and unverified means a user-uploaded address that was never verified.
      - `include` string[]: Statuses to keep: valid, invalid, catch-all, unknown, do_not_mail, personal, unverified. OR-joined over statuses and over the contact's addresses: a contact is kept when any one of its emails carries any one of the listed statuses.
      - `exclude` string[]: Statuses to drop: valid, invalid, catch-all, unknown, do_not_mail, personal, unverified. OR-joined over statuses and over the contact's addresses: a contact is dropped when any one of its emails carries any one of the listed statuses.
    - `icpMatch` union: ICP match outcome (contact lists). Accepted values: exactly one of yes (rows matching the ICP saved in the app) or no (the rest). When the workspace has no ICP configured nothing is a match, so yes returns no rows at all and no returns every row.
      - string[]: Accepted values: exactly one of ["yes"] (ICP-matched rows) or ["no"] (the rest); ["yes","no"] is read as ["yes"].
      - object
        - `include` string[]
        - `exclude` string[]
    - `campaignEngagement` union: Campaign engagement bucket (both list kinds). One of exactly: contacted (at least one campaign email has been sent to the contact), in_active_campaign (targeted by a running campaign, nothing sent yet) or never_engaged (neither) — compared as whole values, case included, so any other value is accepted and returns no rows. A company's bucket is the highest bucket among its contacts.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `otherLists` union: NAMES (not ids) of other lists the contact must — or with exclude, must not — also belong to (contact lists). Each value is matched as a case-insensitive substring of the list names from `GET /business/lists`; only the team's static contact lists are searched, so the list being read, dynamic lists and archived lists never satisfy it.
      - string[]: Accepted values, OR-joined: a row matches when any one of them matches (1-200 values, max 500 characters each).
      - object: Explicit include/exclude lists; include is OR-joined, exclude drops a row matching any of its values.
        - `include` string[]: Values to keep, OR-joined: a row matches when any one of them matches (up to 200).
        - `exclude` string[]: Values to drop: a row is dropped when it matches any one of them (up to 200).
    - `customColumns` object: Cell filters keyed by custom/smart column id (from `GET /business/lists/{listId}/columns`), both list kinds. Every column listed must match (AND-ed); a column id that is not on this list matches no row.
  - `listId` string: 24-character hex id of an existing contact list to copy into; mutually exclusive with listName.
  - `listName` string: Name of the destination contact list (1-255 characters): an existing list with this exact name (case-insensitive) is reused, otherwise it is created; two lists sharing the name answer 409 LIST_NAME_AMBIGUOUS and reserved system titles answer 422 LIST_NAME_RESERVED.

## Response `202`

Success

- object
  - `listIds` string[], 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)
