ContactDrive Core REST API (v2)

This document is for third-party developers integrating with ContactDrive using an API key.

Base URL

https://api.contactdrive.app/api/v2  

Authentication

Send your API key as a Bearer token header:

  1. Bearer token header (required going forward)
  2. apiKey   query parameter (deprecated — still accepted, but emits aDeprecation   response header and will be removed in a future release)

Header auth example

curl --request GET \
  --url 'https://api.contactdrive.app/api/v2/whoami' \
  --header 'Authorization: Bearer <API_KEY>'

Query auth example

curl --request GET \
  --url 'https://api.contactdrive.app/api/v2/whoami?apiKey=<API_KEY>'

Workspace scope

  • API keys are workspace-scoped
  • The workspace is resolved from the API key
  • You do not pass a workspace ID in these REST calls
  • All results are constrained to the workspace tied to the key

Endpoints most relevant to contact integrations

GET /whoami  

Use this first to validate your API key and discover the resolved user/workspace context.

Success response (200)

{
  "userId": "abc123",
  "firstName": "Jane",
  "lastName": "Doe",
  "emailAddress": "jane@example.com",
  "workspaceId": "my-workspace",
  "workspaceName": "My Workspace"
}

Unauthorized response (401)

{
  "error": {
    "code": "UNAUTHORIZED",
    "message": "A valid API key is required."
  }
}

Error envelope

All error responses use a consistent shape. field   is present on validation

failures and identifies the offending input path:

{
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request body must include a \"contact\" object.",
    "field": "contact"
  }
}

Error codes include UNAUTHORIZED   (401), VALIDATION_ERROR   (422),

NOT_FOUND   (404), IDEMPOTENCY_CONFLICT   (409), and INTERNAL_ERROR   (500).

Idempotency

Send an Idempotency-Key   header on POST  /PUT  /PATCH   requests. A repeated

request with the same key (same workspace + route, within 24h) returns the

original response without re-executing.

Dry run

Add ?dryRun=true   to a POST  /PUT   request to validate and authorize it and

receive the would-be record ({ "data": { "dryRun": true, "wouldWrite": … } }  )

without writing anything.


GET /workspace  

Returns workspace metadata including custom contact field definitions and activity types.

Success response (200)

{
  "workspaceId": "my-workspace",
  "workspaceName": "My Workspace",
  "status": "active",
  "customFields": [
    {
      "value": "customFields.donor_level",
      "label": "Contacts - Donor level"
    }
  ],
  "activityTypes": [
    {
      "value": "call",
      "label": "Phone Call",
      "helpText": ""
    }
  ]
}

GET /contacts  

Returns a paginated list of contacts for the API key's workspace.

Query parameters

  • limit   (optional, default 10  )
  • offset   (optional, default 0  )

Example

curl --request GET \
  --url 'https://api.contactdrive.app/api/v2/contacts?limit=10&offset=0' \
  --header 'Authorization: Bearer <API_KEY>'

Response shape

Returns an array of flattened contact objects. Common keys include:

  • cdId  
  • firstName  
  • middleName  
  • lastName  
  • fullname  
  • email  
  • primaryPhone  
  • phoneMobile  
  • phoneWork  
  • phoneHome  
  • organization  
  • jobTitle  
  • tags  
  • notes  
  • createdAt  
  • Custom fields by slug (for example: donor_level  )

POST /contacts  

Creates a contact in the API key's workspace. Body: { "contact": { … } }  .

Supports ?upsertByExternalId=true   (update an existing contact matched on a

shared externalIds   entry) and ?dryRun=true  .


Returns 201   with { "data": { "contactId", "workspaceId", "created": true } }  .


PUT /contacts/:contactId  

Updates the supplied fields on a contact. System-managed fields

(workspaceId  , createdBy  , _id  ) are ignored. Supports ?dryRun=true  .


POST /contacts/query  

Structured field-level query (AND-joined { field, op, value }   conditions,

countOnly   supported). See the route comment in contacts/endpoints.js  .


GET /contacts/search  

Text (q  ) and external-ID search with pagination.

Integration guidance

  • Use GET /whoami   to validate keys
  • Use GET /workspace   to discover custom field metadata
  • Use GET /contacts   / GET /contacts/search   / POST /contacts/query   for reads
  • Use POST /contacts   and PUT /contacts/:id   for writes (send anIdempotency-Key   header; preview with ?dryRun=true  )

Machine-readable contract

A full OpenAPI 3.1 description is served, unauthenticated, at:

GET /api/v2/openapi.json

Versioning & deprecation policy

  • The API is versioned in the path (/api/v2  ). Breaking changes ship under anew path segment (/api/v3  ); /api/v2   will not change incompatibly once asuccessor exists.
  • Additive changes (new endpoints, new optional fields, new response fields)are not breaking and may land in /api/v2   at any time. Clients mustignore unknown response fields.
  • Deprecated inputs respond with an RFC 8594 Deprecation: true   header (anda human-readable Warning  ). Currently deprecated: the apiKey   queryparameter — send the key as an Authorization: Bearer   header instead.
  • A deprecated feature stays functional for at least one successor versionbefore removal.

Other mounted REST paths (not documented here)

  • /activity  
  • /apps  
  • /services  
  • /webhooks  

These exist in the server routing layer, but this document is focused on third-party contact integration paths.

Did this answer your question? Thanks for the feedback There was a problem submitting your feedback. Please try again later.

Still need help? Contact Us Contact Us