Contacts API

List, get, create, update, and delete contacts through the REST API.

Overview

The contacts endpoints expose every operation you can do on the contact book through the dashboard. Identity fields, tags, and "introduced by" links are all editable through PATCH. Manual contacts can be created and deleted; Google-sourced contacts can be edited but not created via the API (their identity is owned by Google).

For what each field means in product terms, see the user-facing Contacts page.

Endpoints

MethodPathPurpose
GET/api/v1/contactsList contacts with filters and pagination.
POST/api/v1/contactsCreate a manual contact.
GET/api/v1/contacts/:idGet a single contact.
PATCH/api/v1/contacts/:idUpdate a contact.
DELETE/api/v1/contacts/:idDelete a contact.

List Contacts

GET /api/v1/contacts?query=acme&source=google&sortBy=recent&limit=50&offset=0

Query parameters

ParamTypeDefaultNotes
querystringMatches name, email, or company.
tagsstringComma-separated tag IDs. Filters to contacts who have all the listed tags.
sourceall | google | manualallLimits to a contact source.
sortByrecent | namerecentSort field.
sortOrderasc | descdescSort direction.
limitnumber50Max 100.
offsetnumber0Skip count for pagination.

Response

{
  "data": [
    {
      "id": "8a1d...",
      "givenName": "Alice",
      "familyName": "Nguyen",
      "primaryEmail": "alice@example.com",
      "emails": ["alice@example.com"],
      "phones": ["+1 555 0100"],
      "company": "Acme Corp",
      "jobTitle": "CTO",
      "source": "google",
      "tags": [{ "id": "...", "name": "investor" }],
      "introducedBy": null,
      "lastContactAt": "2026-04-30T19:21:00.000Z",
      "createdAt": "2026-01-12T10:00:00.000Z",
      "updatedAt": "2026-04-30T19:21:00.000Z"
    }
  ],
  "total": 247
}

total is the count before pagination, so you can render "showing 1–50 of 247" client-side.

Create a Contact

POST /api/v1/contacts
Content-Type: application/json

{
  "givenName": "Carol",
  "familyName": "Mendes",
  "primaryEmail": "carol@startup.io",
  "company": "Startup",
  "tagIds": ["8c2b...", "founder"],
  "introducedById": "9d3e..."
}

Body fields

At least one of givenName, familyName, company, or primaryEmail is required.

FieldTypeNotes
givenName, familyNamestring (≤100 chars)First / last name.
primaryEmailemail (≤320 chars)Optional. The "main" email for the contact.
emailsarray of emailsAdditional emails.
phonesarray of strings (≤50 chars each)Phone numbers in any format you use.
companystring (≤200 chars)
jobTitlestring (≤200 chars)
birthdayYear, birthdayMonth, birthdayDayintegersAll optional. Year 1900–current.
tagIdsarray of stringsUUIDs of existing tags or plain tag names — names are upserted.
introducedByIdUUID or nullThe contact who introduced you to this person.

Response

201 Created

{
  "data": { "id": "...", "givenName": "Carol", ... }
}

Validation errors

422 Unprocessable Entity

{
  "error": "Validation failed",
  "details": {
    "fieldErrors": {
      "givenName": ["Too long"]
    },
    "formErrors": ["Enter at least one of First name, Last name, Company, or Email."]
  }
}

Get a Contact

GET /api/v1/contacts/8a1d...

Response

{ "data": { "id": "...", "givenName": "Alice", ... } }

404 if the contact doesn't exist or doesn't belong to your account.

Update a Contact

PATCH /api/v1/contacts/8a1d...
Content-Type: application/json

{
  "company": "New Acme Corp",
  "tagIds": ["customer"],
  "introducedById": null
}

The body uses the same field shape as create, with two differences:

  • All fields are optional. Only the keys you include are updated.
  • Most identity fields accept null to clear the value (e.g., "company": null clears the company).
  • tagIds: [] clears all tags. tagIds: ["a"] replaces the entire set with ["a"] — patch semantics are full-set replacement, not delta.

After the update, the contact must still have at least one of givenName, familyName, company, or primaryEmail. A patch that empties all four is rejected with 422.

Response

{ "data": { ... } }

Delete a Contact

DELETE /api/v1/contacts/8a1d...

Response

{ "success": true }

In read-only Google sync mode, deleting a Google-sourced contact returns an error — the next sync would resurrect it. See Why some deletes are blocked for the rules and what to do instead.

There is no recycle bin for deleted contacts today. The deleted record is kept in storage but not reachable from the API or the UI.

Example: Creating a Contact in JavaScript

const res = await fetch("https://app.remy.com/api/v1/contacts", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    Authorization: `Bearer ${process.env.REMY_API_KEY}`,
  },
  body: JSON.stringify({
    givenName: "Carol",
    primaryEmail: "carol@startup.io",
    tagIds: ["founder"],
  }),
});

if (!res.ok) {
  console.error(await res.json());
  throw new Error(`Failed: ${res.status}`);
}

const { data: contact } = await res.json();
console.log("Created", contact.id);

Audit Trail

Every mutation through these endpoints lands in the Activity Log with authMethod: "api_key" and the IP and user-agent of the calling client. Helpful when debugging which script touched which contact.

On this page