API Import Guide
Overview
Section titled “Overview”The batch import API accepts JSON payloads of up to 1,000,000 records per request. Imports are processed asynchronously — submit a batch, get back a job ID, and poll for results.
Authentication
Section titled “Authentication”Include one of the following headers:
# API Key (for IT admins and integrations)X-API-Key: adva_k_your_key_here
# Bearer Token (for web UI users)Authorization: Bearer YOUR_ACCESS_TOKENTo create an API key, go to Settings > API Keys in the Adva web app.
Required role
Section titled “Required role”Import endpoints — including POST /api/v1/import/{entity_type} and the
multipart POST /api/v1/import/{entity_type}/upload — require the API key’s
owner to hold an account Owner or Admin role. An API key
authenticates as its owner: scopes on the key can only narrow that role’s
access, never grant it.
A 403 response with error_code: FORBIDDEN_ACTION and a message containing
cannot manage Import means the key owner’s role is too low. This is a
role-level restriction, not an API-key scope problem — regenerating the key
will not fix it. Ask an account admin to grant the owner an Admin role.
Submit an Import
Section titled “Submit an Import”POST /api/v1/import/{entity_type}Replace {entity_type} with one of the supported types: product, territory, customer, team_member, crew, opportunity, location, proposal, proposal_item, job, job_item, invoice, warranty, transaction, commission_plan, brand, brand_assignment, promotion, proposal_discount, commission.
Request Body
Section titled “Request Body”{ "source": "airtable-migration", "mode": "incremental", "records": [ { "external_id": "PROD-001", "name": "Premium Bermuda Turf", "category": "turf", "unit_price": 2.5, "unit_type": "sqft" }, { "external_id": "PROD-002", "name": "Standard Fescue", "category": "turf", "unit_price": 1.75, "unit_type": "sqft" } ]}| Field | Type | Required | Description |
|---|---|---|---|
source | string | Yes | Identifier for this import source (e.g., "airtable", "jobber", "csv-upload") |
mode | string | No | incremental (default; only supported value) |
records | array | Yes | Array of record objects (1 to 1,000,000) |
Response (202 Accepted)
Section titled “Response (202 Accepted)”{ "import_job_id": "550e8400-e29b-41d4-a716-446655440000", "status": "pending", "entity_type": "product", "total_rows": 2, "poll_url": "/api/v1/import/jobs/550e8400-e29b-41d4-a716-446655440000"}Poll for Results
Section titled “Poll for Results”GET /api/v1/import/jobs/{import_job_id}Response
Section titled “Response”{ "id": "550e8400-e29b-41d4-a716-446655440000", "entity_type": "product", "status": "completed", "total_rows": 2, "processed_rows": 2, "valid_rows": 2, "error_rows": 0, "created_count": 2, "updated_count": 0, "skipped_count": 0, "errors": [], "warnings": [], "started_at": "2026-04-08T15:30:00Z", "completed_at": "2026-04-08T15:30:01Z"}Job Status Values
Section titled “Job Status Values”| Status | Meaning |
|---|---|
pending | Job is queued |
validating | Records are being validated against the schema |
importing | Valid records are being inserted/updated |
completed | All records processed |
failed | Job failed (check errors array) |
List Import Jobs
Section titled “List Import Jobs”GET /api/v1/import/jobsReturns a list of all import jobs for your business, ordered by most recent.
Error Handling
Section titled “Error Handling”When records fail validation, the job still completes — valid records are imported and invalid records are reported in the errors array:
{ "status": "completed", "valid_rows": 98, "error_rows": 2, "errors": [ { "row": 15, "external_id": "CUST-015", "field": "email", "message": "Invalid email format", "value": "not-an-email" }, { "row": 42, "external_id": "CUST-042", "field": "lifecycle_status", "message": "Invalid enum value. Expected 'lead' | 'prospect' | 'customer' | 'former_customer' | 'inactive'", "value": "deleted" } ]}Cross-Entity References
Section titled “Cross-Entity References”When importing entities that reference other entities (e.g., a proposal that references a customer), use *_external_id fields. The import system resolves these to internal UUIDs automatically.
The referenced entity must have been imported first with a matching external_id and source value.
{ "source": "airtable-migration", "records": [ { "external_id": "PROP-001", "customer_external_id": "CUST-001", "opportunity_external_id": "OPP-001", "proposal_number": "P-10001", "status": "approved", "final_price": 8500.0 } ]}Import Order
Section titled “Import Order”Import entities in tier order. The API validates that referenced entities exist:
- Products, Territories, Brands
- Customers, Team Members, Crews, Commission Plans, Promotions
- Deals, Locations, Brand Assignments
- Proposals, Proposal Items, Jobs, Job Items
- Invoices, Warranties, Proposal Discounts
- Transactions, Commissions
Get Templates and Validation Rules
Section titled “Get Templates and Validation Rules”# List all importable entity typesGET /api/v1/import/entities
# Download CSV template for an entity typeGET /api/v1/import/templates/{entity_type}
# Get validation rules for an entity typeGET /api/v1/import/validation/{entity_type}