> For the complete documentation index, see [llms.txt](https://docs.moderncollections.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.moderncollections.io/rest-api/placements.md).

# Placements

A **placement** is the root resource of the Modern Collections API: one overdue invoice we're collecting on behalf of one creditor against one debtor. Every other resource (calls, emails, payments, disputes, audit events) is scoped to a placement.

## Lifecycle

Status values:

```
pending_enrichment
  → ready
  → in_outreach
        ├── pending_payment       (a payment commitment was made)
        │     → paid              (terminal)
        ├── disputed              (debtor filed dispute via portal)
        │     → resolved          (back to outreach, or one of the terminal resolutions)
        ├── closed_uncollected    (terminal)
        ├── recalled              (creditor-initiated, terminal)
        ├── needs_review          (QBO non-USD intake, awaits operator triage)
        ├── resolved              (generic terminal "no longer collectable")
        ├── resolved_out_of_band  (QuickBooks payment recorded with no matching portal charge)
        └── withdrawn_by_creditor (QBO Invoice.Void)
```

State transitions are driven by:

* **Your API actions** — `POST /v1/placements`, `POST /v1/placements/{id}/recall`, `POST /v1/placements/{id}/posture`, `POST /v1/placements/{id}/payment-received`, `POST /v1/placements/{id}/amend`, `POST /v1/placements/{id}/dispute/acknowledge`.
* **Debtor behavior** — portal payments, disputes filed via the portal.
* **Compliance and scheduling** — placements progress autonomously through outreach once they reach `in_outreach`.

## Key concepts

### Posture

Every placement carries a `posture`:

* `preserve` (default — from the creditor's `posture_default`) — relationship-preserving tone, slower cadence.
* `escalate` — firmer tone, faster cadence.

Change posture mid-flight with `POST /v1/placements/{id}/posture` with body `{"posture": "escalate"}`.

### Days past due

`days_past_due` is computed server-side from `due_date`. Don't compute it on your end and pass it in.

### The compliance object

`GET /v1/placements/{id}` includes a `compliance` block with a snapshot of the latest compliance decision:

```json
"compliance": {
  "can_call": true,
  "can_email": true,
  "escalation_allowed": false,
  "reasons": ["..."],
  "evaluated_at": "2026-04-01T10:05:00Z"
}
```

For the full decision (including `expires_at`, `is_expired`, restrictions, two-party consent, AI-disclosure flag), call `GET /v1/placements/{id}/compliance-decision`.

## Endpoints

| Method | Path                                      | Purpose                                                                                                                                                                                                                           |
| ------ | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST` | `/v1/placements`                          | Create a placement. Returns `201` with the created placement, or `202` with `{"intake_review_id", "status": "pending_review", "reason": "sol_time_barred"}` when the debtor state's SOL bars collection.                          |
| `GET`  | `/v1/placements`                          | List placements with `status` (CSV), `page`, `page_size` (max 100).                                                                                                                                                               |
| `GET`  | `/v1/placements/{id}`                     | Retrieve full placement detail with debtor, compliance snapshot, timeline, planner state.                                                                                                                                         |
| `GET`  | `/v1/placements/{id}/calls`               | List call records for a placement.                                                                                                                                                                                                |
| `GET`  | `/v1/placements/{id}/emails`              | List email sends for a placement.                                                                                                                                                                                                 |
| `GET`  | `/v1/placements/{id}/audit`               | Audit event log. Filters: `event_type` (CSV, max 25 entries / 500 chars), `actor`, `since`, `until`, `limit` (1..500), `cursor`. Returns keyset pagination.                                                                       |
| `GET`  | `/v1/placements/{id}/compliance-decision` | Latest compliance decision (full shape).                                                                                                                                                                                          |
| `POST` | `/v1/placements/{id}/enrichment/refresh`  | Re-run enrichment on demand. Rate-limited to 1/min per (creditor, placement).                                                                                                                                                     |
| `POST` | `/v1/placements/{id}/recall`              | Recall the placement — pauses active email sequences and cancels scheduled calls.                                                                                                                                                 |
| `POST` | `/v1/placements/{id}/posture`             | Change posture mid-flight.                                                                                                                                                                                                        |
| `POST` | `/v1/placements/{id}/amend`               | Apply an append-only amendment. Body: `{amendment_type, after_value, reason}`. Note: `status_correction` is not available via the creditor API — use the dedicated dispute, payment, or recall workflows instead (returns `403`). |
| `GET`  | `/v1/placements/{id}/payments`            | List payment commitments for a placement.                                                                                                                                                                                         |
| `POST` | `/v1/placements/{id}/payment-received`    | Record a payment received against a placement. Body: `{"amount": 7500.00}`.                                                                                                                                                       |
| `GET`  | `/v1/placements/{id}/dispute`             | Return the most recent dispute on a placement.                                                                                                                                                                                    |
| `POST` | `/v1/placements/{id}/dispute/acknowledge` | Creditor acknowledges an open dispute.                                                                                                                                                                                            |

Full request and response shapes are in [openapi.yaml](https://github.com/knollmeyersam-dot/MC-Docs/blob/main/api-reference/rest-api/openapi.yaml).

## Create-placement request body

```json
{
  "invoice_number": "INV-2026-0042",
  "invoice_amount": 7500.00,
  "currency": "USD",
  "invoice_date": "2026-04-01",
  "due_date": "2026-05-01",
  "posture": "preserve",
  "debtor": {
    "company_name": "Acme Inc.",
    "primary_domain": "acme.example",
    "primary_email": "ap@acme.example",
    "primary_phone": "+15105550101",
    "state": "TX",
    "city": "Austin",
    "zip": "78701"
  },
  "notes": "Optional creditor note",
  "callback_url": "https://yourapp.example/webhooks/placement-events"
}
```

### Validation rules

* `invoice_amount` — required, positive `Decimal`.
* `currency` — defaults to `"USD"`.
* `debtor.company_name` — required.
* `debtor.state` — recommended (drives compliance evaluation).
* `posture` — `"preserve"` or `"escalate"`; defaults to the creditor's `posture_default`.
* `callback_url` — optional HTTPS URL. Must pass the outbound SSRF guard (no http\://, localhost, RFC1918, etc.). Empty string is normalised to `null`.

### Create-placement response (201)

```json
{
  "placement_id": "550e8400-e29b-41d4-a716-446655440000",
  "status": "pending_enrichment",
  "debtor_id": "660e8400-e29b-41d4-a716-446655440001",
  "estimated_first_contact": "2026-05-12T10:45:00Z",
  "created_at": "2026-05-12T10:30:00Z"
}
```

`estimated_first_contact` is `created_at + enrichment_sla_minutes` (default 15 minutes).

### Duplicate handling

`POST /v1/placements` with an `invoice_number` already used by this creditor returns `409 {"detail": "Duplicate invoice: <invoice_number>"}`.

## Common patterns

### Filing a backlog

```python
for invoice in backlog:
    try:
        client.post("/placements", json=to_placement_payload(invoice))
    except httpx.HTTPStatusError as e:
        if e.response.status_code == 409 and "Duplicate invoice" in e.response.json().get("detail", ""):
            continue
        raise
```

There is no `Idempotency-Key` header today. Use a stable `invoice_number` per invoice so re-runs hit the duplicate guard.

### Watching for state changes

Register a webhook rather than polling the detail endpoint. See [Webhooks overview](/webhooks/overview.md).

### Pulling audit events

```
GET /v1/placements/{id}/audit?limit=100
```

Returns `{"events": [...], "pagination": {"next_cursor": ..., "has_more": ...}}`. JSON only — there is no `?format=csv` parameter today.

## See also

* [`placement.first_contact` webhook](/webhooks/events/placement.first_contact.md)
* [`placement.status_changed` webhook](/webhooks/events/placement.status_changed.md)
* [Ingest overdue invoices](/integration-guides/ingest-overdue-invoices.md)
* [Compliance for integrators](/compliance-for-integrators/overview.md)


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## 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, and the optional `goal` query parameter:

```
GET https://docs.moderncollections.io/rest-api/placements.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
