
iCloud Contacts speaks CardDAV, a standard protocol (RFC 6352) layered on WebDAV and HTTP. The discovery entry point is contacts.icloud.com on port 443 (SSL required). You authenticate with your Apple ID email and an app-specific password, discover your address book home in two PROPFIND requests, choose the specific collection to sync, then read and write individual contacts as vCard files. The sections below walk through each step and explain how ETag-based conflict detection keeps concurrent updates safe.
Apple ID with two-factor authentication. App-specific passwords cannot be generated without 2FA enabled on your Apple Account.
An app-specific password. Go to account.apple.com, open Sign-In and Security, and select App-Specific Passwords (support.apple.com/en-us/102654). Generate one and save it — Apple shows it only once. Use your full Apple ID email as the CardDAV username.
Alternative for supported apps. Apple documents a third-party app authorization path for clients that access iCloud Mail, Calendar, and Contacts (support.apple.com/en-us/121539). When a client supports this flow, users enter their Apple Account email address and click Allow in Apple's prompt rather than generating an app-specific password manually. If your client does not support it, the app-specific password path works for CardDAV tools.
SSL/TLS. Port 443 and a valid certificate chain are required. Never skip SSL certificate verification.
CardDAV clients must not hardcode a contact list URL. iCloud routes accounts to partition hosts, and that destination can change. Use the two-stage discovery defined in RFC 6352 and RFC 6764.
Stage 1 — well-known redirect. Send an authenticated PROPFIND to the well-known URI:
PROPFIND https://contacts.icloud.com/.well-known/carddav
iCloud returns a redirect to your user principal URL on a partition host. Follow it with your credentials.
Stage 2 — find the address book home set. PROPFIND the principal URL for CARDDAV:addressbook-home-set:
PROPFIND {principal-url}
Depth: 0
Content-Type: application/xml
<D:propfind xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav">
<D:prop>
<C:addressbook-home-set />
</D:prop>
</D:propfind>
The response contains a URL identifying the home collection. PROPFIND that URL with Depth: 1 to list the individual address book collections and their DAV:displayname values.
Stage 3 — select the right address book. An account can contain multiple address book collections — the default address book, collections created by other apps, and so on. Read the DAV:displayname of each collection and choose the specific one you intend to sync. Never assume the first result is the only collection, and never blindly overwrite all collections without first inspecting their contents. Cache the principal URL per session; re-run discovery after a 301 or 404 on a previously valid path.
RFC 6352 also recommends requesting DAV:current-user-principal (defined in RFC 5397) in the same first PROPFIND. Many clients batch both properties in one request to save a round trip.
Fetch a single contact. GET the resource URL returned by the collection PROPFIND. The server responds with a text/vcard body. iCloud supports vCard 3.0 (RFC 2426) at minimum; RFC 6352 says servers should also support vCard 4.0 (RFC 6350), so clients that handle both formats are more robust against future responses.
List contacts efficiently. A REPORT with the addressbook-query report type lets you retrieve multiple vCards — or just their ETags — in one response:
REPORT {addressbook-url}
Depth: 1
Content-Type: application/xml
<C:addressbook-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:carddav">
<D:prop>
<D:getetag />
<C:address-data />
</D:prop>
</C:addressbook-query>
The server returns 207 Multi-Status with one <D:response> per contact. On first sync, request only getetag to build your local inventory cheaply, then fetch address-data only for resources whose ETag has changed since the last sync.
Create or update a contact. PUT a vCard body to {addressbook-url}/{uid}.vcf. RFC 6352 requires each vCard to carry a unique UID property within the collection:
PUT {addressbook-url}/{uid}.vcf
Content-Type: text/vcard
BEGIN:VCARD
VERSION:3.0
UID:{uid}
FN:Alex Chen
N:Chen;Alex;;;
EMAIL;type=INTERNET:alex@example.com
END:VCARD
A 201 Created response confirms the resource was saved and includes an ETag header — store it.
Every contact resource carries an ETag that changes whenever the resource is modified. Use it to guard against overwriting concurrent changes.
Conditional update. Include If-Match with the stored ETag when you PUT an update:
PUT {addressbook-url}/{uid}.vcf
If-Match: "abc123etag"
Content-Type: text/vcard
...
If the resource changed since you read it, the server returns 412 Precondition Failed. Fetch the current version with GET, merge your changes, and retry with the new ETag.
Safe creation. Add If-None-Match: * when creating a new resource to prevent overwriting an existing contact that shares the same URL.
Incremental sync. If the server advertises DAV:sync-collection REPORT support (via DAV:supported-report-set) as defined in RFC 6578, use sync tokens to retrieve only changed and deleted resources since your last sync, rather than comparing ETags across the full collection.
| Symptom | Likely cause | Fix |
|---|---|---|
401 Unauthorized immediately | Apple Account password used instead of app-specific password | Generate an app-specific password at account.apple.com |
401 with correct credentials | 2FA not enabled | Enable 2FA before generating app-specific passwords |
Client stuck on contacts.icloud.com | Redirect not followed with credentials | Configure the client to resend credentials on redirects |
404 on a previously valid principal URL | Partition routing changed | Re-run well-known discovery to get the current URL |
412 Precondition Failed on PUT | Stored ETag is stale | GET the latest version, merge your changes, PUT with the new ETag |
Figment Apple Connect provides a synthetic read-only sandbox for Contacts. The sandbox endpoint returns invented sample records — useful for building and testing your sync integration before connecting to live CardDAV data.
GET https://figment.so/api/apple-connect/v1/connections/{connection_id}/contacts
Authorization: Bearer {ACCESS_TOKEN}
Each item in the response includes id, name, organization, emails, and phones. The response envelope includes mode: "sandbox" and scope: "synthetic sandbox data". This is not live iCloud Contacts data; live customer access to Contacts through Figment is not yet available. For live contact sync, the CardDAV protocol described in this article is the Apple-supported path.
See Connect iCloud Mail, Calendar, and Contacts for the multi-protocol overview, Read and Update iCloud Calendars with CalDAV for the calendar equivalent, Connect iCloud Mail with IMAP and SMTP for the email protocol, and OAuth for iCloud: Connect Apple Data to Your App for the full Figment grant model.
Connect Apple Account to get sandbox access and explore Contacts data with synthetic records.