# Uploading your first placements

A **placement** is a single overdue invoice you've turned over to ai-collect for collection. The platform accepts placements through four channels, all gated on your creditor API key:

1. **CSV upload** — `POST /v1/intake/csv/preview` then `POST /v1/intake/csv/commit`. Best for batches; up to 20 MB per file.
2. **File drop** — `POST /v1/intake/file`. Upload a PDF (≤ 10 MB) or image (≤ 5 MB) of an invoice; an LLM extracts the fields and creates a draft you confirm via `POST /v1/intake/file/{draft_id}/confirm`.
3. **Webhook** — `POST /v1/intake/webhook`, HMAC-SHA256-signed with your per-tenant secret (rotate via `POST /v1/intake/webhook/rotate-secret`).
4. **Direct connection** — automated from QuickBooks Online (private beta). See [Connecting your accounting software](/for-creditor-partners/creditor-onboarding/connecting-your-accounting.md). Placements show up tagged with the `accounting_sync` intake channel.

There is no dedicated "one at a time" dashboard form today; for individual placements use the CSV channel with a one-row file or the API.

This page covers CSV upload.

## Volume policy

These caps come from `src/settings.py` and apply on top of every channel:

| Cap              | Default                                                                                   | What happens at the limit                                                                                                         |
| ---------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| Unverified daily | 50 placements/day                                                                         | Excess placements are still accepted but flagged `volume_quota_exceeded`; the compliance engine refuses outreach on flagged rows. |
| Verified weekly  | 10,000 placements/week                                                                    | Same flag behaviour.                                                                                                              |
| Pile-on guard    | Same debtor (matched by lowercased company name) hit by ≥ 3 distinct creditors in 30 days | Every new placement against that debtor gets the safety flag.                                                                     |

A flagged placement doesn't fail intake — it just won't be worked until you remove the flag or the window passes.

## CSV format

The CSV parser fuzzy-matches column headers (lowercased, non-alphanumeric stripped) against a list of aliases, so a number of common spreadsheet conventions just work. The canonical columns and their accepted aliases:

| Canonical field              | Required?   | Accepted aliases                                                                                     | Notes                                                                                                                             |
| ---------------------------- | ----------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `debtor_business_name`       | Yes         | `debtor_name`, `company`, `company_name`, `customer`, `customer_name`, `bill_to`, `bill_to_name`     | The legal/operating name of the business that owes you.                                                                           |
| `debtor_contact_name`        | Recommended | `contact`, `contact_name`, `ap_contact`, `accounts_payable_contact`                                  | The AP person, if known.                                                                                                          |
| `debtor_email`               | Recommended | `email`, `email_address`, `ap_email`, `billing_email`, `customer_email`                              | RFC-shaped.                                                                                                                       |
| `debtor_phone`               | Recommended | `phone`, `phone_number`, `telephone`, `billing_phone`                                                | Normalised to E.164 internally.                                                                                                   |
| `amount_cents` *or* `amount` | Yes         | `amount_in_cents`; `balance`, `balance_due`, `total_due`, `total`, `invoice_total`, `invoice_amount` | Either is accepted. `amount` is converted to cents using the row's `currency` convention.                                         |
| `currency`                   | Recommended | `currency_code`, `ccy`                                                                               | Defaults to `USD` if missing. Affects how `amount` is parsed (US: `1,250.00`; EU `1.250,00` for currencies like EUR/GBP/CHF/SEK). |
| `original_invoice_id`        | Recommended | `invoice_number`, `invoice_id`, `invoice_no`, `invoice`, `inv`, `document_number`                    | Your invoice reference.                                                                                                           |
| `invoice_date`               | Recommended | `invoice_date`, `issued`, `issue_date`, `bill_date`                                                  | ISO `YYYY-MM-DD` is the safe default.                                                                                             |
| `due_date`                   | Recommended | `due_date`, `due`, `payment_due`                                                                     | Same date rules as `invoice_date`.                                                                                                |
| `notes`                      | Optional    | `notes`, `memo`, `description`, `comments`                                                           | Free text.                                                                                                                        |

