Skip to main content
POST
/
api
/
lists
/
{id}
/
rows
Add a person row to a list
curl --request POST \
  --url https://app.puffle.ai/api/lists/{id}/rows \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "firstName": "<string>",
  "lastName": "<string>",
  "company": "<string>",
  "role": "<string>",
  "linkedinUrl": "<string>",
  "location": "<string>",
  "domain": "<string>",
  "email": "<string>",
  "phone": "<string>",
  "customColumns": {}
}
'
{ "row": { "id": "r1111111-1111-1111-1111-111111111111", "listId": "a1111111-1111-1111-1111-111111111111", "linkedinUrl": "https://linkedin.com/in/jane-doe", "profileData": { "firstName": "Jane", "lastName": "Doe", "fullName": "Jane Doe", "currentCompany": "Acme Capital", "currentRole": "Founder & CEO" }, "source": "manual", "addedAt": "2026-04-22T10:00:00Z", "companyRowId": null, "companyDomain": null, "cells": [] }, "columnsToEnrich": [] }

Overview

Insert (or merge into) a person row. Runs the three-tier dedup cascade: (1) LinkedIn URL, (2) name + company, (3) name + location, and finally (4) name only. On match, merges the incoming profile into the existing row (new values only fill gaps — existing non-empty values are preserved). Built-in column cells are created on fresh inserts. The response columnsToEnrich tells the caller which AI-fill columns still need a background enrichment task dispatched.
This operation shares the URL path /api/lists/{id}/rows with other verbs. See the sibling page for related operations on the same resource.

Authorizations

Authorization
string
header
required

Bearer authentication header of the form Bearer <token>, where <token> is your auth token.

Path Parameters

id
string<uuid>
required

List UUID.

Pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$

Body

application/json

Create a person row. The endpoint dedupes against existing rows with four strategies (LinkedIn URL, name + company, name + location, name only) and merges into the existing row when a match is found — the response merged: true signals a merge vs. a fresh insert.

firstName
string
required

Required.

lastName
string
required

Required.

company
string
required

Current company. Required.

role
string

Current role/title.

linkedinUrl
string

LinkedIn URL. If omitted, a placeholder imported-<timestamp>-<random> is generated.

location
string

Free-text location string.

domain
string

Company domain. Also checked against the caller's blocklist of blocked domains/company names.

email
string
phone
string
customColumns
object

Optional map of columnId → value/AI-fill. letAiFill: true creates a pending cell and kicks off enrichment; otherwise the value is written as-is.

Response

Row created or merged. merged: true signals a dedup match against an existing row.

row
object
required
columnsToEnrich
string<uuid>[]
required

Column UUIDs for which a background enrichment task should be dispatched. The caller is responsible for triggering enrichment — this endpoint only creates the pending cells.

Pattern: ^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$
merged
boolean

Present and true only when the row deduped into an existing row. Absent otherwise.