Skip to content

API reference

API: Contacts

Markdown version

MethodPathWhat it doesScope
GET/v1/contactsList contacts (filters: q, owner_id, list_id, linked_to, dismissed, filter[field]=value, filter[field][gte|lte|gt|lt|in|exists]=value)contacts:read
POST/v1/contactsCreate a contactcontacts:write
DELETE/v1/contacts/:idArchive a record (restore with POST …/restore)contacts:write
GET/v1/contacts/:idGet a record (as_of=ISO time for its values at that moment). A merged record answers 301 with the survivor's id.contacts:read
PATCH/v1/contacts/:idUpdate values or owner. A merged record answers 410. Pass version for optimistic concurrency.contacts:write
POST/v1/contacts/:id/dismissDismiss a noise record so it stops resurfacing (undo with dismissed: false)contacts:write
GET/v1/contacts/:id/historyEvery 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.contacts:read
POST/v1/contacts/:id/mergeMerge other records into this one. They redirect here afterwards.contacts:write
GET/v1/contacts/:id/provenanceWhere each current value came from (user, API, import, activity, agent or sync). link points at the activity, import row or record behind it.contacts:read
POST/v1/contacts/:id/restoreRestore an archived recordcontacts:write
GET/v1/contacts/:id/timelineActivity about this record, newest first (deleted-at-source items hidden)activities:read
POST/v1/contacts/analyze-writeBefore 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.contacts:read
POST/v1/contacts/bulkCreate 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.contacts:write
POST/v1/contacts/dismissDismiss (or undo with dismissed: false) many noise records at oncecontacts:write
POST/v1/contacts/duplicatesCheck for existing records before creating or updating (same email, domain, phone, LinkedIn or name)contacts:read
GET/v1/contacts/possible-duplicatesGroups of records that look like the same thing (shared email, domain, phone, LinkedIn or exact name), to review and mergecontacts:read
GET/v1/contacts/schemaJSON Schemas for create and update bodies, including this organization's custom fieldscontacts:read

Examples

GET /v1/contacts

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

curl -s "https://api.closefinch.com/v1/contacts" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/contacts`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

POST /v1/contacts

Create a contact

curl -s -X POST "https://api.closefinch.com/v1/contacts" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/contacts`, {
  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/contacts/$ID

Archive a record (restore with POST …/restore)

curl -s -X DELETE "https://api.closefinch.com/v1/contacts/$ID" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/contacts/${id}`, {
  method: "DELETE",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

GET /v1/contacts/$ID

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

curl -s "https://api.closefinch.com/v1/contacts/$ID" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/contacts/${id}`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

PATCH /v1/contacts/$ID

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

curl -s -X PATCH "https://api.closefinch.com/v1/contacts/$ID" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/contacts/${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/contacts/$ID/dismiss

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

curl -s -X POST "https://api.closefinch.com/v1/contacts/$ID/dismiss" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/contacts/${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/contacts/$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.

curl -s "https://api.closefinch.com/v1/contacts/$ID/history" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/contacts/${id}/history`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

POST /v1/contacts/$ID/merge

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

curl -s -X POST "https://api.closefinch.com/v1/contacts/$ID/merge" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/contacts/${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/contacts/$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.

curl -s "https://api.closefinch.com/v1/contacts/$ID/provenance" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/contacts/${id}/provenance`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

POST /v1/contacts/$ID/restore

Restore an archived record

curl -s -X POST "https://api.closefinch.com/v1/contacts/$ID/restore" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/contacts/${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/contacts/$ID/timeline

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

curl -s "https://api.closefinch.com/v1/contacts/$ID/timeline" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/contacts/${id}/timeline`, {
  method: "GET",
  headers: { authorization: `Bearer ${process.env.RA_API_KEY}` },
});
const data = await res.json();

POST /v1/contacts/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.

curl -s -X POST "https://api.closefinch.com/v1/contacts/analyze-write" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/contacts/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/contacts/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.

curl -s -X POST "https://api.closefinch.com/v1/contacts/bulk" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/contacts/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/contacts/dismiss

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

curl -s -X POST "https://api.closefinch.com/v1/contacts/dismiss" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/contacts/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/contacts/duplicates

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

curl -s -X POST "https://api.closefinch.com/v1/contacts/duplicates" \
  -H "Authorization: Bearer $RA_API_KEY" \
  -H "content-type: application/json" \
  -d '{}'
const res = await fetch(`https://api.closefinch.com/v1/contacts/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/contacts/possible-duplicates

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

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

GET /v1/contacts/schema

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

curl -s "https://api.closefinch.com/v1/contacts/schema" \
  -H "Authorization: Bearer $RA_API_KEY"
const res = await fetch(`https://api.closefinch.com/v1/contacts/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. All resources: API reference.

Send feedback

Tell us what's broken, confusing or missing on this page, or an idea. It goes to the team that builds the product.

Please don't include passwords or card numbers.
Only to reply about this. Never added to any mailing list.