---
title: "Upsert leads into a data collection"
method: POST
path: "/data-collections/{id}/leads"
tags: ["Data collections"]
---

# Upsert leads into a data collection

`POST /data-collections/{id}/leads`

## Request

- Base URL: `https://api.flogpt.com/api/v1`
- URL: `https://api.flogpt.com/api/v1/data-collections/{id}/leads`
- Auth: one of:
  - HTTP bearer
  - API key in header `x-api-key`

Up to 1000 leads per request, applied through the same importer the CSV
upload uses. A row that matches an existing lead updates it rather than
inserting a second copy; the match column is the collection's
`primaryKeyColumn` when it declares one, otherwise its phone column.
Phone matching compares the last ten digits, so `+1 (555) 100-0000` and
`5551000000` are the same lead.

Rows are never rejected as a batch — the response tallies what was
created, updated and skipped, and explains each skipped or malformed
value in `warnings`. Rows that would take the collection past its plan
row limit are refused individually and reported the same way; `409` is
returned only when the collection was already over its limit and nothing
could be written.

A collection owned by an integration (`readOnly: true`, e.g. Shopify)
cannot be written to and returns `400 read_only_collection`.

## Request body

- object
  - `leads` LeadInput[], required
    - `name` string
    - `phone` string
    - `email` string
    - `company` string
    - `status` 'new' | 'contacted' | 'callback' | 'won' | 'lost'
    - `potential` 'hot' | 'warm' | 'cold'
    - `followUpAt` string, date-time
    - `tags` string[]
    - `optedOut` boolean
    - `fields` object: Extra columns, keyed by column name. Columns the list does not have yet are added to it, with their type inferred from the values in the request. A key that collides with one of the named fields above is ignored in favour of the named one.

## Response `200`

Import tally

- object
  - `data` LeadImportResult: Per-request tally. Rows are reported, not rejected wholesale.
    - `created` integer
    - `updated` integer
    - `skipped` integer: Rows carrying nothing usable, or repeating a match key from earlier in the same request.
    - `columnsAdded` string[]: Columns this request added to the list.
    - `columnsIgnored` string[]: Fields recognised as server-derived and therefore not importable.
    - `matchedOn` string, nullable: The column that decided whether a row was an update. Null when every row was inserted.
    - `warnings` string[]

## Other responses

- `400`: The request is invalid.
- `404`: The resource does not exist in the API key's organization.
- `409`: The request cannot be applied in the resource's current state.

---

[API](https://skmtc.dev/flogpt/apis/flogpt-organization-api.md) · [All operations](https://skmtc.dev/flogpt/apis/flogpt-organization-api/llms.txt) · [OpenAPI document](https://skmtc.dev/flogpt/apis/flogpt-organization-api/revisions/c3a6cff76797?raw)
