# API: Deals

| Method | Path | What it does | Scope |
|---|---|---|---|
| GET | `/v1/deals` | List deals (filters: q, owner_id, list_id, linked_to, dismissed, filter[field]=value, filter[field][gte\|lte\|gt\|lt\|in\|exists]=value) | `deals:read` |
| POST | `/v1/deals` | Create a deal | `deals:write` |
| DELETE | `/v1/deals/:id` | Archive a record (restore with POST …/restore) | `deals:write` |
| GET | `/v1/deals/:id` | Get a record (as_of=ISO time for its values at that moment). A merged record answers 301 with the survivor's id. | `deals:read` |
| PATCH | `/v1/deals/:id` | Update values or owner. A merged record answers 410. Pass version for optimistic concurrency. | `deals:write` |
| POST | `/v1/deals/:id/dismiss` | Dismiss a noise record so it stops resurfacing (undo with dismissed: false) | `deals:write` |
| GET | `/v1/deals/:id/history` | Every change to this record, newest first, with field-level before and after. Values that came from an item now private (or metadata-only) to someone else show as null with redacted: true. | `deals:read` |
| POST | `/v1/deals/:id/merge` | Merge other records into this one. They redirect here afterwards. | `deals:write` |
| GET | `/v1/deals/:id/provenance` | Where each current value came from (user, API, import, activity, agent or sync). `link` points at the activity, import row or record behind it. | `deals:read` |
| POST | `/v1/deals/:id/restore` | Restore an archived record | `deals:write` |
| GET | `/v1/deals/:id/timeline` | Activity about this record, newest first (deleted-at-source items hidden) | `activities:read` |
| POST | `/v1/deals/analyze-write` | Before creating or updating: does this record already exist? Returns create, update (with the record to update) or review, with every match (same email, domain, phone, LinkedIn, same or similar name), rows of a batch that repeat each other, and for updates the values that would be overwritten. Agents' creates run this check and are refused when the record exists. | `deals:read` |
| POST | `/v1/deals/bulk` | Create up to 25 records. Each row_key (yours alone) is idempotent: the same key and data replays the record it created; the same key with different data is a conflict. | `deals:write` |
| POST | `/v1/deals/dismiss` | Dismiss (or undo with dismissed: false) many noise records at once | `deals:write` |
| POST | `/v1/deals/duplicates` | Check for existing records before creating or updating (same email, domain, phone, LinkedIn or name) | `deals:read` |
| GET | `/v1/deals/possible-duplicates` | Groups of records that look like the same thing (shared email, domain, phone, LinkedIn or exact name), to review and merge | `deals:read` |
| GET | `/v1/deals/schema` | JSON Schemas for create and update bodies, including this organization's custom fields | `deals:read` |

## Examples

### GET /v1/deals

List deals (filters: q, owner_id, list_id, linked_to, dismissed, filter[field]=value, filter[field][gte|lte|gt|lt|in|exists]=value)

```bash
curl -s "https://api.closefinch.com/v1/deals" \
  -H "Authorization: Bearer $RA_API_KEY"
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();
```

### POST /v1/deals

Create a deal

