---
title: "Start a planned import — seals the uploaded objects and queues the load (owner/admin)"
method: POST
path: "/v1/projects/{project_id}/imports"
tags: ["imports"]
---

# Start a planned import — seals the uploaded objects and queues the load (owner/admin)

`POST /v1/projects/{project_id}/imports`

## Request

- Base URL: `https://api.wavehouse.cloud`
- URL: `https://api.wavehouse.cloud/v1/projects/{project_id}/imports`
- Auth: one of:
  - API key in cookie `whc_session`
  - HTTP bearer
  - API key in header `X-API-Key`

Owner/admin. SEALS the upload: every object you pushed is recorded by version, and each is then server-side COPIED into a location your upload credential cannot write, from which — and only from which — the load reads. Overwriting your staged files after this point therefore cannot change what is imported. A table with nothing uploaded is a 422. This CREATES TABLES and WRITES DATA and is NOT a transaction: a failed or cancelled import leaves what it already wrote. One import runs per project at a time.

## Path parameters

- `project_id` string, required

## Headers

- `Idempotency-Key` string: A unique value you choose per logical request (a random UUID). A retry with the same key and the same request within 24 hours returns the first response, marked Idempotency-Replayed: true, instead of acting twice. The same key with a different request is refused with 422; one sent while the first is still running gets 409.

## Request body

- StartProjectImportInput
  - `job_id` string, required: the job_id the plan returned. On the staging path this is the prefix you uploaded into
  - `object_store` ImportObjectStoreInput
    - `access_key_id` string: leave both fields empty for a public bucket
    - `secret_access_key` string: stored envelope-encrypted and never returned
  - `path` string
  - `plan_digest` string: the plan_digest from the plan response; when supplied the recomputed plan must match it exactly (409 if not)
  - `remote` ImportRemoteInput
    - `addr` string, required: host:port of your ClickHouse native TLS endpoint; the port must be 9440. Must be reachable from the internet and resolve to a public address
    - `database` string: the database your source tables live in; defaults to the database named in each CREATE TABLE
    - `password` string: that user's password. Sent only when STARTING the import, stored envelope-encrypted, and never returned
    - `user` string, required: a READ-ONLY user; the plan response contains the exact SQL to create one
  - `tables` ImportTableInput[], required: the SAME tables the plan was built from; the plan is recomputed and must match
    - `format` string: object_store_pull only: the exported format. Native (the default) is strongly preferred — Parquet loses Enum ordinals and JSON silently drops RFC-3339 timestamps on the ClickHouse build WaveHouse runs
    - `source_ddl` string, required: the table's CREATE TABLE, verbatim from SHOW CREATE TABLE on your ClickHouse
    - `source_url` string: object_store_pull only: the https URL (globs allowed) of this table's exported data in YOUR bucket
    - `target_table` string: rename on the way in; empty keeps the source table name

## Response `202`

Accepted

- ImportJobView
  - `attempt` integer, required
  - `bytes_done` integer, required
  - `cancel_requested` boolean
  - `created_at` string, date-time, required
  - `finished_at` string, date-time
  - `job_id` string, required
  - `last_error` string
  - `max_attempts` integer, required
  - `path` string
  - `query_id` string: the engine query_id of the statement most recently issued for this job
  - `rows_done` integer, required
  - `started_at` string, date-time
  - `status` string, required: queued | running | succeeded | failed | dead | cancelled
  - `steps` ImportStepView[]
    - `detail` string
    - `done_at` string, date-time
    - `name` string, required: create:<table>, load:<table>/<object>, verify:<table>, or the terminal cancelled step
    - `status` string, required: pending | done | failed
  - `tables` string[]
  - `updated_at` string, date-time, required

## Other responses

- `429`: Rate limited, per client IP, API key or org; a client IP is also refused on every route for a while after repeated authentication failures. A limited operation carries RateLimit-Policy and RateLimit on every response; wait Retry-After seconds before retrying.
- `default`: Error

---

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