# API: First-party signals

| Method | Path | What it does | Scope |
|---|---|---|---|
| GET | `/v1/accounts/:id/first-party` | An account's website visits (90 days), product use, Stripe customers with spend trend, and expansion proposals | `accounts:read` |
| GET | `/v1/billing-signals` | Stripe former-customer and spend-trend settings, the scopes the restricted key needs (read only), and the last pull | `customers:read` |
| PUT | `/v1/billing-signals` | Change Stripe signal settings (owner): on/off, spend window (30, 60 or 90 days), change threshold, minimum baseline, how far back lost deals count | `billing:write` |
| GET | `/v1/billing-signals/customers` | Stripe customers from the last pull: status (active, former), plan, MRR (lost), tenure, cancellation, spend trend | `customers:read` |
| GET | `/v1/billing-signals/lost-moves` | Contacts from closed-lost deals who now work at another account (or whose email bounces: may have moved) | `customers:read` |
| POST | `/v1/billing-signals/pull` | Pull Stripe now (read only; it also runs daily): customers, subscriptions and paid invoices | `billing:write` |
| POST | `/v1/collect/:key` | The website pixel's endpoint (public; the site key is in the snippet). Needs an allowed Origin and, where required, consent; honours Do Not Track and Global Privacy Control. Answers only ok (no sign-in) | — |
| GET | `/v1/expansion-proposals` | Expansion openings proposed from product use, newest first | `customers:read` |
| POST | `/v1/expansion-proposals/:id/accept` | Accept an expansion opening: creates the deal (owner: the account's owner) with what the current contract covers | `customers:write` |
| POST | `/v1/expansion-proposals/:id/dismiss` | Dismiss an expansion opening (no other proposal for that account and team for 90 days) | `customers:write` |
| GET | `/v1/product-signals` | Product signal settings: signup, plan-limit, seat, team and feature event names; spike and expansion thresholds | `customers:read` |
| PUT | `/v1/product-signals` | Change product signal settings (owner) | `settings:write` |
| POST | `/v1/product-signals/run` | Check product events for expansion openings now (it also runs every 10 minutes). Proposals only: nothing is created in the CRM | `customers:write` |
| GET | `/v1/site-tracking` | Website tracking: on/off, consent mode, allowed origins, retention, intent thresholds, the snippet to paste, page tags and the match rate over 30 days | `signals:read` |
| PUT | `/v1/site-tracking` | Change website tracking (owner): turn it on, consent mode (EU/UK visitors, or everyone), allowed origins, retention and intent thresholds | `settings:write` |
| POST | `/v1/site-tracking/rotate-key` | Replace the site key (owner): the old snippet stops working at once | `settings:write` |
| GET | `/v1/site-tracking/tags` | Page tags: a path, a label and an intent weight (0 = never a signal) | `signals:read` |
| POST | `/v1/site-tracking/tags` | Tag a page (owner), e.g. /pricing as Pricing with weight 80 | `settings:write` |
| DELETE | `/v1/site-tracking/tags/:id` | Remove a page tag (owner) | `settings:write` |
| PATCH | `/v1/site-tracking/tags/:id` | Change a page tag (owner) | `settings:write` |
| POST | `/v1/site-tracking/tracked-link` | A link for your own email to one contact (ra_t: a random token, 14 days, tied to the first browser that opens it; the contact id isn't in the URL). M5 email tracking adds it to links automatically | `signals:write` |
| GET | `/v1/site-tracking/visits` | Recent page views (company level: the account, how it was matched and with what confidence; never an IP address) | `signals:read` |

## Examples

### GET /v1/accounts/$ID/first-party

An account's website visits (90 days), product use, Stripe customers with spend trend, and expansion proposals

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

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

### GET /v1/billing-signals

Stripe former-customer and spend-trend settings, the scopes the restricted key needs (read only), and the last pull

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

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

### PUT /v1/billing-signals

Change Stripe signal settings (owner): on/off, spend window (30, 60 or 90 days), change threshold, minimum baseline, how far back lost deals count

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

```js
const res = await fetch(`https://api.closefinch.com/v1/billing-signals`, {
  method: "PUT",
  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/billing-signals/customers

Stripe customers from the last pull: status (active, former), plan, MRR (lost), tenure, cancellation, spend trend

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

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

### GET /v1/billing-signals/lost-moves

Contacts from closed-lost deals who now work at another account (or whose email bounces: may have moved)

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

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

### POST /v1/billing-signals/pull

Pull Stripe now (read only; it also runs daily): customers, subscriptions and paid invoices

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

```js
const res = await fetch(`https://api.closefinch.com/v1/billing-signals/pull`, {
  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/collect/$KEY

The website pixel's endpoint (public; the site key is in the snippet). Needs an allowed Origin and, where required, consent; honours Do Not Track and Global Privacy Control. Answers only ok. No sign-in, so it sends no key.

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

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

### GET /v1/expansion-proposals

Expansion openings proposed from product use, newest first

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

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

### POST /v1/expansion-proposals/$ID/accept

Accept an expansion opening: creates the deal (owner: the account's owner) with what the current contract covers

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

```js
const res = await fetch(`https://api.closefinch.com/v1/expansion-proposals/${id}/accept`, {
  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/expansion-proposals/$ID/dismiss

Dismiss an expansion opening (no other proposal for that account and team for 90 days)

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

```js
const res = await fetch(`https://api.closefinch.com/v1/expansion-proposals/${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/product-signals

Product signal settings: signup, plan-limit, seat, team and feature event names; spike and expansion thresholds

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

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

### PUT /v1/product-signals

Change product signal settings (owner)

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

```js
const res = await fetch(`https://api.closefinch.com/v1/product-signals`, {
  method: "PUT",
  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/product-signals/run

Check product events for expansion openings now (it also runs every 10 minutes). Proposals only: nothing is created in the CRM

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

```js
const res = await fetch(`https://api.closefinch.com/v1/product-signals/run`, {
  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/site-tracking

Website tracking: on/off, consent mode, allowed origins, retention, intent thresholds, the snippet to paste, page tags and the match rate over 30 days

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

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

### PUT /v1/site-tracking

Change website tracking (owner): turn it on, consent mode (EU/UK visitors, or everyone), allowed origins, retention and intent thresholds

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

```js
const res = await fetch(`https://api.closefinch.com/v1/site-tracking`, {
  method: "PUT",
  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/site-tracking/rotate-key

Replace the site key (owner): the old snippet stops working at once

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

```js
const res = await fetch(`https://api.closefinch.com/v1/site-tracking/rotate-key`, {
  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/site-tracking/tags

Page tags: a path, a label and an intent weight (0 = never a signal)

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

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

### POST /v1/site-tracking/tags

Tag a page (owner), e.g. /pricing as Pricing with weight 80

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

```js
const res = await fetch(`https://api.closefinch.com/v1/site-tracking/tags`, {
  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/site-tracking/tags/$ID

Remove a page tag (owner)

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

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

### PATCH /v1/site-tracking/tags/$ID

Change a page tag (owner)

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

```js
const res = await fetch(`https://api.closefinch.com/v1/site-tracking/tags/${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/site-tracking/tracked-link

A link for your own email to one contact (ra_t: a random token, 14 days, tied to the first browser that opens it; the contact id isn't in the URL). M5 email tracking adds it to links automatically

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

```js
const res = await fetch(`https://api.closefinch.com/v1/site-tracking/tracked-link`, {
  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/site-tracking/visits

Recent page views (company level: the account, how it was matched and with what confidence; never an IP address)

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

```js
const res = await fetch(`https://api.closefinch.com/v1/site-tracking/visits`, {
  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).
