Skip to main content
POST
/
api
/
lists
/
{id}
/
add-search-results
Add saved search results to a list
curl --request POST \
  --url https://app.puffle.ai/api/lists/{id}/add-search-results \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "searchId": "<string>",
  "skipCrossListDedup": true
}
'
{ "inserted": 4503599627370495, "skipped": 4503599627370495, "companiesCreated": 4503599627370495 }

Overview

After POST /api/leads/search completes (asynchronously, via Trigger.dev — see GET /api/leads/search/{id} for polling), call this endpoint to commit those results into a list. The server:
  1. Loads the lead_searches row by searchId, verifies the caller owns both the search and the target list, and checks the search is in the completed state with at least one result.
  2. Dedupes new rows against the list’s existing rows (by LinkedIn URL) and — unless skipCrossListDedup: true — against every other list owned by the caller (four-tier cascade).
  3. Extracts company firmographics, creates or links list_company_rows, and sets company_row_id on each person.
  4. Auto-creates profile columns for any search-payload fields the list doesn’t already have.
  5. Dispatches the standard per-column enrichment Trigger.dev runs.
The work is heavy for large searches — the route allows up to 120 s of execution. Do not abort the fetch client-side.

AI agent notes

When to call. Immediately after GET /api/leads/search/{id} reports status: "completed". The lead-search task can auto-add results to a list when given a listId up front (see the leads-search API), so you only need this endpoint when you created the list after the search started.Body. { searchId: "<uuid>" } is the minimum. Pass skipCrossListDedup: true only for deliberate re-imports into a dedicated list.Error handling.
  • 400 Search is not completed — poll again later; the search is still running.
  • 400 Search has no results — the search returned zero leads. Stop.
  • 404 Search not found / List not found — the caller doesn’t own the resource. Surface to the user.
Follow-up. Use GET /api/lists/{id} (core route) to observe the new row count and enrichment status. The response counters (inserted, skipped, companiesCreated) are the single source of truth for “did we actually add anything” — don’t infer from HTTP status alone.Service-role caveat. Internally this route uses the Supabase service role to bypass RLS for fan-out writes. Ownership is enforced in application code before any write — if this endpoint ever returns 200, the caller owns the resulting rows.

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

JSON body. searchId is required.

searchId
string
required

lead_searches.id — the completed search run whose results should be added. Typically the id returned from POST /api/leads/search.

Minimum string length: 1
skipCrossListDedup
boolean

Default false. When true, rows already present in other lists owned by the caller are still inserted (the per-list linkedin_url uniqueness check still applies). Use this for targeted re-imports.

Response

Results added. Returns per-category counters.

Shape of the addSearchResultsToList() result. Additional counters (e.g. columns created, enrichment tasks triggered) may be present — treat the response as extensible.

inserted
integer
required

New list_rows created from the search payload.

Required range: 0 <= x <= 9007199254740991
skipped
integer
required

Rows skipped due to duplicates (within-list linkedin_url uniqueness, optional cross-list dedup, or blocked companies).

Required range: 0 <= x <= 9007199254740991
companiesCreated
integer
required

New list_company_rows extracted from the search payload.

Required range: 0 <= x <= 9007199254740991
{key}
any