# Contacts Tables

**Contacts Tables API:** Create and manage persistent tables of contacts inside Lusha.

Tables are spreadsheets with configurable columns — default Lusha fields, enrichment data, Signals, AI insights, premium data points, CRM fields, and custom fields. Populate a table directly through the endpoints below, or pass `tableId` on Prospecting, Enrich, Signals, or Lookalike calls to persist those results automatically.

Every surface that touches table data — this API, MCP, and the Workspace UI — reads and writes the same underlying data. Changes made through one surface are reflected on the others.

**Working with tables:**
- **Tables** — create, list, get status, update (rename/archive/visibility), delete
- **Entities** — add, remove, or read the rows in a table
- **Columns** — list, remove, or run a column across a table's rows

**Owner resolution:** `owner.email` resolves to a user on your account and determines table ownership. **Required on every call when authenticating with an API key** — omitting it returns `400`. Optional for OAuth/token callers, since the caller is already identified by the token. Sent in the body as `owner: { email }` on `POST`/`PATCH` calls (and on `DELETE .../entities`, which carries a body); sent as a `?email=` query parameter on other `GET`/`DELETE` calls, which have no body.

**Billing:**
- Adding contacts to a table is free.
- Reading entities (`GET .../entities`) charges per row returned.
- Create / List / Get / Update / Delete / List Columns / Remove Column / Remove Entities are free.
- Running a column charges per row per the column's tier (contact enrichment per row with data; signal/AI/score per row per run).
- Non-public-API-plan accounts always resolve to `0` credits charged.

**Limits:** up to 500 entity IDs per add/remove call · max 50,000 entities per table · max 500 tables per account.

See also: [Companies Tables](#tag/Companies-Tables).


## Create Contacts Table

 - [POST /v3/contacts/tables](https://docs.lusha.com/apis/openapi/contacts-tables/createcontactstable.md): Create a new, empty contacts table, optionally seeded with an initial list of contact IDs.

> Billing: Free.

## List Contacts Tables

 - [POST /v3/contacts/tables/list](https://docs.lusha.com/apis/openapi/contacts-tables/listcontactstables.md): List contacts tables owned by the given user, plus any tables shared with the account.

> Billing: Free.

## Get Contacts Table

 - [GET /v3/contacts/tables/{table_id}](https://docs.lusha.com/apis/openapi/contacts-tables/getcontactstable.md): Get a table's metadata and current processing status.

> Billing: Free.

## Update Contacts Table

 - [PATCH /v3/contacts/tables/{table_id}](https://docs.lusha.com/apis/openapi/contacts-tables/updatecontactstable.md): Rename a table, change its visibility, or reassign its owner. All fields except owner are optional — send only what changes.

> Billing: Free.

## Delete Contacts Table

 - [DELETE /v3/contacts/tables/{table_id}](https://docs.lusha.com/apis/openapi/contacts-tables/deletecontactstable.md): Permanently delete a table and all its data. This cannot be undone.

> Billing: Free.

## Get Contacts Table Entities

 - [GET /v3/contacts/tables/{table_id}/entities](https://docs.lusha.com/apis/openapi/contacts-tables/getcontactstableentities.md): Read a page of rows in the table, with all column values and per-cell status.

> Billing: Charged per row returned via export_api.

## Add Entities to Contacts Table

 - [POST /v3/contacts/tables/{table_id}/entities](https://docs.lusha.com/apis/openapi/contacts-tables/addcontactstableentities.md): Add up to 500 contact IDs to an existing table. entityIds accepts either the encrypted Lusha token (v{N}.…, as returned by Search/Enrich/Get Entities) or the legacy numeric personId — an ID that's neither returns 400. Already-present IDs are reported as alreadyPresent and not re-added; unresolvable IDs are not an error, they come back in invalidIds with a 200.

Optionally pass companyIds — one lushaCompanyId per contact, index-aligned with entityIds — to help pair company-level enrichment to the right company for each contact.

> Billing: Free.

## Remove Entities from Contacts Table

 - [DELETE /v3/contacts/tables/{table_id}/entities](https://docs.lusha.com/apis/openapi/contacts-tables/removecontactstableentities.md): Remove specific contact IDs from a table.

> Billing: Free.

## List Contacts Table Columns

 - [GET /v3/contacts/tables/{table_id}/columns](https://docs.lusha.com/apis/openapi/contacts-tables/listcontactstablecolumns.md): List the columns on a table, with type and aggregated per-cell status counts.

> Billing: Free.

## Remove Column from Contacts Table

 - [DELETE /v3/contacts/tables/{table_id}/columns/{column_id}](https://docs.lusha.com/apis/openapi/contacts-tables/removecontactstablecolumn.md): Remove a column and delete all of its cell data across the table. Default Lusha columns cannot be removed.

> Billing: Free.

## Run Column on Contacts Table

 - [POST /v3/contacts/tables/{table_id}/columns/{column_id}/run](https://docs.lusha.com/apis/openapi/contacts-tables/runcontactstablecolumn.md): Populate or refresh a column's data for some or all rows in the table. This is asynchronous — the call returns immediately with status: "processing"; poll Get Contacts Table for isProcessing and per-column row-status counts to know when it's done, then read the values via Get Contacts Table Entities.

runScope values:
- all — every row, including already-processed ones (re-runs / refreshes). Most expensive.
- missing — only rows that have never been run for this column. Cheapest, safe to call repeatedly.
- specific — only the entityIds you pass. Also how you implement "run for this page" — fetch the page via Get Contacts Table Entities, then pass those IDs here.

> Billing: Charged per row processed, per the column's credit tier. Re-running with all charges again for rows that already have data.

