Bulk-imports accounts from a single CSV — one account + one location (store) per row — designed for up to ~100K rows. The endpoint validates the upload for shape/size, stored, and queued; processing runs asynchronously on the bulk queue (prepare, then parallel chunk jobs, then finalize). Returns an import_id immediately — poll Bulk import status for progress.
Per created row the system provisions the full cascade: account company (bundle_id=3), company_plans, company_options, owner user + person, location-account company (bundle_id=2), store, store_options, store_plans, and store_review_sites (one per the plan's review-site set). The endpoint suppresses welcome/notification emails for bulk imports; The endpoint creates every owner with a temporary password and must set their own at first login.
Auth
- Partner — authentication. Caller must be a partner company (company
bundle_id= 1); The endpoint scopes the import to that partner. Non-partner callers get 403.
Rate limit
- No rate limit (bulk import — the
bulkworker controls back-pressure).
Body (multipart/form-data)
| Field | Type | Required | Notes |
|---|---|---|---|
file | file (.csv) | Required | CSV only, max 100 MB. Header row must contain every required column below. The endpoint rejects the xlsx (streamed row-by-row for memory safety). |
CSV columns — one account+location per row
| Column | Required | Notes |
|---|---|---|
account_name | Required | Account name (max 100, no <>). |
client_account_id | Required | Partner-unique external account id (^[A-Za-z0-9_-]+$, max 100). Duplicate (in-file or existing) → skipped duplicate_client_account_id. Also the re-run idempotency key. |
plan_id | Required | Numeric plan id assigned to the partner (company_plans). Unassigned → skipped plan_unavailable. |
owner_first_name | Required | Account owner first name. |
owner_email | Required | Globally unique on users. Duplicate → skipped duplicate_email. |
location_name | Required | Location/store name (max 100, no <>). |
client_location_id | Required | Partner-unique external location id (same charset). Duplicate → skipped duplicate_client_location_id. |
address | Required | max 255, no <>. |
country | Required | Country code or name; must already exist (unknown falls back to skipped unknown_country). Countries are never auto-created. |
state | Required | State code or name; created under the country when missing. |
city | Required | City name; created under (country, state) when missing. |
postal_code | Required | 3–12 chars. |
website | Optional | URL — account website. |
billing_id | Optional | max 100. |
owner_last_name | Optional | Account owner last name. |
owner_phone | Optional | 9–15 digits, no spaces/symbols. |
storeid | Optional | Store label; defaults to the resolved city name when blank. |
business_phone | Optional | 9–15 digits. |
site_url | Optional | URL — location website. |
companyindustry_id | Optional | Industry id; blank, then defaults to Home Services. Non-existent id, then skipped invalid_row. A partner-level industry still overrides the per-row value when set. |
Behaviour
- 202 Accepted →
{ data: { import_id, status: "queued" } }— the response returns immediately with animport_id. The import itself runs in the background, so poll the status endpoint rather than treating 202 as "import finished". - Validation, de-dupe (in-file + against the partner's existing accounts/locations) and geo resolve/create all happen in the single-threaded prepare stage. The endpoint records invalid/duplicate rows and skipped while the rest continue (skip-and-report).
- Safe to re-run: re-uploading the same file only creates rows whose
client_account_id/client_location_iddon't already exist for the partner. - 403 if the caller is not a partner; 422 if the file is missing, not a
.csv, or larger than 100 MB.
| Time | Status | User Agent | |
|---|---|---|---|
Retrieving recent requests… | |||
