---
title: "Plan a data import — report what will be created, rewritten and refused, and hand back the upload credential + per-table commands (owner/admin)"
method: POST
path: "/v1/projects/{project_id}/imports/plan"
tags: ["imports"]
---

# Plan a data import — report what will be created, rewritten and refused, and hand back the upload credential + per-table commands (owner/admin)

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

## Request

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

Owner/admin. Pure except for minting the upload credential: nothing is created and no job row is written, so an abandoned plan leaves nothing behind. On the default `staging_push` path the response carries a write-only credential valid for 12 hours (re-plan to mint another) and one `INSERT INTO FUNCTION s3(…)` per table for you to run on YOUR ClickHouse — no data passes through this API. Anything you push is transfer scratch: the staging bucket deletes it after 2 days, so start the import before then. See transfer/limits for this deployment's exact values.

## Path parameters

- `project_id` string, required

## Request body

- PlanProjectImportInput
  - `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` 'staging_push' | 'remote_pull' | 'object_store_pull': how the data reaches us. Default staging_push: you run one INSERT INTO FUNCTION s3(…) per table against a credential we mint. remote_pull: we SELECT out of your ClickHouse (zero commands, needs reachability). object_store_pull: you export to your own bucket and we read it
  - `read_only_user` string: remote_pull only: the name to use in the generated CREATE USER/GRANT statements (default whc_import)
  - `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
    - `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 `200`

OK

- ImportPlanOutput
  - `applyable` boolean, required: false when anything was refused: fix or drop those tables and re-plan
  - `job_id` string, required: pass this back when starting the import — on the staging path it is also the upload prefix, so a different id is a different, empty upload
  - `path` string, required
  - `plan` ImportPlanView, required
    - `path` string, required
    - `rejected` ImportRejectionView[], required
      - `code` string, required: stable machine code, e.g. unsupported_engine, storage_policy, materialized_view
      - `detail` string, required
      - `table` string, required
    - `tables` ImportPlannedTableView[], required
      - `changes` string[]: every rewrite applied to your DDL, and why
      - `columns` string[], required: the writable columns, in order; MATERIALIZED/ALIAS columns are computed and excluded
      - `ddl` string, required: the statement we will run, verbatim
      - `format` string, required
      - `partition_by` string
      - `source_database` string
      - `source_engine` string, required
      - `source_table` string, required
      - `source_url` string
      - `target_database` string, required
      - `target_engine` string, required: the engine we will actually create — replicated when the project runs highly available, which is the rewrite that matters most
      - `target_table` string, required
      - `warnings` string[]
    - `target_database` string, required
    - `warnings` string[]
  - `plan_digest` string, required: fingerprint of this exact plan; pass it to the start endpoint to prove the plan you approved is the plan that runs
  - `remote_setup` ImportRemoteSetupView
    - `statements` string[], required: run these on YOUR ClickHouse to create the read-only user we will connect as
    - `user` string, required
  - `staging` ImportStagingView
    - `access_key_id` string, required
    - `bucket` string, required
    - `commands` ImportCommandView[], required: run these on YOUR ClickHouse — one per table — then start the import
      - `sql` string, required
      - `table` string, required
    - `expires_at` string, date-time, required
    - `prefix` string, required: every object this credential may write lives under this key prefix
    - `region` string, required
    - `secret_access_key` string, required: shown once; re-plan to mint another
    - `session_token` string, required
  - `warnings` string[]

## 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/adea029bf505?raw)
