-
-
Notifications
You must be signed in to change notification settings - Fork 29
CardDAV Contact Sync
Shipped in 1.8.0. Asked for by mbsouth in #133.
The people who raise tickets with you almost certainly exist already, in a shared address book, with their phone numbers and job titles kept current by somebody. Setting FreeITSM up meant typing a lot of them in a second time β and from that moment the two lists start drifting apart.
This reads that address book and keeps the FreeITSM copy up to date. Change somebody's mobile number where you already maintain it, and the next import changes it here.
β οΈ Nobody signs in through this. It is configured on System β Authentication, alongside single sign-on and LDAP, and it is the odd one out on that screen: an address book is not an identity provider, has no login button, and grants nobody access to anything. It imports contact details. It is there because it is another source of people, and because it shares its safety rules with the LDAP import.
| Direction | One way only β address book β FreeITSM. Nothing is ever written back. |
| Who it creates | Self-service users, the people who raise tickets. Never analysts. |
| What it needs | An account that can read. No write access, ever. |
| What it changes at your end | Nothing at all. |
That last row is worth saying plainly: it is safe to point this at an address book you rely on for other things. FreeITSM does not add, edit, delete or reorganise anything in it.
If you want your service desk staff to come from an external system, that is LDAP and Active Directory instead.
Any of them β CardDAV is a standard, and FreeITSM asks the server what it holds rather than assuming. Built and tested against BaΓ―kal 0.12.1, the server the request came from. The same setup applies to Nextcloud, ownCloud, Radicale, or anything else built on sabre/dav.
System β Authentication β + Add, then set Type to CardDAV address book (contacts only, no sign-in). You get a page with three tabs: Connection, Contacts and History.
| Field | What to put |
|---|---|
| Display name | Whatever you will recognise it by. It is never shown to anybody signing in, because nobody signs in. |
| Server URL | The CardDAV path, not the web page you log into. On a sabre/dav server it looks like https://dav.example.com/dav.php/addressbooks/jsmith/. |
| Username / password | A read-only account if your server can make one. Many servers prefer an app password to the account password. |
| Authentication | Leave on Automatic. |
π Point the URL at the level that holds your address books, not at one book. FreeITSM will list what it finds there and let you choose β which is more reliable than getting one book's exact path right by hand.
π΄ Leave Authentication on Automatic. A stock BaΓ―kal wants Digest, not Basic β and Basic is what almost everything reaches for first. Automatic asks the server which it wants and uses that, and the connection test tells you which one won. This is the single most likely cause of "the password is definitely right and it still won't connect", so it is worth not having to know.
Pressing Test connection is how the rest of the form gets filled in. FreeITSM signs in, asks your server which address books the account can see, and lists them for you to pick from.
Until a test has succeeded there is genuinely nothing to choose from, and the form says so rather than offering you an empty list. If you change the URL or the password later, test again β the lists belong to the previous connection until you do.
An address book usually holds more than belongs in a service desk: suppliers, family, the plumber. So having picked a book, you choose what to take from it:
| Option | What you get |
|---|---|
| Everyone in the book | Every contact in it. Right when the book exists for this purpose. |
| Chosen groups | Only members of the contact groups you tick. What most address-book apps call a "group" or a "list". |
| Chosen categories | Only contacts carrying the tags you tick. The per-contact labels your app may call "tags". |
Both are tick boxes, not a single choice β three groups out of nine is a perfectly good answer β with All and None links so a long list is not a long click. The original request was for one named group, which would have worked; letting the server enumerate what it actually has turned out to be both more flexible and less to explain.
Not sure which your address book uses? Test the connection and look. An empty groups list means nothing in that book uses groups β try categories, or bring in everyone and narrow it down later. Changing your mind costs one re-run.
Group cards are not people. An address book stores a group as a card of its own, so "Acme Staff" is a record in the same list as the people in it. Those are skipped, and you will never find a contact called Acme Staff sitting in your people list looking like somebody who might answer the phone.
Save, then use Preview on the Connection tab. Preview is the same code as a real import with the writing switched off, so what it tells you is what would happen, not an estimate. Worth doing every time, not just the first.
Seven things, each mapping onto a field already on a person in FreeITSM:
| In FreeITSM | Comes from the contact card |
|---|---|
| Name | Their display name β falling back to the structured name, then email, then their id, so a person never lands as a blank row. |
| Their first valid email address. Also how a contact is matched to somebody already here. | |
| Job title | Their title. |
| Department | The second part of the organisation field, which is where address books keep a department. |
| Office | The town or city from their address, preferring a work address. |
| Phone and Mobile | Their numbers, split by the type the card gives each one. |
- Employee ID β a contact card has nowhere to keep one. Left empty rather than filled with something that merely looks like one.
- Manager β the standard does have a way to record it, and almost nothing writes it. The reporting line is left untouched rather than half-imported.
- A sign-in β contacts get no username and no password. They can still use the self-service portal; you grant that the usual way.
A contact with no unique id is skipped, and counted. Every card carries one, and it is what lets FreeITSM recognise the same person next time even after a rename or a move between books. A card without one would be imported as a new person on every single run.
Matching is by email address, and the When somebody is already here setting decides what happens:
- Link them to this address book β from now on the import keeps them up to date.
- Leave them alone and flag it β nothing changes, and you get told.
Once somebody is linked, those details become read-only on the people screens, and the screen says why. The address book is the source of truth and the next import would overwrite anything typed here, so the save is refused up front rather than accepted and quietly undone overnight. Change it in the address book instead. See Contact details.
An import is the kind of job that is quietly destructive when it goes wrong, so it is built around three rules. They are the same rules, in the same code, as the LDAP import, and they are not optional.
| Rule | What it means for you |
|---|---|
| Nobody is ever deleted | The worst an import can do to a person is mark them as having left. Their tickets and history stay put. |
| A run that looks wrong changes nothing | If an import finds far fewer contacts than last time β a fifth fewer, by default β it stops before touching anything and says why. |
| Missing once is noise | Absent from one import is not a leaver. Three imports running (your setting) before anyone is marked as left. Set it to 0 and that never happens automatically. |
Rule 2 is the one that matters most here, because pointing at the wrong address book looks exactly like everybody leaving at once. A scope that matches nothing stops the run too: a group renamed on your server is far likelier than your entire customer base resigning.
A first import is never braked β there is nothing to compare against yet.
Nothing imports on its own. You either press Run on the Connection tab, or you set up a scheduled task β the same one that runs an LDAP import:
php scripts/directory_sync.php --all
That picks up every enabled source with importing switched on β address books and directories alike β and sends each through the right engine. Nightly is a sensible starting point. It sets a proper exit code, so a monitored task tells you when an import failed rather than quietly doing nothing for six months.
See Scheduled tasks for where this sits among the other jobs.
The History tab lists every import and what it did: whether it was run by hand or by the schedule, and how many contacts were read, created, updated and skipped.
A run shown as stopped was halted by the safety check. Nothing was changed, and it is waiting on you rather than broken.
"Connected, but found no address books."
The URL is a level too deep or too shallow, or it is the web interface rather than the CardDAV path. The error says which of the two it looks like. On a sabre/dav server the path contains /dav.php/addressbooks/.
It works in a browser but not here. Your browser had a session; this does not. Check the username and password on their own, and try an app password.
"Wrong username or password" β but they are right. Set Authentication explicitly to Digest, then to Basic. Automatic reports which schemes the server offered; if that list is empty, something in front of the server is stripping the challenge.
The groups and categories lists are empty. Either nothing in that book uses them, or the test ran before you chose the book. Choose the address book, test again, and look at both lists.
It stopped and changed nothing. That is rule 2 working. Either the scope matched nothing β a renamed group, usually β or the count dropped sharply. Preview, check the numbers, run again.
Fewer contacts than the book holds. Group cards are skipped, and so is any contact with no unique id. The run summary counts those separately from what it brought in.
Somebody's details are not updating. They are probably not linked to this address book β check the conflict setting, and whether they were created here by hand before the import existed. Only people the import manages are kept current by it.
Nothing has imported for weeks. An import runs only when something runs it. Check the scheduled task exists and is firing; History shows the last run and where it came from.
By default the import only ever reads. Switch on Write changes made in FreeITSM back to the address book, on the Contacts tab, and it works the other way too: when an analyst corrects a contact's details, the contact card is updated as well.
This is the half that answers the GDPR argument in the original request, and it is the harder half β so it is worth knowing exactly what it does.
What it changes: job title, department, office, phone and mobile. Employee number and manager stay in FreeITSM, because a contact card has nowhere to keep them β which is also why those two are yours to fill in freely even on an imported contact.
What it never touches: everything else on the card. FreeITSM edits the single line that changed and leaves the rest exactly as it found it β photo, notes, birthday, the full street address, group membership, and anything your other systems have added. It does not rebuild the card from what it knows.
When two people change the same thing: before writing, FreeITSM re-reads the card and compares it with what it last imported. If somebody has changed that same detail in the address book meanwhile, the change is refused and you are told what their version says. If they changed something else, that is left alone and your change goes through. A refusal is not an error to work around β it means two people had different information.
Check permission first. The button on the Contacts tab asks the server what your account is allowed to do, without writing anything. Shared and subscribed address books are commonly read-only however correct the password is, and it is far better to know here than to find out halfway through somebody's edit. If the answer is no, grant write access on the address book server; nothing in FreeITSM can work around a permission the server has not given.
Someone who emailed in exists in FreeITSM but not in your address book. On the address book's Contacts tab, tick Let analysts add people to this address book - it needs write-back on, and is off by default, because for some organisations being in the address book is the record of who they may hold details about.
Analysts then get Add to address book on that person in Tickets β Users and Assets β Users. It asks first, showing what goes on the card, then creates the contact and links the person - their existing FreeITSM record, never a copy.
- No duplicates. If a card in the book already has their email address, nothing is written; run the import and it links that card instead. The link records the new card's own ID, so the next import recognises the person and neither creates nor adopts anyone.
- It lands where the import looks. If the address book only imports one tag or group, the new card gets the tag or joins the group. FreeITSM then reads the book back and asks the import's own rules whether the card would be picked up; if not, it deletes the card again and links nobody. Otherwise the next imports would not see the person and would, after three runs, mark them as having left.
- It appears in the write log as Added.
Your work is never lost. The person's record is saved in FreeITSM first and completely. Sending the change onward happens afterwards and cannot undo it, so an address book that is down, read-only or holding a newer value never costs you the edit you just made.
You are always told. A save that reached the card says so; one that did not says what stopped it. An unreachable server costs a couple of seconds rather than holding the form open β a write-back runs on a shorter time limit than an import.
Every attempt is recorded. The History tab has a Changes sent back section: who, when, which details, and what the server itself said, kept word for word rather than tidied up. When an address book refuses something the reason is in its own response, and that is the thing worth having when the server is one FreeITSM cannot log in to.
| It says | It means |
|---|---|
| Written | the contact card was updated |
| Refused | somebody had already changed the same detail there. Nothing was overwritten |
| Failed | the server could not be reached, or said no |
| Nothing to send | the card already matched |
"Refused" is not a fault. It is the safety net doing its job. Import first to see their version, then decide which is right.
Two tools under System β Debug Tools, answering two different questions.
D015 β CardDAV health. Walks the connection one rung at a time: reach the server, sign in, read the chosen book, and may this account write to it. They fail for different reasons, so each is reported on its own rather than as one verdict. It names the mismatch that catches people out β write-back switched on while the account has no write permission, which saves every change here and has every one refused β and how many contacts are missing the card reference a write-back needs. It writes nothing to find any of this out.
D016 β CardDAV drift. A top-to-bottom comparison: every imported person against their card as it stands, detail by detail, with both values side by side. Worth running before you switch write-back on, and whenever somebody says a number is out of date and you need to know which side is stale. It also tells you which details drift most across everybody, which is the honest answer to whether keeping them in step is worth the trouble.
D016 changes nothing and offers no "fix it" button on purpose. Which side is right is a judgement, and one button reconciling hundreds of contacts at once is how a careful integration turns into a data-loss incident.
π CardDAV write-back β when it goes wrong covers all of this properly: what each result means and what to do about it, the Digest trap that makes a correct password look wrong, how to read the log, both tools rung by rung, and a symptom-to-cause table.
Calendars are a separate feature. CalDAV calendar sync is built too - see Scheduled work in your own calendar Β§3b.
Portal self-service edits reach the address book only if you allow it. By default a customer correcting their own phone number in the self-service portal is refused on an imported contact, because those five details are owned by the address book. Tick Let them change these too, and send their change to the address book on System β Portal profile and their change is sent to the card first, and saved in FreeITSM only once the address book has accepted it. See Contact details.
- Contact details β the seven fields, and who is allowed to change each one
- Directory sync β the safety rules this shares, in more depth
- LDAP and Active Directory β the other import, for staff
- Scheduled tasks β automating the import
- CardDAV contact sync β developer guide β how the reading half works inside
- CardDAV write-back β when it goes wrong β diagnosing it: the four results, the log, D015 and D016
- CardDAV write-back internals β how the writing half works, and why it is careful
- CalDAV and CardDAV β the original analysis, and CalDAV
FreeITSM β an open-source IT Service Management platform Β· github.com/edmozley/freeitsm Β· MIT licence
- Installation
- β° Scheduled tasks (cron jobs)
- Architecture
- π§ͺ Developer tests
- AI Providers
- Internationalisation (i18n)
- Timezones & Time Handling
- π Date & Time Formats
- Theming & Dark Mode
- ποΈ Recent β getting back to what you were doing
- β¨οΈ Command palette (βK)
- π Searching inside tickets
- π Attached documents
-
MobileβFriendly
- β³ π« Mobile: Tickets
- β³ π» Mobile: Assets
- β³ π Mobile: Calendar
- β³ π Mobile: Knowledge
- β³ π¦ Mobile: Service Status
- β³ πΌ Mobile: Watchtower
- β³ π§© Mobile: Problem Management
- β³ π Mobile: Change Management
- β³ πΏ Mobile: Software
- β³ β Mobile: Tasks
- β³ π Mobile: Forms
- β³ π Mobile: Contracts
- β³ π Mobile: Domains
- β³ π Mobile: People
- β³ π Mobile: LMS
- β³ πΊοΈ Mobile: CMDB
- β³ πΊοΈ Mobile: Network Mapper
- β³ π§ Mobile: Process Mapper
- β³ βοΈ Mobile: Workflow
- β³ π₯οΈ Mobile: System
- β³ π Mobile: Reporting
- β³ π Mobile: System Wiki
- β³ π Mobile: Self-Service Portal
- β³ π§° Mobile: Techniques & Tricks
-
Security
- Layer 1 β which modules you can enter
- β³ π§© Module Access Control
- β³ π οΈ Module Access β Developer Guide
- Layer 2 β what you can administer
- β³ π Roles & Permissions
- β³ π οΈ Roles β Developer Guide
- β³ π€ Why capabilities are constants
- Layer 3 β the System module
- β³ π Admin Access Control
- Hardening
- β³ π Security review response 2026-08
- β³ π‘οΈ Security hardening 2026-08
- β³ π οΈ Security hardening 2026-08 β Developer Guide
- β³ π‘οΈ Round three β plain English
- β³ π οΈ Round three β Developer Guide
- β³ π‘οΈ CSRF protection (S4) β Developer Guide
- Single Sign-On (SSO)
- ποΈ LDAP & Active Directory
- π CardDAV contact sync
- Browser Extension
- API Reference
-
π REST API β how it works
- β³ π« REST API: Tickets
- β³ π» REST API: Assets
- β³ π΄ REST API: Problems
- β³ π REST API: Changes
- β³ π REST API: Knowledge
- β³ β REST API: Tasks
- β³ ποΈ REST API: CMDB
- β³ π REST API: Contracts
- β³ ποΈ REST API: Calendar
- β³ πΏ REST API: Software
- β³ π REST API: Domains
- β³ π¦ REST API: Service Status
- β³ βοΈ REST API: Morning Checks
- β³ π REST API: Forms
- β³ βοΈ REST API: Workflow
- β³ π·οΈ REST API: Cost centres
- β³ πΊοΈ REST API: Network Mapper
- β³ π§ Using the API docs page
- β³ π OpenAPI specification
- β³ β OpenAPI: kept correct
- β³ π οΈ Maintaining the catalogue
- Watchtower
-
Tickets
- β³ π Rota copy and paste β Developer Deep Dive
- β³ β Checklists & SOPs
- β³ βοΈ Mandatory fields
- β³ π·οΈ Ticket categories
- β³ π₯ Assigning tickets to a team, and escalation
- β³ π’ One board across every company
- β³ Mailbox Authentication
- β³ π€ Email send log
- β³ Basic IMAP mailboxes
- β³ Email rendering & images
- β³ SLA Management
- β³ WhatsApp channel
-
β³
βοΈ Telegram channel - β³ β CSAT company scope and filters β Developer Guide
- β³ π₯ Microsoft Teams channel
- β³ π¨οΈ Mattermost channel
- β³ π¬ Web chat channel
- β³ π£ Slack channel
- β³ π Linking tickets
- β³ β Record previews
- β³ π Ticket notes: internal or shared
- β³ ποΈ Canned responses
- β³ βοΈ Limiting replies to particular senders
- β³ π¨ Telling the analyst a ticket is theirs
- β³ βοΈ Email signatures
- β³ π The public web address
- β³ π’ Ticket numbering
- β³ π Raising a ticket for someone else
- β³ π Merging tickets
- β³ π Confidential tickets
- β³ π₯ Portal managers
- β³ π Who has seen a ticket
- β³ π Reading long tickets
- β³ β Splitting tickets
- β³ β Selecting several tickets
- β³ ποΈ The folder pane
- β³ π½ Just my tickets, or no closed ones
- β³ π οΈ Snoozing tickets β Developer Guide
- β³ π₯ Collision detection
- β³ β±οΈ Time tracking
- β³ π Scheduled work in your own calendar
- Problem Management
- Tasks
-
Assets
- β³ π’ Moving an asset between companies
- β³ π Shared asset locations
- β³ π§βπΌ Assigning assets to analysts
- β³ π Warranty and lease alerts
- β³ π Saved table views
- β³ π¨οΈ Recording anything, and importing it
- β³ π·οΈ QR asset labels
- β³ π Who holds what, and handover documents
- β³ π₯οΈ The inventory agent (PowerShell)
- β³ ποΈ Proxmox VE servers
- β³ βοΈ VMware Cloud Director servers
- β³ π Linking equipment to tickets
- β³ βοΈ Follow-up tasks on a ticket
- Knowledge
- Change Management
- Calendar
- Morning Checks
- Reporting
- Software
-
Forms
- β³ π¨ The form designer β Developer Guide
- β³ π Layout & the grid β Developer Guide
- β³ ποΈ Collections β grouping submissions
- β³ π Submissions as PDFs
- β³ β‘ What happens next β a form's own actions
- β³ π οΈ Sections & conditional logic β Developer Guide
- β³ π οΈ Lookup fields β Developer Guide
- β³ π‘οΈ Catalogue request approvals
- People
- Domains
- Contracts
- Service Status
- π Notifications
- π¨ War Room
- Self-Service Portal
- LMS
- Process Mapper
- CMDB
- Network Mapper
- Workflows
- Issue trackers (Jira, Azure DevOps)
- System
-
Overview
- β³ π Progress tracker
- β³ Concepts & vocabulary
- β³ Email routing & mailboxes
- β³ Settings: global vs per-company
- β³ Users & self-service
- β³ Staff cross-company access
- β³ π’ One board across every company
- β³ Worked examples
- β³ Pitfalls & gotchas
- β³ Scope: what it's for
- β³ π οΈ Developer Guide (make a module multi-company)
- β³ ποΈ Case study: CMDB (a linked graph)
- β³ π§ͺ Test harness (prove it's isolated)
- What this is
-
π Bugs resolved
- β³ πΌοΈ Logo and courses broke on Apache with PHP-FPM
- β³ π’ Chat tickets ignored your ticket numbering
- β³ π Dates shown as a dash, or in server time
- β³ π Assets β Users showed people from other companies
- β³ π Restricted analysts could read other modules' data
- β³ πΌοΈ Replies with a picture in the thread failed to send
- β³ π Reply attachments never reached the customer
- β³ π οΈ Outbound email attachments β Developer Guide
- β³ π A global SSO provider was missing from the portal
- β³ π Behind a proxy, the SSO redirect said http
- β³ βοΈ The portal tagline moved when you saved it
- β³ π¨ The portal settings screen forgot what you saved
- β³ π‘οΈ The approvals inbox said "Error" and nothing else
- β³ π A table's answers were missing from the PDF
- β³ β A single-select column let you tick every option
- β³ π The portal ignored a form's field widths
- β³ π The tasks board stopped taking clicks
- β³ ποΈ #121 The index list is out of date after upgrading
- β³ π #133 The calendar subscription was empty
- β³ π #131 Tasks always reopened on the board
- β³ π₯ #129 Every page returned HTTP 500 after upgrading
- β³ π³ #127 A PHP warning above the System page
- β³ π #126 Notes stamped with the server's clock
- β³ π Storing every date in UTC
- β³ πͺ The portal was down for everyone signed in
- β³ βοΈ #120 Workflow notes could never be written
- β³ βοΈ #123 Three errors when running Database Verification
- β³ π #122 The description box was a stub in the corner
- β³ π£ Demo data deleted real accounts
- β³ π #117 Sign-in redirected to the wrong address
- β³ π¨ #108 The priority dot was invisible
- β³ β±οΈ #116 Time logged from the right-click menu
- β³ π #114 API keys refused by our own guard
- β³ ποΈ #110 Assigning a task told nobody
- β³ πͺ #107 Signed out while still working
- β³ π #103 "Share with Requester" reached nobody
- β³ π #102 Search found nothing for hyphens
- β³ πͺ #101 Source code editor opened behind
- β³ βοΈ #88 Subtasks could not be ticked off
- β³ π» #84 Asset deep link selected nothing
- β³ π« #79 A new ticket arrived with no status
- β³ π§ #79 A ticket from email did not say so
- β³ π #78 Bell opened to nothing
- β³ π¬ #77 Mail only collected from Inbox
- β³ π #74 The default password could not be changed
- β³ π¦ #70 Renaming an impact level
- β³ π€ #67 App-only mailboxes could not send
- β³ π #45 Verify only ever worked for Microsoft
- β³ π #45 IMAP reported as not authenticated
- β³ βοΈ An email template stopped escaping itself
- β³ π The portal dashboard showed the wrong time
- β³ π’ The folder said 99 and the list showed 96