Skip to content

CardDAV Contact Sync

Ed Mozley edited this page Sep 16, 2026 · 6 revisions

CardDAV contact sync β€” reading people out of an address book

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.


What it is, and what it is not

⚠️ 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.

Which servers work

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.


Setting one up

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.

1. Connection

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.

2. Test connection β€” this is a setup step, not a check

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.

3. Contacts β€” choosing which people

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.

4. Preview, then run

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.


What arrives, and what does not

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.
Email 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.

Three things deliberately left alone

  • 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.

When they already exist here

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.


Safety β€” the three rules

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.


Running it on a schedule

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.

History

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.


Troubleshooting

"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.


Sending changes back

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.

Adding people who are not in it yet

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.

⚠️ If you later narrow an address book to one tag or group, people added before then are treated like any other card without it.


When something goes wrong

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.


Checking it is working

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.


What this still does not do

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.


See also

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally