---
name: dealgraph
description: >
  Read and write a DealGraph VC portfolio (companies, investments, funding rounds, metrics,
  LP fund drawdowns, GP marks) over the REST API. Use when asked about portfolio companies,
  MOIC/TVPI, pushing investor-reporting metrics, recording rounds or drawdowns, or syncing
  portfolio data. Enforces the propose-before-write golden loop.
---

# DealGraph skill

Base URL: `https://dealgraph.vc/api/v1` · Auth: `Authorization: Bearer $DEALGRAPH_API_KEY` ·
Contract: `GET /openapi.json` (v1.4.0) · Full docs: `https://dealgraph.vc/llms-full.txt`

## Non-negotiable rules

1. **Propose before write.** GET current state → compute the diff → dry-run the write
   (`?dryRun=true`) → show the human before → after → write only on explicit yes.
2. **Idempotency-Key on every POST create** (any UUID). Retry-safe by construction.
3. **Attribute writes**: header `X-Actor: <agent> (<operator>)` plus `"source"`/`"notes"` fields.
4. **Never `?hard=true`** without explicit human approval. Soft delete + `/restore` is the default.
5. **All money is absolute EUR.** Convert foreign currencies at the ECB reference rate of the
   metric/round date and state the rate in `notes`
   (`https://data-api.ecb.europa.eu/service/data/EXR/D.<CCY>.EUR.SP00.A`; EUR = amount ÷ rate).

## Recipes

**Resolve a company** — `GET /companies?externalRef=<your-id>` or `GET /search?q=<name>`.
Company names are unique per org; a 409 on create returns `details.existingId` — attach to it.

**Push a reporting metric**

```bash
curl -s -X POST "$BASE/companies/<id>/metrics?dryRun=true" \
  -H "Authorization: Bearer $DEALGRAPH_API_KEY" -H "Content-Type: application/json" \
  -d '{"metricKey":"arr_eur","metricDate":"2026-06-30","valueNumeric":"1200000",
       "source":"agent:<name>","notes":"FX rate used: ..."}'
# show the human the would-be result, then re-run without dryRun + with Idempotency-Key
```

Metric keys: `arr_eur` (absolute EUR), `headcount`, `runway_months` (≥0; negative runway goes
in notes only), `traction_score`/`moat_score` (1-10), `valuation_eur_m` (absolute EUR despite
the name). Writing a metric auto-updates the company's cached `*Latest` fields.

**Record a funding round** — `POST /companies/{id}/funding-rounds`. `equity` requires
`valuationPostEur`; `cla`/`safe` take an optional `valuationPreEur` (the cap). At least one of
amount/pre/post required. Valuations round to €10k; re-read to confirm.

**Fund (LP) workflows** — drawdowns: `POST /portfolios/{id}/drawdowns`; GP marks:
`POST /portfolios/{id}/reported-marks` (`asOfDate` + ≥1 of NAV/TVPI/DPI/MOIC; latest mark by
asOfDate overrides the bottom-up valuation; re-posting an asOfDate updates it).

**One company, several holders.** A company can be held by more than one portfolio at once — typically a direct ticket plus a look-through ticket from a fund you are an LP in. Each portfolio is a separate **position** with its own entry price, ownership stake and cost, so each is marked independently. `GET /companies/{id}` returns those rows in `positions[]`; the company-level `performance{}` is their **sum**. Read the matching `positions[]` entry when reporting on one holding: the blended company MOIC mixes different entry prices and can show a strong position as flat. Never add ownership percentages from different positions together as though they were one stake.

**Incremental sync** — `?updatedSince=<ISO>` on lists; `GET /changes?since=<ISO>` is the
guaranteed feed; webhooks are best-effort (reconcile via /changes).

**Verify your own writes** — read back after writing; check `GET /audit?entityId=<id>`.

## Error handling

Stable codes: `validation_error` (400, `field` names the offender), `unauthorized` (401),
`forbidden_readonly`/`forbidden` (403), `not_found` (404), `unique_conflict`/
`idempotency_conflict` (409), `rate_limited` (429, honor `Retry-After`), `internal` (500,
retry with backoff + same Idempotency-Key).
