Skip to main content
POST
/
api
/
lists
/
{id}
/
companies
Add a company to a list
curl --request POST \
  --url https://app.puffle.ai/api/lists/{id}/companies \
  --header 'Authorization: Bearer <token>' \
  --header 'Content-Type: application/json' \
  --data '
{
  "name": "<string>",
  "domain": "<string>",
  "linkedinUrl": "<string>",
  "industry": "<string>",
  "employeeCount": "<string>",
  "employeeCountExact": 0,
  "foundedYear": 0,
  "fundingStage": "<string>",
  "fundingTotal": 123,
  "location": "<string>",
  "city": "<string>",
  "state": "<string>",
  "country": "<string>",
  "description": "<string>",
  "website": "<string>",
  "logoUrl": "<string>",
  "source": "<string>",
  "sourceId": "<string>"
}
'
{ "company": { "id": "3c90c3cc-0d44-4b50-8888-8dd25736052a", "list_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a", "name": "<string>", "domain": "<string>" } }

Overview

Adds one company row to a list. The only required field is name — everything else (domain, linkedinUrl, industry/size/funding/location, etc.) is optional and maps to matching columns on list_company_rows. Domain is lowercased before insert and enforced unique per list at the database level. Submitting a company whose domain or name matches the caller’s blocklist is rejected with a 400 — clear the blocklist entry or use a different identifier. Unlike listListCompanies (which returns camelCase rows), this endpoint returns the raw snake_case DB row. If you need camelCase, fetch the company via getListCompany after insert.

AI agent notes

Deduping. Domain is the only unique key on the row. Two companies with the same name but different (or null) domains are both inserted. Call listListCompanies first if you want to avoid near-duplicates by name.Blocklist. The caller’s blocked_companies is checked on both domain and name. A match on either returns 400 with error: "Company is blocked". There is no override flag on this endpoint — unblock first via the blocklist endpoint, then retry.Response shape mismatch. This POST returns snake_case ({ company: { list_id, logo_url, ... } }), whereas GET returns camelCase ({ company: { listId, logoUrl, ... } }). Don’t reuse the same parser for both.

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

All optional except name. Only name and domain are read from the top level; the rest are mapped via a camelCase → snake_case field map.

name
string
required

Company name. Required.

Minimum string length: 1
domain
string

Company domain. Lowercased before insert. Unique per list — a duplicate returns 409.

linkedinUrl
string

LinkedIn company-page URL.

industry
string
employeeCount
string

Free-form size string (e.g. "51-200", "1k-5k").

employeeCountExact
integer
Required range: -9007199254740991 <= x <= 9007199254740991
foundedYear
integer
Required range: -9007199254740991 <= x <= 9007199254740991
fundingStage
string
fundingTotal
number
location
string
city
string
state
string
country
string
description
string
website
string
logoUrl
string
source
string
sourceId
string

Response

Company inserted. Raw snake_case row returned.

company
object
required

Raw Supabase row (snake_case). Unlike GET, this POST returns the DB row directly without the toCompanyRow transform.