← Back to Plugins
Tools

Icloud Contacts

omarshahine By omarshahine 👁 20 views ▲ 0 votes

OpenClaw plugin: Apple iCloud Contacts over CardDAV (Apple ID + app-specific password), zero-dep

GitHub

Install

npm install
npm

Configuration Example

{
     "plugins": {
       "entries": {
         "openclaw-icloud-contacts": {
           "enabled": true,
           "config": {
             "appleId": "[email protected]",
             "appPassword": "${ICLOUD_APP_PASSWORD}"
           }
         }
       }
     }
   }

README

# openclaw-icloud-contacts

OpenClaw plugin that reads and writes **Apple iCloud Contacts** directly over CardDAV, using an
Apple ID and an app-specific password. Server-to-server: it runs wherever your OpenClaw gateway
runs (Linux, a Raspberry Pi, a VPS). No macOS, no Contacts.app, no phone in the loop.

Zero runtime dependencies for the CardDAV path: `fetch` plus a small XML/vCard layer written for
exactly the responses iCloud sends. Sister plugin of
[openclaw-icloud-calendar](https://github.com/omarshahine/openclaw-icloud-calendar).

## Tools

| Tool | What it does |
|------|--------------|
| `icloud_contacts_search` | Find contacts by name, email, phone (digits), organization, or any |
| `icloud_contacts_get` | One contact by id, all fields; optional photo as base64 |
| `icloud_contacts_list` | Browse sorted by name with paging; optionally one group |
| `icloud_contacts_groups` | List groups, list members, create, add/remove members, delete |
| `icloud_contacts_create` | New contact (name or organization required) |
| `icloud_contacts_update` | Change fields by id (etag-protected; arrays replace) |
| `icloud_contacts_delete` | Delete a contact by id |

Set `readOnly: true` and the write tools are not registered at all (group mutations also refuse).

## Install

```bash
openclaw plugins install clawhub:openclaw-icloud-contacts
```

## Setup

1. Turn on two-factor authentication for your Apple Account if it is not already on.
2. Create an app-specific password at <https://account.apple.com> → Sign-In and Security →
   App-Specific Passwords. Copy the `xxxx-xxxx-xxxx-xxxx` value.
3. Make it available to the gateway as an environment variable named `ICLOUD_APP_PASSWORD` (or store it
   in `~/.openclaw/secrets.json` and reference it with a SecretRef, see below).
4. Configure the plugin in `openclaw.json`:

   ```jsonc
   {
     "plugins": {
       "entries": {
         "openclaw-icloud-contacts": {
           "enabled": true,
           "config": {
             "appleId": "[email protected]",
             "appPassword": "${ICLOUD_APP_PASSWORD}"
           }
         }
       }
     }
   }
   ```

   `appPassword` also accepts a SecretRef (`{"source":"env","provider":"env","id":"ICLOUD_APP_PASSWORD"}`
   or `{"source":"file","provider":"secrets","id":"/icloud/appPassword"}` into `~/.openclaw/secrets.json`).
   Without config, `ICLOUD_APPLE_ID` and `ICLOUD_APP_PASSWORD` environment variables are used.
   The same app-specific password works for the calendar plugin.

## Configuration

| Key | Type | Default | Notes |
|-----|------|---------|-------|
| `appleId` | string | | Apple Account email. Required. |
| `appPassword` | string / `${ENV}` / SecretRef | | App-specific password. Required. Never your Apple Account password. |
| `serverUrl` | string | `https://contacts.icloud.com` | Must be `https`. |
| `readOnly` | boolean | `false` | No writes; create/update/delete not registered. |
| `groups` | string[] | all | Allowlist: only members of these groups are visible or editable. |
| `defaultLimit` | integer | 25 | Default page size for list/search. |
| `maxContacts` | integer | 10000 | Refuse to sync larger address books. |

## Behavior worth knowing

- **Sync cache.** First call runs an RFC 6578 `sync-collection` plus batched `addressbook-multiget`
  (about 4 s for 600 contacts); later calls send only the sync token and fetch changed cards. iCloud
  invalidates tokens occasionally; the plugin resyncs transparently. Search is client-side because
  iCloud's `addressbook-query` silently downgrades `allof` to `anyof` and matches case-sensitively.
- **Contact JSON** mirrors apple-pim's contact tool: brief records for lists/searches, full records
  with labeled entries for get/create/update. `id` is the vCard UID (stable across devices).
- **Apple vCard dialect.** vCard 3.0 only. Labels use Apple's `itemN.X-ABLabel` grouping and
  `_$!<Home>!$_` sentinels; standard labels map to TYPE params, custom labels round-trip as text.
  Unknown properties are preserved on update.
- **Photos** are external URIs on iCloud; `photoUrl` is always returned, `includePhoto=true` fetches
  it (authenticated) and returns base64. Photos larger than 5 MB are not returned.
- **Credential hygiene.** The Apple ID and app password are sent only to hosts within the configured
  server's domain (`*.icloud.com` by default). Redirects to other hosts are refused, and photo URLs on
  other hosts are not fetched, so a shared card cannot steer your credentials elsewhere.
- **Groups** are Apple `X-ADDRESSBOOKSERVER-KIND:group` cards; membership is by contact UID.
- **Concurrency.** Writes send `If-Match`; one re-read + retry on 412, then `conflict`.
- **Rate limits.** iCloud throttles roughly 15-20 writes/minute (503). The plugin has no bulk write
  tool on purpose; batch mutations should be paced.
- **Untrusted content.** Names, organizations, titles, nicknames and notes are wrapped in per-session
  `[UNTRUSTED_CONTACT_DATA_...]` markers and instruction-like text is flagged.
- **Errors.** `error.code`: `auth_failed`, `not_found`, `conflict`, `read_only`, `invalid_input`,
  `not_configured`, `server_error`.

## Privacy

An address book is more sensitive than a calendar. Defaults are conservative: brief shape, small
limits, no photos unless asked. For shared or family gateways use `groups` to expose only a slice,
or `readOnly` to prevent edits. Consider a dedicated agent with only the read tools allowed.

## Development

```bash
npm install
npm test                # unit + fake-server tests, no network
npm run typecheck && npm run build
npm run plugin:check    # @openclaw/plugin-inspector
```

Live test against a real account (syncs the whole book read-only, then creates and deletes one
`[openclaw-test]` contact and group):

```bash
ICLOUD_INTEGRATION=1 [email protected] ICLOUD_TEST_APP_PASSWORD=<app-specific password> npm run test:integration
```

## Not in v1

Multiple address books, vCard 4 export, contact photo upload, merging duplicates, bulk import.

## License

MIT
tools

Comments

Sign in to leave a comment

Loading comments...