Documentation
Overview
Every commission line moves through the same pipeline regardless of how it arrived:
An import (a CSV upload or an API pull) creates one Statement Import covering a supply partner and a date range. Each row in the source file/response becomes one Raw Statement Line, normalized to a common shape. Reconciliation then tries to attribute each line to a demand-side organisation/container and compute its commission split — lines it can't confidently attribute are flagged as anomalies instead of silently guessing.
| Import status | Meaning |
|---|---|
| pending | Received, not yet processed |
| ingesting | Worker is parsing the file / API response |
| ingested | Lines extracted, not yet reconciled |
| reconciling | Matching against demand partners in progress |
| reconciled | All lines matched (or flagged as anomalies) |
| failed | Hard error during processing — check the import's error message |
| cancelled | Manually cancelled by an ops user |
Importing data
There are two ways to get commission data in, and every supply partner uses exactly one depending on its connector type.
A. CSV / Excel upload
On the Imports page, click Upload Statement, pick the
supply partner, set the period the file covers, and drop a .csv or .xlsx
file (max 50MB). The file is parsed using that partner's configured
field mapping — a dictionary of raw column name → normalized field set
up when the partner's connector was configured. Supported normalized fields:
| Normalized field | Required? | Notes |
|---|---|---|
| transaction_date | Yes | Accepts YYYY-MM-DD, DD/MM/YYYY, MM/DD/YYYY, DD-MM-YYYY, or either of those with a THH:MM:SS/ HH:MM:SS time suffix |
| click_date | No | Same accepted formats as transaction_date |
| external_transaction_id | Recommended | Missing it is flagged as an anomaly — it's also half of the idempotency key, so duplicate-upload protection is weaker without it |
| external_order_id | No | The merchant/order reference, if the network provides one |
| gross_amount | Yes | Decimal string, e.g. "12.50" — never pre-multiplied into minor units |
| commission_amount | Yes | Decimal string, same rule as gross_amount |
| currency | Yes | ISO 4217 code (EUR, USD, …) |
| container_id | Yes* | The affiliate-network identifier that maps to a demand partner. *Or derived from a combined sub-id column — see below |
| tracking_org_id | No | Secondary attribution field, or derived from the same combined sub-id column |
| merchant_name | No | Advertiser / program display name |
| product_category | No | Free text |
| status | No | Whatever status string the source uses (approved/pending/declined/…) — passed through as-is |
Combined sub-id columns: some networks put both the container and tracking-org
reference in a single column (e.g. "container123|org456"). Set
sub_id_separator (default |),
container_index and org_index in that
partner's connector config instead of mapping separate columns.
B. API pull
Supply partners with an API-type connector (Awin, Impact.com, Commission Junction, TravelPayouts, Tapfiliate, Rakuten, or a custom REST API) fetch directly — no file involved. Credentials must be saved first via Configure API on the Supply Partners page, then either trigger a one-off pull with Fetch Now (pick a date range) or let the scheduled daily job handle it automatically. Use Test Connection after saving credentials to confirm they're valid before the first real fetch.
| Connector | Required credentials |
|---|---|
| Awin API | Publisher ID, API Key |
| Impact.com API | Account SID, Auth Token (HTTP Basic) |
| Commission Junction API | Personal Access Token, Publisher CID — optionally Advertiser IDs to scope a per-merchant partner to one advertiser instead of the whole account feed |
| TravelPayouts API | API Token (sent as X-Access-Token) |
| Tapfiliate API | API Key |
| Rakuten API | API Token, Publisher ID |
| Custom API | Base URL, API Key |
Fields marked as secret in the Configure API modal (tokens/keys) are stored write-only — the form shows a placeholder, not the saved value, and leaving it blank on save keeps the existing credential.
Defining new supply partners
On Supply Partners, click + New Partner and fill in:
| Field | Required? | Notes |
|---|---|---|
| Name | Yes | Display name shown throughout the UI |
| Slug | Yes | Unique, stable identifier — used in idempotency keys, so avoid changing it once imports exist |
| Legal Name | No | Used on invoices/credit notes if different from Name |
| Affiliate Network | No | Informational grouping (Awin, Impact, Commission Junction, …) — doesn't affect processing |
| Connector Type | Yes | Determines whether this partner imports via CSV upload or API pull, and which fields Configure API asks for |
| Currency | Yes | Default currency assumed when a file/response doesn't specify one |
| Country Code | Yes | ISO 2-letter code, used for VAT treatment |
| Payment Terms (days) | No | Defaults to 30 — informational, used for AR aging |
Picking an API connector type immediately opens Configure API so credentials can be entered right away. For a CSV connector, set up the field mapping via PUT /api/v1/admin/supply-partners/{id}/connector-config (see API Docs) before the first upload — the mapping isn't editable from this UI yet.
Assigning tracking hashes
A tracking hash is whatever per-container tracking value a connector carries through from the affiliate link/widget — the platform's own MD5-style hash, or a network-specific equivalent (CJ's websiteId, Impact's SubId1, TravelPayouts' sub_id). Every connector that supports one resolves it the same way, in this order:
- Tracking hash cache — a mirror of the platform's own product_tracking_hashes table, kept fresh automatically. This is the primary source and always wins when present.
- Manual assignment — hashes ops has assigned by hand in this UI. Used only when the cache has no entry.
- Default fallback — unattributed lines fall back to the Ancillarate-Playground org/container so they're never silently dropped, and get flagged unresolved_hash instead.
The Tracking Hashes page lists every hash currently falling through to the fallback — supply partner, how many lines it's affected, when it was first seen, and an example amount. Click Assign, pick the organisation (optionally a product ID), and confirm — this resolves every current and future line carrying that hash automatically, not just the ones already imported. The same assignment flow is also reachable per-line from the Import Issues page.
Resolving import issues
After ingestion, reconciliation flags any line it can't confidently process instead of guessing. Flagged lines show up on that import's Import Issues page (reachable from the Imports list) with one or more of these anomaly types:
| Anomaly | Meaning | How to resolve |
|---|---|---|
| missing_mapping | No demand partner found for this line's container ID | Click Assign Partner and pick the correct organisation — resolves just this line |
| unresolved_hash | The line's tracking hash isn't in the cache or a manual assignment | Click Assign Hash — see Assigning tracking hashes above; resolves this and every future line with the same hash |
| duplicate | A line with the same idempotency key (supply partner + external transaction ID + date) already exists | Informational — the line was already imported once, no action needed |
| amount_outlier | Commission amount is unusually large relative to this partner's history | Review manually before approving the settlement that includes it — could be correct, could be a source-file error |
| negative_amount | Negative commission — typically a reversal or clawback | Expected behavior for corrections; verify it nets against the original line |
| unknown_currency | Currency code isn't a recognised ISO 4217 code | Check the source file's currency column, or the connector's default_currency config |
| missing_transaction_id | No external transaction ID on the line | Check the source file / field mapping — without it, duplicate-import protection is weaker for that line |
Data structure reference
Whatever the source (CSV or API), every line is normalized to the same shape before reconciliation ever sees it. If you're setting up a new CSV field mapping or debugging why a connector's data isn't matching, this is the target shape:
| Field | Type | Notes |
|---|---|---|
| idempotency_key | string | Deterministic: sha256(supply_partner_id + ":" + external_transaction_id + ":" + transaction_date) — same inputs always produce the same key, which is what makes re-uploading the same file a no-op instead of double-counting |
| raw_container_id / raw_tracking_org_id | string | null | Matched against demand partners during reconciliation — a miss here is a missing_mapping anomaly |
| gross_amount_minor / commission_amount_minor | integer | Always minor units (pence/cents), never a float — a source value of "12.50" becomes 1250 |
| currency | string | ISO 4217. Zero-decimal currencies (JPY, KRW, VND) and three-decimal currencies (KWD, BHD, OMR) are converted correctly, not just ×100 |
| transaction_date | date | Required — the booking/action date, used for period grouping and FX rate lookup |
| tracking_hash / product_id / experiment_id / variant_key | string | null | Populated by connectors that resolve a tracking hash — see Assigning tracking hashes |
| is_default_attribution | boolean | true when a line fell through to the default org/container rather than a real match |
| status | string | null | Passed through from the source as-is (network status vocabularies differ — not normalized to a fixed set) |