```bash
curl -s -X POST "https://api.closefinch.com/v1/deals" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals`, {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();
```

### DELETE /v1/deals/$ID

Archive a record (restore with POST …/restore)

```bash
curl -s -X DELETE "https://api.closefinch.com/v1/deals/$ID" \
  -H "Authorization: Bearer $RA_API_KEY"
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/${id}`, {
  method: "DELETE",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();
```

### GET /v1/deals/$ID

Get a record (as_of=ISO time for its values at that moment). A merged record answers 301 with the survivor's id.

```bash
curl -s "https://api.closefinch.com/v1/deals/$ID" \
  -H "Authorization: Bearer $RA_API_KEY"
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/${id}`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();
```

### PATCH /v1/deals/$ID

Update values or owner. A merged record answers 410. Pass version for optimistic concurrency.

```bash
curl -s -X PATCH "https://api.closefinch.com/v1/deals/$ID" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/${id}`, {
  method: "PATCH",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();
```

### POST /v1/deals/$ID/dismiss

Dismiss a noise record so it stops resurfacing (undo with dismissed: false)

```bash
curl -s -X POST "https://api.closefinch.com/v1/deals/$ID/dismiss" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/${id}/dismiss`, {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();
```

### GET /v1/deals/$ID/history

Every change to this record, newest first, with field-level before and after. Values that came from an item now private (or metadata-only) to someone else show as null with redacted: true.

```bash
curl -s "https://api.closefinch.com/v1/deals/$ID/history" \
  -H "Authorization: Bearer $RA_API_KEY"
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/${id}/history`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();
```

### POST /v1/deals/$ID/merge

Merge other records into this one. They redirect here afterwards.

```bash
curl -s -X POST "https://api.closefinch.com/v1/deals/$ID/merge" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/${id}/merge`, {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();
```

### GET /v1/deals/$ID/provenance

Where each current value came from (user, API, import, activity, agent or sync). `link` points at the activity, import row or record behind it.

```bash
curl -s "https://api.closefinch.com/v1/deals/$ID/provenance" \
  -H "Authorization: Bearer $RA_API_KEY"
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/${id}/provenance`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();
```

### POST /v1/deals/$ID/restore

Restore an archived record

```bash
curl -s -X POST "https://api.closefinch.com/v1/deals/$ID/restore" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/${id}/restore`, {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();
```

### GET /v1/deals/$ID/timeline

Activity about this record, newest first (deleted-at-source items hidden)

```bash
curl -s "https://api.closefinch.com/v1/deals/$ID/timeline" \
  -H "Authorization: Bearer $RA_API_KEY"
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/${id}/timeline`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();
```

### POST /v1/deals/analyze-write

Before creating or updating: does this record already exist? Returns create, update (with the record to update) or review, with every match (same email, domain, phone, LinkedIn, same or similar name), rows of a batch that repeat each other, and for updates the values that would be overwritten. Agents' creates run this check and are refused when the record exists.

```bash
curl -s -X POST "https://api.closefinch.com/v1/deals/analyze-write" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/analyze-write`, {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();
```

### POST /v1/deals/bulk

Create up to 25 records. Each row_key (yours alone) is idempotent: the same key and data replays the record it created; the same key with different data is a conflict.

```bash
curl -s -X POST "https://api.closefinch.com/v1/deals/bulk" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/bulk`, {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();
```

### POST /v1/deals/dismiss

Dismiss (or undo with dismissed: false) many noise records at once

```bash
curl -s -X POST "https://api.closefinch.com/v1/deals/dismiss" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/dismiss`, {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();
```

### POST /v1/deals/duplicates

Check for existing records before creating or updating (same email, domain, phone, LinkedIn or name)

```bash
curl -s -X POST "https://api.closefinch.com/v1/deals/duplicates" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/duplicates`, {
  method: "POST",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}`, "content-type": "application/json" },
  body: JSON.stringify({}), // fields: see the OpenAPI schema
});
const data = await res.json();
```

### GET /v1/deals/possible-duplicates

Groups of records that look like the same thing (shared email, domain, phone, LinkedIn or exact name), to review and merge

```bash
curl -s "https://api.closefinch.com/v1/deals/possible-duplicates" \
  -H "Authorization: Bearer $RA_API_KEY"
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/possible-duplicates`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();
```

### GET /v1/deals/schema

JSON Schemas for create and update bodies, including this organization's custom fields

```bash
curl -s "https://api.closefinch.com/v1/deals/schema" \
  -H "Authorization: Bearer $RA_API_KEY"
```

```js
const res = await fetch(`https://api.closefinch.com/v1/deals/schema`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();
```

Request and response schemas: [https://www.closefinch.com/openapi.json](https://www.closefinch.com/openapi.json). All resources: [API reference](https://www.closefinch.com/docs/api).
