Help CenterData & Imports
Data & ImportsUpdated

Data Imports & Validation

Earnest takes data two ways: CSV upload in the admin UI, and a REST API for programmatic ingestion from a warehouse, CRM or scheduled job. Both share the same validation rules.

Everything below is available under Admin → Data → Imports & API, which also shows your live endpoint URLs and a ready-made curl example.


CSV imports

Uploads are capped at 10 MB and 1,000 rows per file. Larger sets should be split, or sent through the API, which takes bigger batches.

The importer matches your header row case-insensitively and ignores spaces and underscores, so Reference Number, reference_number and referenceNumber all map to the same field. You get a column-mapping preview before anything is written.

1. Compensation

Loads or updates the roster with quotas, OTE and base salaries. Pick a compensation mode first:

  • OTE mode — you supply ote, Earnest derives the commission rate.
  • Commission Rate mode — you supply commissionRate, Earnest derives OTE.
OTE modeCommission Rate mode
Requiredemail, effectiveDate, quota, baseSalary, oteemail, effectiveDate, quota, baseSalary, commissionRate
Optionalname, title, role, teamName, commissionRate, currency, frequencyname, title, role, teamName, ote, currency, frequency
ColumnNotes
emailMust be a valid email address. Matches an existing user or creates one.
effectiveDateThe date this compensation takes effect. Normalized to the 1st of the month.
quotaAnnual quota. Must be greater than 0.
baseSalaryAnnual base salary. Must be 0 or more.
ote / commissionRateWhichever your mode requires. commissionRate is a decimal — 0.10 for 10%.
roleADMIN, MANAGER or REP. Defaults to REP.
teamNameCreates the team if it does not exist.
currencyISO 4217, three letters. Defaults to USD if blank.

2. People

A lighter roster import for adding users without compensation details.

  • Required: email, name
  • Optional: role, teamName, managerEmail, currency, ote, baseSalary, commissionRate, quota

managerEmail is what builds your reporting hierarchy — it matters for manager rollup quotas and for RATIO plans, whose denominator is the manager's subtree.

3. Activities / orders

Deals, bookings, meetings — anything that counts toward quota. The required columns depend on whether you are importing against a Revenue or a Unit plan.

RevenueUnit
RequiredreferenceNumber, userEmail, amount, currency, bookingDatereferenceNumber, userEmail, quantity, bookingDate
Optionalquantity, unitLabel, splitEmail, splitPercent, planNameunitLabel, amount, currency, splitEmail, splitPercent, planName
ColumnNotes
referenceNumberUnique within your organization. Re-importing the same reference updates the existing record rather than duplicating it.
userEmailMust match a user who already exists. Import people before activities.
bookingDateDetermines which period the activity falls into, and which FX rate applies.
amountRequired for Revenue. Must be 0 or more.
currencyRequired for Revenue imports — three-letter ISO 4217.
quantityRequired for Unit. May be fractional.
planNameTags the activity to a specific comp plan. Only relevant for reps on a team plan mix. Untagged activities fall to the mix's default plan.
splitEmail / splitPercentShare credit with a second rep — see below.

Validation

Every row is validated before any write. A failing row is skipped with a specific message; passing rows still import. You get a per-row results summary at the end, and persistent import errors are recorded so they can be reviewed later.

CheckRule
Email formatMust be a valid address, or the row is skipped.
Currency codeThree letters. Invalid currency code "X" — must be 3 letters (e.g. USD, EUR). Blank defaults to USD.
Positive numbersquota must be greater than 0.
Non-negative numbersamount and baseSalary must be 0 or more.
Whole numbersFields that must be integers are rejected with the value that failed.
DatesMust be parsable. ISO 8601 (2026-04-15) is safest.
String sanitizationNames, titles and reference numbers are stripped of control characters and truncated to safe lengths.
Unknown userActivity rows whose userEmail has no matching user fail.
Published periodsA row landing in a period that already has a published payout is rejected — published periods are frozen.
Position windowsAn activity outside the rep's position dates can never be paid. Import will take it, but it will hold up publishing until the position is extended or the activity removed.

