---
title: "Create a table fed by an integration"
method: POST
path: "/table-sources/"
tags: ["table-sources"]
---

# Create a table fed by an integration

`POST /table-sources/`

## Request

- Base URL: `https://api.getoneprofile.ai` (the document declares no server; this is the origin it was published from)
- URL: `https://api.getoneprofile.ai/table-sources/`
- Auth: one of:
  - API key in header `X-API-Key`
  - HTTP bearer

Create the table, its imported-record column and a few extracted columns, then start loading rows in the background.

The table exists as soon as this returns, so the client can navigate to it immediately and watch rows arrive. Poll the run to follow progress.

Every record lands whole in one Object column. The extracted columns are formulas reading paths out of it, so a refresh updates them for free, and any field the import brought back can be given its own column later without re-fetching anything.

`identity_field` picks which of the preview's `identity_field_choices` rows are matched on across refreshes; left out, the preview's recommended one is used. A key some records lack or records share is refused with a 422.

## Request body

- CreateTableFromSourceRequestSchema: What to import, and into a table called what.
  - `folder_id` string, uuid, nullable: The folder to make the table in, as when the import starts from inside one on the Tables list. Leave it out for the top level.
  - `identity_field` string, nullable: Which id key to match rows on, one of the preview's identity_field_choices. Null takes the preview's recommended identity_field. A key some records lack, or one records share, is refused.
  - `integration_id` string, uuid, required: The connected integration to import from.
  - `promote_paths` string[], nullable: Fields to give their own column, in this order. Null uses the preview's suggestions; an empty list promotes nothing. Unknown paths are skipped; the table's column limit is the only ceiling.
  - `record_type` string, required: Which object to import.
  - `row_limit` integer, nullable: Most rows one run may pull; null for everything. A limited source can never sweep deletions: it cannot tell a record it chose not to fetch from one that is gone.
  - `table_name` string, required: Name for the new table.

## Response `201`

Successful Response

- CreateTableFromSourceResponseSchema: The table that now exists, and the run filling it.
  - `run` TableSourceRunSchema, required: One import run's progress and outcome.
    - `error` string, nullable, required
    - `error_code` string, nullable: Classified cause of the failure: unauthorized, rate_limited, server_unavailable, network_error, not_found, pagination_error, bad_request. NULL when the run did not fail.
    - `finished_at` string, date-time, nullable, required
    - `id` string, uuid, required
    - `identity_field` string, nullable, required: The field records were matched on during this run. Null for a run from before this was recorded; read the source's current identity_field instead in that case.
    - `is_full_relist` boolean, required: Whether this run asked the provider for everything.
    - `needs_reconnect` boolean: Whether fixing this means reconnecting the integration. Only true for a credentials failure, so the prompt keeps its meaning.
    - `rows_created` integer, required
    - `rows_deleted` integer, required
    - `rows_skipped` integer, required: Records the run read and left out, broken down by the two counts below. Stopping at the row limit is not a skip; see stopped_at_row_limit.
    - `rows_skipped_duplicate` integer, required: Of those, records whose match value an earlier, different record in the same page already had; only the first was added.
    - `rows_skipped_no_identity` integer, required: Of those, records that came back with no value for the field rows are matched on, so there was no row for them to be.
    - `rows_updated` integer, required
    - `source_id` string, uuid, required
    - `started_at` string, date-time, required
    - `status` 'running' | 'cancelling' | 'cancelled' | 'succeeded' | 'failed', required: Where one import run has got to. The terminal states are absorbing: a run never leaves them. There is no PENDING here. Unlike a CSV upload, which sits parsed and uncommitted until the user says go, a source run is only ever created at the moment something decides to run it. CANCELLING is a request, not an outcome. Stopping an import is a database status flip the workflow observes between pages, never a Temporal terminate: a terminate could land mid-page and leave rows half-written with nothing to settle the run, which is exactly the stuck state that blocks every future refresh of the table.
    - `stopped_at_row_limit` integer, nullable, required: The row limit this run stopped at with records still unread in the tool; null when it read everything it was asked for. Raising the source's row_limit brings the rest in.
    - `sweep_deletions` boolean, required: Whether this run was asked to delete rows whose records are gone from the tool. A run that stopped at a row limit (stopped_at_row_limit) deletes nothing, because it has not read the whole tool.
    - `table_id` string, uuid, required
    - `trigger` 'initial' | 'manual' | 'scheduled', required: What asked for a run.
  - `source` TableSourceSchema, required: A table's import source.
    - `created_at` string, date-time, required
    - `id` string, uuid, required
    - `identity_field` string, required
    - `integration_id` string, uuid, required
    - `integration_type` string, required
    - `last_synced_at` string, date-time, nullable, required: Start of the last successful run; the incremental watermark.
    - `object_column_id` string, uuid, required: The column each imported record lands in.
    - `record_type` string, required
    - `record_type_label` string, required: The object type's name for a person to read ('Contacts', 'Event type').
    - `row_limit` integer, nullable, required
    - `supports_incremental` boolean, required
    - `table_id` string, uuid, required
    - `updated_at` string, date-time, required
  - `table_id` string, uuid, required: Navigate here immediately; rows arrive behind you.

## Other responses

- `401`: Unauthorized
- `403`: Forbidden
- `404`: Integration or folder not found
- `422`: Invalid request made to the API.
- `500`: Internal server error.
- `502`: Bad gateway error.
- `503`: Service unavailable.

---

[API](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile.md) · [All operations](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/llms.txt) · [OpenAPI document](https://skmtc.dev/getoneprofile/apis/api-reference-oneprofile/revisions/4a6bd71e3f02?raw)
