Skip to content

CardDAV

Nametag can sync bidirectionally with a CardDAV server (Google Contacts, iCloud, Outlook, Nextcloud, and others). This page covers the API surface; see CardDAV Sync for the feature overview and provider setup.

Only one CardDAV connection is allowed per user. All endpoints require authentication.

POST /api/carddav/connection
FieldTypeNotes
serverUrlstring (URI)Required.
usernamestringRequired.
passwordstringRequired.
providerstring or nullHint: google, icloud, outlook, nextcloud, custom.
syncEnabledboolean
autoExportNewbooleanAuto-export new Nametag contacts.
autoSyncIntervalintegerSeconds between automatic syncs, 60-86400.
importModemanual | notify | autoHow new remote contacts are handled.
cardDavNameFormatFULL | FIRST_LAST | NICKNAME_PREFERRED | SHORTName format used for the vCard FN field.
Terminal window
curl -X POST https://your-instance.example.com/api/carddav/connection \
-H "Authorization: Bearer ntag_xxx" \
-H "Content-Type: application/json" \
-d '{
"serverUrl": "https://contacts.icloud.com/",
"username": "ada@example.com",
"password": "app-specific-password",
"provider": "icloud",
"syncEnabled": true
}'
{ "connection": { "id": "clxconn1", "serverUrl": "https://contacts.icloud.com/", "syncEnabled": true } }

Returns 409 if a connection already exists.

PUT /api/carddav/connection

Same fields as create, except password is optional (only changed if provided).

Terminal window
curl -X PUT https://your-instance.example.com/api/carddav/connection \
-H "Authorization: Bearer ntag_xxx" \
-H "Content-Type: application/json" \
-d '{ "serverUrl": "https://contacts.icloud.com/", "username": "ada@example.com", "autoSyncInterval": 900 }'
{ "connection": { "id": "clxconn1", "autoSyncInterval": 900 } }
DELETE /api/carddav/connection

Disconnects and deletes the connection along with all sync mappings and pending imports.

Terminal window
curl -X DELETE https://your-instance.example.com/api/carddav/connection \
-H "Authorization: Bearer ntag_xxx"
{ "success": true }
POST /api/carddav/connection/test

Tests connectivity without saving credentials.

Terminal window
curl -X POST https://your-instance.example.com/api/carddav/connection/test \
-H "Authorization: Bearer ntag_xxx" \
-H "Content-Type: application/json" \
-d '{ "serverUrl": "https://contacts.icloud.com/", "username": "ada@example.com", "password": "app-specific-password" }'
{ "success": true, "message": "Connection successful" }

Returns 401 for bad credentials, 404 if the server can’t be found, 408 on timeout.

POST /api/carddav/sync

Triggers a full sync and streams progress as Server-Sent Events. Event types: progress (in-flight updates), complete (final counts), error.

The complete event payload includes imported, exported, updatedLocally, updatedRemotely, conflicts, errors, errorMessages, and pendingImports.

Terminal window
curl -N -X POST https://your-instance.example.com/api/carddav/sync \
-H "Authorization: Bearer ntag_xxx"
event: progress
data: {"phase":"fetching","message":"Fetching remote changes"}
event: complete
data: {"imported":2,"exported":1,"updatedLocally":0,"updatedRemotely":0,"conflicts":0,"errors":0,"errorMessages":[],"pendingImports":0}
POST /api/carddav/discover

Scans the server for contacts not yet imported and creates pending import records.

Terminal window
curl -X POST https://your-instance.example.com/api/carddav/discover \
-H "Authorization: Bearer ntag_xxx"
{ "success": true, "discovered": 5, "errors": 0, "errorMessages": [] }
GET /api/carddav/pending-count

Used for a notification badge.

Terminal window
curl https://your-instance.example.com/api/carddav/pending-count \
-H "Authorization: Bearer ntag_xxx"
{ "count": 5 }
POST /api/carddav/import

Imports previously discovered pending contacts.

FieldTypeNotes
importIdsstring[]Required, at least 1. IDs of pending imports to import.
globalGroupIdsstring[]Group IDs to assign to all imported contacts.
perContactGroupsobjectMap of import ID to group IDs, for per-contact assignment.
Terminal window
curl -X POST https://your-instance.example.com/api/carddav/import \
-H "Authorization: Bearer ntag_xxx" \
-H "Content-Type: application/json" \
-d '{ "importIds": ["clxpend1", "clxpend2"], "globalGroupIds": ["clxgroup1"] }'
{ "success": true, "imported": 2, "skipped": 0, "errors": 0, "errorMessages": [] }
POST /api/carddav/export-bulk

Exports selected Nametag contacts to the connected server, in batches of 50.

Terminal window
curl -X POST https://your-instance.example.com/api/carddav/export-bulk \
-H "Authorization: Bearer ntag_xxx" \
-H "Content-Type: application/json" \
-d '{ "personIds": ["clx1", "clx2"] }'
{ "success": true, "exported": 2, "skipped": 0, "errors": 0, "errorMessages": [] }
POST /api/carddav/backup

Downloads every contact from the connected server as a single .vcf file.

Terminal window
curl -X POST https://your-instance.example.com/api/carddav/backup \
-H "Authorization: Bearer ntag_xxx" \
-o nametag-backup.vcf

Response headers include Content-Disposition: attachment; filename="nametag-backup.vcf" and X-Contact-Count.

POST /api/carddav/conflicts/{id}/resolve
FieldTypeNotes
resolutionkeep_local | keep_remote | mergedRequired.
Terminal window
curl -X POST https://your-instance.example.com/api/carddav/conflicts/clxconf1/resolve \
-H "Authorization: Bearer ntag_xxx" \
-H "Content-Type: application/json" \
-d '{ "resolution": "keep_local" }'
{ "success": true }

Direct, one-off .vcf file import (without a CardDAV server connection) is covered in Import & Export.

GET /api/cron/carddav-sync

Syncs every user with CardDAV enabled who is due for a sync, with a 200ms delay between users. Authenticated with a bearer token matching the CRON_SECRET environment variable, not a user session or API token. Intended to be called by your deployment’s scheduler, see Cron Jobs.

Terminal window
curl https://your-instance.example.com/api/cron/carddav-sync \
-H "Authorization: Bearer $CRON_SECRET"
{ "success": true, "total": 40, "synced": 38, "skipped": 1, "errors": 1, "errorMessages": ["..."] }