# Forge Native Agent — External API
**Base:** `https://forge.kin.team/api/v1`  
**Auth:** `Authorization: Bearer fk_live_...` (key is **one company / tenant only**)  
**Status:** Live · Primary integration path for Kin, Claude, Grok, custom bots  
**Updated:** 2026-08-11  
**See also:** `AGENT_COST_ARCHITECTURE.md` (models/context — server-side; callers still send one NL message)

## Product Law v2 (Balanced) — what outer platforms must know

Runtime constitution on **client / quote / job / invoice creates** (UI + API + MCP + this agent):

| Situation | Forge behavior |
|-----------|----------------|
| Clear email/phone/name match | Reuses client — returns existing id on conflict |
| Site address in brief/notes | Links or creates **Property**, strips site from notes |
| Ambiguous multi-match | Blocks with recovery — outer agent must disambiguate |
| "Fix INV-38" / update job | Routes to **update/revise** — same number |
| Job already has open invoice | Points at that INV — no second invoice |
| `force: true` | Audited exception; pass `forceReason` |

**Mandatory read for integrators:** `GET https://forge.kin.team/api/v1/agent-laws`  
**Discovery bump:** `/.well-known/api.json` version **2.0.0** includes `productLaw` + updated `agentContract`.

When your platform reconnects or re-syncs tools, pull:
1. `/.well-known/api.json`
2. `/.well-known/mcp.json` (live tool list)
3. `/api/v1/agent-laws`
4. `/api/v1/openapi.yaml`

Do **not** hardcode an old create-only tool subset — that causes duplicate J-/INV- numbers.

## Mental model

You are **not** rebuilding Forge’s CRM in the outer model.  
You send **plain language**; Forge’s native agent executes multi-step ops with quality rules.

```
Outer platform chat
    →  ONE request to Forge
    →  Q-#### / J-#### / INV-#### + links + needsApproval
```

At 10,000 companies: each company has its own key(s). Same endpoint. Forge isolates by tenant.

## One call

`POST /api/v1/agent/run`

```http
Authorization: Bearer fk_live_...
Content-Type: application/json
Idempotency-Key: optional-but-recommended-uuid
```

```json
{
  "message": "Quote Killam at Vibe Lofts unit 401 drywall repairs $2400. One house-style line with full scope in detail. Do not send.",
  "dryRun": false,
  "conversationId": null,
  "autoSend": false
}
```

### Fields
| Field | Notes |
|-------|--------|
| `message` | Natural language. User should not need special prompting. |
| `conversationId` | Continue a thread (must belong to **this** tenant). |
| `dryRun` | Plan only; no mutating writes. |
| `autoSend` | Default false. true still subject to tenant policy. |

### Timeouts
Use **≥ 120s** client timeout for ops runs.

## Response shape

- `ok`, `runId`, `conversationId`
- `status`: `completed` | `needs_approval` | `failed` | `dry_run`
- `summary` — plain English for the human
- `actions[]` — `{ tool, ok, summary|message }`
- `artifacts[]` — `{ type, id, number, url }` (quotes include public `/q/` when present)
- `needsApproval[]` — send/delete parked until approve
- `tokensIn`, `tokensOut`, `costCents`, optional `qualityScore`

## Approvals

- `POST /api/v1/agent/approvals/{id}/approve`
- `POST /api/v1/agent/approvals/{id}/reject`

## Run status

- `GET /api/v1/agent/runs/{runId}`

## MCP

| Tool | Maps to |
|------|---------|
| `forge_agent` | `POST /agent/run` body |
| `forge_agent_approve` | approval endpoints |

## Rules for Kin / Claude / Grok / Cursor

1. **Default path:** one `forge_agent` / HTTP run for multi-step ops.  
2. **Do not** chain list→create→quote for normal work unless Forge asks a clarifying question.  
3. Kin’s thin built-in `forge_*` tools are a **subset** — not proof a capability is missing inside Forge.  
4. Never set `autoSend: true` in tests; never claim email sent without approval proof.  
5. Prefer Idempotency-Key on every mutating run.  
6. Wrong or foreign `conversationId` must fail — do not retry with another tenant’s ids.  

## Connecting another platform (checklist)

1. Create Forge API key for the **customer tenant** (scopes for quotes/jobs/invoices as needed).  
2. Store key in that platform’s secret store.  
3. Register **one** tool: `run_forge_agent(message, conversationId?, dryRun?, autoSend?)`.  
4. System blurb: “For any Forge CRM work, call run_forge_agent with the user’s words. Do not invent line-item JSON.”  
5. Surface `summary` + artifact links + approval ids to the user.  

Same design for 1 user or 10,000 companies — only the key changes.

## Live proof (historical)

- 2026-07-24: dry-run OK + live Q-0016 one-call  
- 2026-07-25+: retest R1–R7 core loop; pricing + detail fixes  

## Related

- Architecture: `docs/FORGE_NATIVE_AGENT.md`  
- Scale list: `docs/SCALE_NEVER_FORGET.md`

## Thin tools vs full surface

See **`docs/KIN_FORGE_TOOLS.md`** for the full MCP/REST/Kin matrix (**67 tools** as of 2026-08-05), email→billingEmail aliases, contacts, visits, payroll, receipts, QB publish, and quote line-item merge rules.

Kin thin tools are a subset — missing thin tool ≠ missing Forge capability. Default to `forge_agent`.

### Quote description cleanup
`forge_update_quote` with line item text only **keeps** existing unit prices/qty (merge by id or index). Do not recreate the quote to fix wording. Dirty prices like `$2.80/sq ft` parse correctly.



## Public discovery (for external developers)

| Resource | URL |
|----------|-----|
| Developer hub | https://forge.kin.team/developers |
| API discovery | https://forge.kin.team/.well-known/api.json |
| MCP discovery | https://forge.kin.team/.well-known/mcp.json |
| Quickstart | https://forge.kin.team/docs/INTEGRATION_QUICKSTART.md |
| llms.txt | https://forge.kin.team/llms.txt |
| OpenAPI | https://forge.kin.team/api/v1/openapi.yaml |

## MCP idempotency (v1.2)

Mutating MCP tools accept **`idempotencyKey`** (also `idempotency_key` or `_meta.idempotencyKey`):

```json
{
  "name": "forge_create_job",
  "arguments": {
    "clientId": "…",
    "title": "Drywall repair",
    "idempotencyKey": "8f2c1a6e-your-uuid"
  }
}
```

- Same key + success → **replayed for 24h** (no second J-/INV-/Q- number)
- Errors are **not** cached — fix args and retry with the same key
- REST equivalent: header `Idempotency-Key: <uuid>` on POST creates and `POST /api/v1/agent/run`

## REST error envelope (v1.2)

All `/api/v1/*` JSON errors include:

```json
{
  "ok": false,
  "code": "DUPLICATE_DETECTED",
  "message": "…",
  "hint": "…",
  "existing": { "jobNumber": "J-0042" },
  "recovery": {
    "suggestedTool": "forge_update_job",
    "suggestedArgs": { "id": "J-0042" },
    "doNot": ["Do not call forge_create_job again"]
  },
  "_agent": {
    "next": "Call forge_update_job with {\"id\":\"J-0042\"}",
    "lawsUrl": "https://forge.kin.team/api/v1/agent-laws"
  },
  "error": { "code": "DUPLICATE_DETECTED", "message": "…" }
}
```

On any `ok: false`, call `recovery.suggestedTool` with `recovery.suggestedArgs`. Never freestyle.
