---
title: "Upload new contact data into a list (RAW upload)"
method: POST
path: "/business/lists/{listId}/contacts"
tags: ["Lists"]
---

# Upload new contact data into a list (RAW upload)

`POST /business/lists/{listId}/contacts`

## Request

- Base URL: `https://api.tryfuse.ai/api/v1`
- URL: `https://api.tryfuse.ai/api/v1/business/lists/{listId}/contacts`
- Auth: HTTP basic

Creates new contacts from the supplied data and adds them to the list, the same path a CSV upload takes (no enrichment runs and no credits are spent). Every row becomes a new contact — there is no de-duplication against contacts already in the workspace, so retrying a call duplicates rows. Emails are stored as the primary email with status `valid` without verification, phones are normalised to E.164 (US assumed when no country code is given), and LinkedIn URLs are normalised to `linkedin.com/in/<slug>` so the contact can later be found with `POST /business/contacts/resolve`. The returned `contactIds` are the new rows' ids, usable with `columns/{columnId}/values`, `enrich` and `crm-push`. Up to 500 contacts per call.

Errors:
- `404` `LIST_NOT_FOUND` — No list with that id is visible to the token owner.
- `422` `CONTACT_LIST_REQUIRED` — The list is a company list; contact rows cannot be written into it.
- `422` `VALIDATION_FAILED` — contacts missing/empty/over 500, a row with none of email/linkedinUrl/firstName/lastName, a malformed email, or an unknown key.
- `429` `RATE_LIMITED` — The lists rate bucket (120 requests/minute, 10,000/day per token owner) is exhausted.

Requires one of the following token scopes: lists.

## Path parameters

- `listId` string, required: 24-character hex id of the list (from GET /business/lists).

## Request body

- object
  - `contacts` object[], required: Contacts to create as new rows in the list, at most 500 per call. The entries do not combine: each one becomes its own new contact, in the order sent, and there is no de-duplication — against the workspace or within the call, so the same person sent twice lands twice. Every field is free text stored as given (only linkedinUrl and phone are normalised); nothing is enriched or verified on upload.
    - `firstName` string: Given name (max 200 characters).
    - `lastName` string: Family name (max 200 characters).
    - `email` string, email: Email address (max 320 characters); stored as the contact's primary email with status valid — it is not verified on upload.
    - `phone` string: Phone number (max 50 characters). Free text in any dialable shape — +1 206 362 2366, (206) 362-2366, 2063622366 — it is never rejected for its format: each value is normalised to E.164, reading the country from a leading +<code> and otherwise assuming US, and a value that cannot be parsed is stored with its digits kept. A comma-separated string is split into several phones and the first becomes the primary.
    - `linkedinUrl` string: LinkedIn profile URL (max 500 characters); normalised to linkedin.com/in/<slug> and used to match the contact later — a non-LinkedIn URL is dropped.
    - `jobTitle` string: Job title (max 300 characters). Free text, stored verbatim: there is no vocabulary and no normalisation, and this is the only field the title and keywords row filters read — the level and dept filters stay empty for an uploaded contact until it is enriched.
    - `companyName` string: Employer name (max 300 characters); matched to an existing company in the workspace by domain or name, otherwise created.
    - `companyDomain` string: Employer web domain (max 300 characters), e.g. brightpay.com.

## Response `201`

Success

- object
  - `addedCount` integer, required
  - `contactIds` 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)