Currency mismatches

If an activity's currency differs from the rep's quota currency, the row is accepted and converted at calculation time using the rate for its booking date. Nothing needs pre-converting. See Multi-Currency Explained.


Credit splits

Splits let two reps share credit for one activity.

  1. The primary rep (userEmail) always receives 100% credit.
  2. The split rep (splitEmail) receives their own independent credit at splitPercent.

Splits are independent, not zero-sum. Credits across one activity can total more than 100%. That is intentional — it reflects overlay credit, SE credit and management rollups, where the same deal legitimately counts for more than one person at full or partial value.

Rules: splitPercent must be greater than 0; you cannot split with the activity's own owner; the split rep must already exist; and re-importing a split replaces the existing split configuration for that activity.

Splits can also be edited directly from Admin → Data → Sales Data without re-importing.

Changing splits on a period that already has a draft payout makes that draft stale — the amounts no longer match what the reps earned. Regenerate before publishing.


API ingestion

Generate an API key under Admin → Data → Imports & API. Authenticate with the x-api-key header.

EndpointPurpose
/api/ingest/bigqueryUsers & compensation
/api/ingest/bigquery/activitiesSales / activity data
/api/ingest/bigquery/ordersLegacy alias for /activities — existing pipelines keep working
MethodBody
POSTA single record.
PUTA batch, up to 5,000 rows per request.
curl -X POST https://your-org.example.com/api/ingest/bigquery/activities \
  -H "Content-Type: application/json" \
  -H "x-api-key: YOUR_KEY" \
  -d '{"referenceNumber":"DEAL-001","userEmail":"rep@co.com","amount":25000,"currency":"USD","bookingDate":"2026-03-15"}'

The compensation endpoint takes email, effectiveDate, quota, baseSalary, and either ote or commissionRate — plus periodDivisor (12 for monthly, 4 for quarterly) to say how the annual figures split across periods.

Behaviour

  • Upsert — users match on email, activities on reference number. Nothing duplicates.
  • Auto-provisioning — unknown users, teams and titles are created as needed.
  • Domain enforcement — user emails must match your organization's verified domain(s).
  • Rate limiting — requests are throttled to prevent accidental overload.
  • Import errors are logged — failures are recorded per row and summarized in a periodic digest.

CSV or API?

ScenarioUse
One-time roster setupCSV
Recurring deal sync from a warehouseAPI (PUT)
Ad-hoc correctionsCSV, or the Sales Data screen
Scheduled pipelineAPI
More than 1,000 rows at onceAPI

Tips

  1. Import people before activities — activity rows need their rep to exist.
  2. Use ISO 8601 dates.
  3. Keep reference numbers stable — they are the update key. Changing one creates a second record.
  4. Check the results summary — it names every skipped row and why.
  5. Regenerate affected drafts after a corrective import, or publishing will be held.

Frequently asked questions

Q: What happens if I import the same activity twice? It updates in place. referenceNumber is the unique key within your org.

Q: What is the file size limit? 10 MB and 1,000 rows per CSV upload. The API takes 5,000 rows per batch request.

Q: Can I import activities dated in the future? Yes, but they only count toward the period containing their booking date, and you cannot draft payouts for a future month.

Q: Can I delete imported data? Individual activities can be deleted from Sales Data. Deleting an activity that a draft payout already used marks that draft stale so it cannot be published with out-of-date figures.

Q: Does importing compensation overwrite existing users? It upserts. Names and currencies update in place; a new position is created only when the compensation actually changed — so your history stays intact and date-scoped. See Scheduled Changes & History.

Q: My import failed on a published period. That is the published-period guard. Published periods are frozen; post the change forward as a correction instead — see Corrections & Clawbacks.