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:
- Bearer token header (required going forward)
apiKeyquery parameter (deprecated — still accepted, but emits aDeprecationresponse 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, default10)offset(optional, default0)
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:
cdIdfirstNamemiddleNamelastNamefullnameemailprimaryPhonephoneMobilephoneWorkphoneHomeorganizationjobTitletagsnotescreatedAt- 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 /whoamito validate keys - Use
GET /workspaceto discover custom field metadata - Use
GET /contacts/GET /contacts/search/POST /contacts/queryfor reads - Use
POST /contactsandPUT /contacts/:idfor writes (send anIdempotency-Keyheader; 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/v2will not change incompatibly once asuccessor exists. - Additive changes (new endpoints, new optional fields, new response fields)are not breaking and may land in
/api/v2at any time. Clients mustignore unknown response fields. - Deprecated inputs respond with an RFC 8594
Deprecation: trueheader (anda human-readableWarning). Currently deprecated: theapiKeyqueryparameter — send the key as anAuthorization: Bearerheader 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.