{/* Metadata lives in ./layout.tsx */}

# Costs

Two shapes: **per-day channel spend** (facts) and **recurring expenses** (specs). Both feed the same metrics layer as the dashboard. {{ className: 'lead' }}

<Warning>
  Not live yet. Wire contract below is the intended v1 surface.
</Warning>

---

## `POST /v1/costs/spend`

**Scope:** `costs:write`. **Batch:** 1-500 rows.

Daily channel spend lands in `ad_insights_daily`. Channel labels are auto-namespaced to `custom:<label>` so they never collide with native integrations (`meta`, `google`, ...).

```json
{
  "rows": [
    {
      "channel": "Influencers",
      "event_date": "2026-07-12",
      "currency": "DKK",
      "spend": "1500.00",
      "impressions": 10000,
      "clicks": 320,
      "campaign_id": "summer",
      "campaign_name": "Summer"
    }
  ]
}
```

| Field | Notes |
| --- | --- |
| `channel` | Merchant label; stored as `custom:influencers` (slugified) |
| `event_date` | `YYYY-MM-DD` |
| `currency` | Becomes `account_id` in the natural key (multi-currency safe) |
| `spend` | Decimal string; push `"0"` to delete/restate a day |

Optional: `impressions`, `clicks`, `conversions`, `conversion_value`, campaign/adset/ad ids and names.

---

## Expenses

Thin API over the existing custom costs substrate, keyed by client `external_id`. UI-created rows (`external_id` null) are never touched.

**Scope:** `costs:write` for all methods.

### `GET /v1/costs/expenses`

List API-managed expenses for the shop.

### `POST /v1/costs/expenses`

Upsert by `external_id` (idempotent retries).

```json
{
  "external_id": "retainer-2026",
  "name": "Agency retainer",
  "category": "marketing",
  "amount": "5000.00",
  "currency": "DKK",
  "frequency": "monthly",
  "started_on": "2026-01-01",
  "ended_on": null,
  "counts_as_ad_spend": true
}
```

| Field | Values |
| --- | --- |
| `category` | `software` \| `tools` \| `va` \| `marketing` \| `other` |
| `frequency` | `monthly` \| `weekly` \| `yearly` \| `one_time` \| `per_order` \| `percent_of_revenue` |
| `counts_as_ad_spend` | When true, charge lands in blended ad spend (ROAS/CAC) |

### `PATCH /v1/costs/expenses/{external_id}`

Full-field update of an existing expense. Body shape matches POST; path is the natural key.

### `DELETE /v1/costs/expenses/{external_id}`

Hard-delete the API-managed row. `204` on success, `404` if missing.
