Imports and API reference
Who this is for: admins who load data regularly, and anyone building an integration. New to uploads? Start with Import your deals.
Only admins can import, by upload or by API. While editing is paused for a billing problem, imports are refused too.
Three ways in
| Route | Where | Best for |
|---|---|---|
Upload a CSV or .xlsx file | The Bulk Upload button on Sales Data, People, Teams or Payouts | One-off loads and corrections |
| Add one entry by hand | Data → Sales Data → Add Entry | A single missing deal |
| API | Your own scheduled job calling Earnest with an API key | Recurring syncs from a CRM or warehouse |
The Data → Imports & API page links to the three non-deal upload pages ("People & Compensation", "Teams & Titles", "Payout Adjustments") and holds the API reference and key. Deals are uploaded from Data → Sales Data.
Upload dialogs and limits
| Dialog | Where | Max rows | Max size |
|---|---|---|---|
| Import Sales Data | Data → Sales Data → Bulk Upload | 1,000 | 4 MB |
| Bulk Upload People | Organization → People → Bulk Upload | 1,000 | 4 MB |
| Bulk Upload Teams | Organization → Teams → Bulk Upload | 5,000 | 4 MB |
| Bulk Adjustment Upload | Payouts → Bulk Upload | 5,000 | 4 MB |
Over the row limit: "File has 1,234 rows. Maximum is 1,000 rows per upload." Over the size limit: "That file is 5.2MB. The maximum is 4MB — split it and upload in parts." The Sales Data and People dialogs allow five uploads a minute per person ("Too many uploads. Please wait a minute before trying again.").
Excel workbooks
.xlsx files are converted to CSV in your browser before upload. Earnest reads the first sheet
and names any it skipped (Read the first sheet, "Reps". Ignored: …). Row 1 must hold the headers.
- Cells come through as Excel stores them:
$1,500.00becomes1500,8%becomes0.08, and a date cell becomes2026-01-15whatever your locale. A formula contributes its last calculated value. .xls,.xlsm,.xlsb,.xlt,.xltx,.xltm,.odsand.numbersare refused: "Save the file as .xlsx or .csv and try again."- A password-protected workbook is refused with "That workbook is password-protected or in the legacy .xls format. …"
- A blank or title row above the headers is refused with "We couldn't find the column names in that workbook. Row 1 of the first sheet has to hold them …"
Header matching
Headers are matched ignoring capitals, spaces and punctuation, and a trailing suffix like "(USD)". So Reference Number, reference_number and referenceNumber all match the
same column.
Common CRM names map automatically:
| Earnest column | Also recognised |
|---|---|
referenceNumber | Opportunity ID, Deal ID, Order Number, Invoice Number, Contract Number, Reference, Ref, External ID, ID |
userEmail | Owner Email, Opportunity Owner Email, Sales Rep Email, Email, Opportunity Owner, Owner |
bookingDate | Close Date, Closed Won Date, Booked Date, Signed Date, Order Date, Invoice Date, Date |
amount | Deal Amount, Opportunity Amount, ACV, ARR, TCV, MRR, Revenue, Value, Price |
quantity | Qty, Units, Meetings, Volume, Count |
currency | Currency ISO Code, Currency Code, Deal Currency |
planName | Commission Plan, Comp Plan, Plan |
splitEmail | Split Owner Email, Split Rep Email, Secondary Owner Email |
splitPercent | Split Percentage, Split Share, Split |
The Column Mapping step lets you override any match and previews the first 10 rows. An unmapped required column shows "Missing required columns: …" and disables the import. If some rows have an unclosed quote or a stray comma, you're warned that "Cells on those rows may have shifted into the wrong columns."
Sales data columns
The Import mode: switch (Revenue or Units) appears only if your organization has a unit plan.
| Mode | Required | Optional |
|---|---|---|
| Revenue | referenceNumber, userEmail, amount, currency, bookingDate | quantity, unitLabel, splitEmail, splitPercent, planName |
| Units | referenceNumber, userEmail, quantity, bookingDate | unitLabel, amount, currency, splitEmail, splitPercent, planName |
The dialog's Expected Columns card lists every column for the mode; the optional ones start with "Optional." Download a template gives you a file with the required headers and an example row.
| Column | Rules |
|---|---|
referenceNumber | Your unique deal ID. See Reference numbers and updates. |
userEmail | Must belong to someone already in Earnest ("User not found: …"). Uploads never create people. |
amount | Revenue mode: required and greater than 0 ("Amount must be a positive number, got 0"). Units mode: optional, any number. Plain numbers only: "1,200" fails with Amount must be a number, got "1,200". |
currency | Three-letter ISO 4217 code. The column must be mapped in Revenue mode, but a blank cell means USD. Errors: Invalid currency code "X" — must be 3 letters (e.g. USD, EUR) and Unknown currency code "XYZ" — not a recognised ISO 4217 currency. |
bookingDate | Decides which month the deal counts in. See Dates. |
quantity | Units mode: required, greater than 0, fractions allowed. Revenue mode: optional, but an unreadable value fails the row. |
unitLabel | A display label such as Meetings. Up to 100 characters. |
planName | For a rep whose team splits pay across plans: the plan this deal counts toward. Case-insensitive; an unknown name fails with "Unknown plan: X". See Comp plan reference. |
splitEmail, splitPercent | Share credit with a second rep. See Credit splits. |
A deal in a different currency from the rep's quota currency is accepted and converted when commission is calculated. See Multi-currency.
Dates
| Accepted | Refused |
|---|---|
2026-03-15, 2026/03/15 | 15/03/2026 (day first) |
03/15/2026, 3/15/26 | 15.03.2026 |
Mar 15, 2026, 15-Mar-2026, 15 March 2026 | 20260315, Excel serial numbers, Q1 2026 |
- Slashed dates are read month first.
03/04/2026is March 4, 2026. A day-first file whose days are all 12 or under imports without an error, on the wrong dates. Convert day-first columns toYYYY-MM-DD. - An unreadable date fails with
Invalid booking date: "x" — use YYYY-MM-DD. - Dates must fall between 2000 and 2100: "bookingDate out of range — must be between 2000 and 2100 (got 1970-01-01). A value like 1970-01-01 or 9999-12-31 usually means the source system substituted a placeholder for a missing date."
- The booking date must fall on a day the rep holds a position. See Refusal messages.
Reference numbers and updates
referenceNumber is the update key within your organization.
- Already in Earnest: the row updates that deal's amount, currency, date and rep. Its plan tag changes only when the row supplies a plan. The result reads "N succeeded — X added, Y already in your account and updated".
- Repeated within one file: the first row is kept; each repeat fails with
Duplicate referenceNumber "X" in this batch — row N already used it. … - Starting with
=,+,-or@, or longer than 100 characters: refused, not rewritten: "referenceNumber cannot be used as an idempotency key because sanitization rewrites it … prefix credit memos as "CM-1001" rather than "-1001"." - Already in a published payout: refused with "This order is frozen into a published payout period and cannot be moved or modified. Post a correction in the current open period instead."
- Changing a reference creates a new deal and leaves the old one in place.
Credit splits
A split gives a second rep credit for the same deal. The deal's owner keeps 100%; the split rep
gets their own credit at splitPercent. Credits are independent, so the total can exceed 100%
(overlay or SE credit, for example).
In an upload:
- Fill both
splitEmailandsplitPercent. One split partner per row. splitPercentis a percentage:50means 50%.0.5means half of one percent.- It must be above 0 and at most 100: "Invalid split percent: must be greater than 0 and at most 100".
- You can't split with the owner ("Cannot split credit with the same user"), and the split rep must exist ("Split user not found: …").
- When both split columns are mapped, a row with blank split cells removes that deal's existing splits. Re-uploading a split replaces the previous one.
To edit splits by hand, click a deal's split icon on Data → Sales Data. The Credit Splits dialog takes several reps (Add Rep, Save Splits). "The total can be above or below 100% (e.g. 200% for double comp), but no single rep can be over 100%." All reps on a split must be measured the same way (revenue or units).
Changing splits for a month that already has drafts puts those drafts out of date; regenerate before publishing.
Refusal messages
Each refused row is skipped with a reason; the other rows still import.
| Situation | Message |
|---|---|
| Before the rep's start date | "Booking date cannot be before the rep's start date (Mar 1, 2026)." |
| After the rep's last position ended | "Booking date cannot be after the rep's last position ended (…) - a deal is paid only on a day the rep holds a position. …" |
| Between two positions | "The rep holds no position on … - a deal is paid only on a day the rep holds a position. …" |
| Split rep has no position that day | "Split recipient … holds no position on … — their share would never be paid. …" |
| Month already published | "Published payout exists for Mar 2026 — cannot ingest activity into finalized period." |
| A manager above the rep has published | "A manager above this rep has already published Mar 2026 — cannot ingest activity into that finalized period." |
| Split rep's month published | "Split recipient … has a published payout for Mar 2026 — cannot add a split credit to a finalized period." |
| Missing reference | "Order number is required" |
If the database rejects a write partway through, nothing from the file is saved: "Import aborted at row N before commit — no rows were written. …" Fix that row and upload the whole file again.
Adding, editing and deleting entries
Add Entry opens Add Sales Entry: Sales Rep, Reference, Plan (only for reps whose
pay is split across plans), Amount with a Currency list of ten codes (USD, EUR, GBP, SGD, JPY,
CHF, AUD, CAD, INR, CNY) or Quantity for unit reps, and Booking Date. The amount must be
greater than 0. A reference that already exists is refused (Reference "X" already exists), and a
closed month gives "Cannot create activity in Mar 2026 - that period is closed (a payout was
finalized). …"
There is no edit for an entry's amount, date or rep. Re-upload the same reference, or delete and re-add it.
To delete, tick entries and click Delete (N). Entries in a published month are skipped and listed with a reason, such as "Has published payout for Mar 2026". Deleting an entry a draft used puts that draft out of date.
The Sales Data list shows one month at a time, with filters for Rep and Payout Status and a "Search by reference..." box. It loads the latest 1,000 entries for the month.
People Bulk Upload
Organization → People → Bulk Upload opens Bulk Upload People. Choose OTE Mode or Commission Rate Mode first; it decides which pay column the file uses.
| Column | Required | Notes |
|---|---|---|
email | Yes | Must match your company domain: "Email domain @x does not match your company domain (@y)." |
name | Yes | Full name. |
role | No | REP, MANAGER or ADMIN in any casing; blank means REP. Any other value is flagged in the preview and the row is not imported. |
teamName | No | Created if new. |
titleName | No | Must already exist on that team, and needs teamName on the same row. |
managerEmail | No | The manager can be anywhere in the file. An unknown address is skipped and the person is added without a manager. |
currency | No | Quota currency, any ISO code. Default USD. |
payoutCurrency | No | Default: same as currency. |
baseSalary | No | Annual. Can't exceed OTE ("Base salary cannot be greater than OTE."). |
quota | No | Annual; each pay period takes its share. |
startDate | No | YYYY-MM-DD; blank means today. A mid-month date prorates the first period. |
ote or commissionRate | No | Depends on the mode. commissionRate is a decimal: 0.08 is 8%. |
sendInvite | No | yes or no; blank follows your Settings default. |
This upload only creates people. An email already in Earnest fails with "User already exists"; to change someone, use Edit Position. See People and teams reference. Seat limits apply to reps and managers; admins are free.
Teams and adjustments uploads
Bulk Upload Teams takes name (required), description, planName (must exist) and
seasonQ1 to seasonQ4 (quarterly weights, default 0.25 each).
Bulk Adjustment Upload applies to the month selected on Payouts. Columns, all required: email,
type (REVENUE or FIXED_BONUS), amount (non-zero) and reason. FIXED_BONUS amounts are in each
rep's payout currency; REVENUE amounts are in their quota currency (a count on a unit plan). See
Payout pipeline reference.
The import error digest
Failed rows from uploads and the API are collected and emailed to your admins once a day. There's no in-app list of past failures, so read each upload's result before closing the dialog.
The API
Your API key
On Data → Imports & API, click Generate API Key. The key is shown once; copy it then. Later the page only says "API key is set. Regenerate to see the new key." Your organization has one key.
- Regenerate issues a new key. The old one stops working as soon as you confirm "Regenerate the
API key?"; integrations using it get
401until updated. - Revoke disables the key with no replacement ("Revoke the API key?").
Send the key in the x-api-key header.
Endpoints
| Endpoint | POST | PUT |
|---|---|---|
/api/ingest/bigquery/activities | One deal, body up to 1 MB | { "rows": [...] }, up to 5,000 rows and 5 MB |
/api/ingest/bigquery | One person, body up to 1 MB | { "compensationMode", "periodDivisor", "rows": [...] }, up to 5,000 rows and 5 MB |
/api/ingest/bigquery/orders is an older alias for /activities. Each key may make 100 requests a
minute.
curl -X POST https://earnestcomp.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"}'
Deal fields
The same as the Sales data columns, with these differences:
amountcan be negative, for credit memos and clawbacks. It's optional whenquantityis sent.currencyis optional and defaults to USD.planIdorplanNametags a plan;planIdwins if both are sent.- There are no split fields. Set splits in the app.
- Dates should be ISO (
2026-03-15); the same range and position rules apply.
People and compensation fields
Required: email, effectiveDate, quota, baseSalary, and ote (OTE mode) or commissionRate
(Commission Rate mode). Optional: name, title, teamName, managerEmail, role, currency,
payoutCurrency, periodDivisor.
- An unknown email creates the person; an unknown team is created.
- Each record sets the person's position from
effectiveDate, kept to the exact day. - Leaving out
title,teamName,managerEmailorrolekeeps the current value. roletakes REP or MANAGER. ADMIN is refused ("ADMIN role assignment requires session-based authentication."); other values fail.periodDivisoris how often the rep is paid: 12 for monthly (default), 4 for quarterly. "It does not scale quota, OTE or base salary — those are always annual."- Emails must match your company domain.
- A position change in a published period is refused with
409and nothing is applied.
Responses
| Status | Meaning |
|---|---|
| 200 | Done. A PUT returns one result per row, plus "Processed N activities: X succeeded, Y failed". |
| 400 | "Validation failed", including the date-range and position refusals |
| 401 | Missing or wrong key |
| 404 | "User not found" (single deal) |
| 409 | "Published period conflict" or "Split credit conflict" |
| 413 | Body too large |
| 429 | Rate limit; the error says when to retry |
| 500 | "Batch aborted before commit; no activities were written." Re-send the whole batch. |
Failed rows also go to the daily digest.
Retries and idempotency
Deals update by referenceNumber and people by email, so re-sending a batch is safe. Keep
references stable: a reference that changes creates a duplicate deal.
Syncing from BigQuery or another warehouse
Earnest has no built-in warehouse connector; "bigquery" is only part of the endpoint path. Schedule
your own job (a scheduled query plus a cloud function, an ETL tool, or a cron script) that reads
new and changed deals and PUTs them in batches of up to 5,000. Sync people before their deals, and
treat 409 responses as deals that belong in a correction.
FAQ
What happens if I import the same deal twice? It updates in place, unless it's already in a published payout (refused) or repeated within the same file (repeats refused).
Can I upload compensation changes in a spreadsheet? No. People Bulk Upload only creates people. Change an existing person with Edit Position, or send the change through the people API.
Why did a zero-amount deal fail? Uploads and Add Entry need amounts above 0. Only the API accepts zero or negative amounts.
Can I import deals dated in the future? Yes. They count in the month of their booking date. Add Entry warns "This booking date is in the future."
My import failed on a published month. What now? Published months are frozen. Post the change forward as a correction; see Corrections and clawbacks.
Can managers or reps import? No. Every upload and the API key are admin-only.