Skip to main content

Importing Data

The Import API allows you to bulk import data into Terros. Use CSV files for most entity types and KML or KMZ files for areas. Imports are asynchronous — you initiate an import, upload a file, then poll for status until processing completes.

Supported Entity Types​

You can import the following entity types:

  • Account — customer accounts
  • Area — areas from KML or KMZ polygons
  • CalendarEvent — calendar events
  • User — users in your organization
  • Task — tasks assigned to users
  • Recruit — recruit records

File Limits and Sizing​

  • Columns: CSV imports support at most 500 columns. The server checks the original header count before applying columnMapping, so unmapped and unused columns still count toward this limit. Remove unused columns before uploading.
  • Account coordinates: For account imports, a file is rejected if more than 5,000 rows are missing latitude or longitude. This counts rows missing either coordinate, not all rows in the file. Supply both coordinates for large account imports; disableGeocoding does not bypass this validation.
  • Areas: A KML or KMZ file can contain at most 50,000 polygons. The import fails if no polygons are found.

Choose file sizes that make uploads, validation errors, and retries manageable. Splitting a file can make troubleshooting easier, but does not increase processing parallelism.

Parallel Processing and Completion Time​

Terros reads each uploaded file, splits its records into smaller batches, and sends them to a shared processing queue for the import type. Terros controls worker parallelism across imports using that queue. Splitting the same data into multiple files, or submitting more files at once, does not create additional worker capacity or bypass the queue's concurrency controls.

Choose file boundaries for manageable uploads and troubleshooting, rather than to increase worker throughput. End-to-end completion time can vary with upload and parsing overhead, queue load, record complexity, location lookups, and retries; one large file and several smaller files are not guaranteed to finish in identical wall-clock time. Use Get Import to monitor each import until it reaches a terminal status.

Import Flow​

Importing data is a three-step process:

Step 1: Initiate the Import​

Call Add Import to create an import job. The response includes a pre-signed S3 URL for uploading your file. Set entity to Area for a KML or KMZ import.

curl -X POST "https://api.terros.com/import/add" \
-H "Content-Type: application/json" \
-H "Authorization: ApiKey $TERROS_API_KEY" \
-d '{
"entity": "Account",
"name": "Q1 Account Import"
}'

Response:

{
"type": "success",
"url": "https://s3.amazonaws.com/terros-imports/imports/Import:abc123/upload.csv?X-Amz-Signature=...",
"importData": {
"importId": "Import:abc123",
"entity": "Account",
"name": "Q1 Account Import",
"importStatus": "New"
}
}

Save the importId from the response — you will need it to check status and fetch errors.

Step 2: Upload the File​

Upload your file to the pre-signed URL returned in Step 1 using a PUT request. The upload URL is valid for 1 hour — you do not need to construct the path yourself, just upload directly to the URL returned by the API. Use text/csv for CSV imports and application/octet-stream for area imports.

curl -X PUT \
-H "Content-Type: text/csv" \
--data-binary @accounts.csv \
"https://s3.amazonaws.com/terros-imports/imports/Import:abc123/upload.csv?X-Amz-Signature=..."

For an area import, initiate the job with "entity": "Area", set UPLOAD_URL to the response's url value, then upload a .kml or .kmz file:

curl -X PUT \
-H "Content-Type: application/octet-stream" \
--data-binary @areas.kmz \
"$UPLOAD_URL"

The importer creates areas from polygon placemarks. A KMZ archive must contain a KML file.

Once the file is uploaded, processing begins automatically. The import transitions through these statuses:

StatusDescription
NewImport created, waiting for file upload
UploadedFile received, queuing records
QueuedRecords enqueued for processing
ProcessingRecords are being processed
PostProcessingFinalizing results
CompletedImport finished successfully
FailedImport failed — check reason field for details

Step 3: Poll for Status​

Call Get Import to check the current status of your import:

curl -X POST "https://api.terros.com/import/get" \
-H "Content-Type: application/json" \
-H "Authorization: ApiKey $TERROS_API_KEY" \
-d '{ "importId": "Import:abc123" }'

Response:

{
"type": "success",
"log": {
"importId": "Import:abc123",
"entity": "Account",
"importStatus": "Completed",
"successes": 150,
"errors": 3,
"totalMessages": 153,
"completedAt": 1772665914046
}
}

Poll until importStatus is Completed or Failed. We recommend using exponential backoff (e.g. 5s, 10s, 20s, 40s) to avoid unnecessary requests for larger imports that take longer to process.

Fetching Errors​

If the import has errors (errors > 0), call Get Import Errors to download the error details:

curl -X POST "https://api.terros.com/import/errors" \
-H "Content-Type: application/json" \
-H "Authorization: ApiKey $TERROS_API_KEY" \
-d '{ "importId": "Import:abc123" }'

Response:

{
"type": "success",
"records": [
{
"key": "imports/Import:abc123/errors/row-42.json",
"signedUrl": "https://s3.amazonaws.com/terros-imports/errors/row-42.json?X-Amz-Signature=..."
}
]
}

Each record contains a signedUrl — a temporary download link for the error file, valid for 12 hours. Download these files to see which rows failed and why.

Import Options​

When initiating an import, you can pass these optional fields:

FieldTypeDefaultApplies ToDescription
entitystring—AllRequired. The entity type to import
namestring—AllA friendly name for the import
disableGeocodingbooleanfalseAccount, CalendarEvent, RecruitSkip location resolution for addresses
addMissingUsersbooleanfalseAccount, Recruit, UserAutomatically create users if they don't exist
createCustomLocationsbooleanfalseAccount, CalendarEvent, RecruitCreate locations for new addresses
notifyUsersbooleanfalseUserSend welcome emails to newly imported users
workflowIdstring—AccountWorkflow to apply to imported records
columnMappingobject—CSV importsMaps CSV column names to the expected header names the parser should read them as

Listing Imports​

Call List Imports to see all imports for your company:

curl -X POST "https://api.terros.com/import/list" \
-H "Content-Type: application/json" \
-H "Authorization: ApiKey $TERROS_API_KEY" \
-d '{}'

Cleanup​

After an import completes, you can call Cleanup Import to remove temporary data. Do not invoke cleanup while an import is still in progress — wait until the status reaches Completed or Failed.

curl -X POST "https://api.terros.com/import/cleanup" \
-H "Content-Type: application/json" \
-H "Authorization: ApiKey $TERROS_API_KEY" \
-d '{ "importId": "Import:abc123" }'