> Date locales: the preview defaults to ISO. Slash- or dash-separated dates that could be US (`MM/DD/YYYY`) or EU (`DD/MM/YYYY`) are flagged as ambiguous in the preview so you can pick the right interpretation before commit. Two-digit years are rejected outright.

The platform doesn't require a separate debtor-state column; billing address is captured as an optional structured dict (mostly populated by the file-drop and webhook channels). State-of-debtor is one of the inputs to compliance evaluation, so for CSV uploads where it matters, include it in the address field on the row you send through the webhook or API.

## Uploading

1. **Placements → Upload** in the dashboard.
2. Drag your CSV into the upload box, or click **Browse**. The dashboard `POST`s to `/v1/intake/csv/preview` (Content-Type `text/csv`, capped at 20 MB).
3. The preview shows row-level parses plus warnings (e.g. `date_locale_ambiguous`). Unmapped headers are shown as such — you can pick a canonical field manually.
4. Fix any rows flagged as errors.
5. Click **Confirm upload**, which calls `/v1/intake/csv/commit` with the preview ID.

Each row becomes a `NormalizedPlacementDraft` that funnels through the same orchestrator the other channels use. The dashboard then shows placements transitioning through the lifecycle defined in code:

* `pending_enrichment` — we look up additional contact information.
* `ready` — compliance has not yet been evaluated; the placement is queued for outreach.
* `in_outreach` — active outreach is happening.
* `disputed` / `pending_payment` / `paid` — outcome states.
* `needs_review` — operator triage required (e.g. non-USD invoice with no FX rule).
* `recalled` / `resolved` / `resolved_out_of_band` / `withdrawn_by_creditor` / `closed_uncollected` — terminal states.

## What happens next

For each placement:

1. **Enrichment** runs first to fill in missing contact info and entity metadata.
2. **Compliance evaluation** runs at planning time, not at intake. It checks creditor verification status (`compliance refuses outreach on any non-verified creditor`), state rules, contact hours, frequency caps, and any safety flags on the row.
3. **Outreach** starts only when both enrichment is done and a fresh compliance decision says `can_call=true`.

You can watch this in real time on the placement detail page. See [Reading the dashboard](/for-creditor-partners/creditor-operations/reading-the-dashboard.md).

## What if I uploaded the wrong file

You can cancel a placement before outreach has started. From the placement detail page, click **Recall** — the placement moves to `recalled`. See [Pausing or recalling a placement](/for-creditor-partners/creditor-operations/pausing-or-recalling-a-placement.md).

Once outreach has started, you can still recall, but anything we already sent or said can't be unsent. The debtor will know you used a collections service.

## Common errors

| Error                              | What to do                                                                                                                       |
| ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| `empty csv`                        | The body had zero bytes — check that the file actually saved before uploading.                                                   |
| 413 / `csv exceeds 20971520 bytes` | File over 20 MB. Split it into chunks.                                                                                           |
| `date_locale_ambiguous` warning    | The preview can't tell `MM/DD/YYYY` from `DD/MM/YYYY`. Pick the locale in the preview UI or reformat to `YYYY-MM-DD`.            |
| Two-digit year rejected            | Use four-digit years — the SOL ambiguity (1924 vs 2024) is too costly to guess.                                                  |
| Amount parsing failure             | Use the convention that matches the row's `currency`. USD-style: `1,250.00`. Multiple decimal points in one number are rejected. |
| Duplicate placement                | Intake dedupes via the orchestrator. Repeat uploads of the same invoice resolve to `status: "duplicate"`.                        |

## TODO

* Screenshot: CSV template download button.
* Screenshot: upload preview with validation states.
* Screenshot: post-upload placement list view.
* Screenshot: placement detail page during enrichment.

***

Last reviewed: 2026-05-24 by Customer Success.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://help.moderncollections.io/for-creditor-partners/creditor-onboarding/uploading-your-first-placements.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
