Syncing with your phone
Keep your phone's address book and Dhaga in step, both ways — with a three-way merge that never silently overwrites, and a hard line that keeps your notes and facts off every device your contacts sync to.
Importing is a one-time copy. Sync is the ongoing version: the mobile app reconciles this phone's address book with Dhaga in both directions, so a number you add in the car and a job title you fix in Dhaga both end up in both places.
Phone sync runs only when you tap it — your handset's address book is never read or written in the background. A connected account (Google or Outlook, set up in Contacts & sync on the web) does keep itself current on a schedule; see Google and Outlook, without a phone below.
Where it lives
In the mobile app, tap the refresh icon in the header (Sync contacts) to open the Sync contacts screen. Grant the Contacts permission when asked — if you've declined permanently, the screen offers an Open Settings shortcut. Then tap Sync contacts.
The screen counts the run off as it goes: Asking for contacts access… → Reading your address book… → Comparing with Dhaga… → Writing changes to your phone… → Confirming…
Mobile only, and it needs a native build
There is no sync screen on the web — the address book lives on the phone, so the phone drives the run. (The one web surface is More › Sync conflicts, where you decide the fields both sides changed — see below.) Like on-device import, sync ships as code but needs a native app build to appear; it can't arrive over-the-air or in plain Expo Go.
What syncs
Exactly nine fields — the ones a vCard would carry:
- Name, nickname, job title, company
- Emails, phones, links, addresses, important dates
Dhaga writes these onto the existing contact record and leaves every other field on it alone.
What deliberately does not sync
Your notes, the facts Dhaga extracted from them, the relationships in your graph, signals and watchlist state, stars and tags — none of it is ever written to an address book.
This is a deliberate boundary, not a missing feature
An address book is not a private place. It syncs onward to your laptop, your car's infotainment system, a shared family iPad, and any app that asks for contacts permission. "Met at the Lisbon offsite, mentioned he's leaving" is a note you wrote for yourself — it stays in Dhaga.
Going the other way, sync also leaves the phone's photos, social profiles, related names and phonetic names on the device: it doesn't read them, so they never enter your graph.
Both directions, and the one you opt into
Pulling in — changes on the phone flow into Dhaga on every run.
Pushing out — edits to contacts Dhaga is already linked to are written back to the phone on every run. People who exist only in Dhaga can be copied into your address book too, but only if you ask: Add Dhaga-only contacts to this phone, on the sync screen, is off by default. Turn it on and everyone you added in Dhaga — typed in, quick-added, scanned from a card — starts landing in your contacts on each run.
Your address book is yours, and it carries onward to every device signed into it, so Dhaga will not start filling it on a default you never chose.
Connected Google and Outlook accounts ask you outright. Pressing Connect Google opens one question before it sends you anywhere: do you want Dhaga to keep both in step (two-way), or just to read the account (one-way)? Answer one-way and Dhaga asks Google or Microsoft only for permission to see your contacts — it is not merely switched off from editing them, it was never given the ability. Answer two-way and edits you make in Dhaga start reaching contacts the account already holds; Add Dhaga-only people to this account then appears in Contacts & sync as its own switch, off, for copying people who exist only in Dhaga.
Changing your mind is a reconnect, not a switch: connect the account again and pick the other answer. That is the price of asking for the narrower permission in the first place, and it is the right way round — the account you connected for reading cannot quietly become one Dhaga can write to.
Nothing is lost while two-way is off. Dhaga remembers that its copy and the account's have drifted apart, so if you turn the switch on later, everything you changed in the meantime is written out on the next run — rather than being quietly forgotten, or quietly overwritten by the account's older version.
Sign-in permissions are a separate thing from that switch. Dhaga asks for permission to change contacts while you are connecting the account, because asking later would mean sending you through the consent screen a second time — but holding the permission is not the same as using it, and it is not used until you switch two-way on. If you declined it, the card says so and offers Reconnect instead of the switch.
Two kinds of person are never pushed out, switch or not:
- People Dhaga only ever heard mentioned in a note — the stubs it infers rather than ones you captured.
- People who arrived by import — a CSV, a vCard, a connected account, or an earlier sync. You didn't author those, and pushing them would turn "add my Dhaga people to my phone" into "replay every list I ever imported".
That leaves the people you actually created here: typed in, quick-added, scanned from a card, or captured from a message.
Turning it on pushes your back-catalogue
The switch applies to every contact you have ever authored, not just the ones you add afterwards — there is no "created after I turned this on" marker. If you have hundreds of scanned cards you may not want all of them in your phone, leave it off. And if you do want all of them, don't get there 500 at a time — see Copying a big back-catalogue across.
Copying a big back-catalogue across
Once the switch is on, one run adds at most 500 new contacts, and it tells you what it held back: "437 contacts still to add — sync again to continue." That count is only people a later run could actually add, so running it again really does drain it — it never pads the number with people who would never be pushed anyway.
You have two ways to get through the rest.
Just sync again. Repeat until the line stops appearing. Nothing else to do, but each run writes contacts one at a time — that is all an address book offers. Neither iOS, Android, Google People nor Microsoft Graph has an "add these 700" call, so 700 people is 700 writes, however they are spread out.
Or seed it in one go. Importing a vCard is the opposite shape: one file, one bulk insert, done in about a second — which is why the same 700 contacts land instantly on the way in. So let the import do the copying and leave sync the job it is actually good at, matching:
- Open Contacts & sync. Seed an address book in one go
downloads everyone you have added in Dhaga as a single
.vcf. If you have already synced once and only want the stragglers, use the Download for … button beside the remaining count instead — that file leaves out whoever the account already has, so nobody arrives twice. - Import that
.vcfwhere the contacts should go — iCloud.com › Contacts › Import vCard, contacts.google.com › Import, Outlook › Manage contacts › Import contacts, or just open the file on the phone. - Sync. Dhaga recognises the people it just seeded — matching on email, then phone, then name + company, the same ladder the importer uses — and links them to the contacts they came from instead of creating a second copy.
Those buttons are /api/export/vcard?scope=authored, plus &provider=device,
google or microsoft for the per-account version. The same two parameters
work on /api/export/csv, but seed from the vCard — the CSV columns carry
no nickname and no important dates, and a field the seed drops is a field the
round trip can later mistake for one you deleted. A mistyped scope or
provider is rejected outright rather than quietly falling back to everything.
The Export links on the People page are a different thing and stay that way:
they download everything, mentioned stubs included, because that is the
leave-with-all-your-data file. Don't seed an address book from those.
What the seed file leaves out, and why
scope=authored applies exactly the rule sync applies, from the same shared
code: no people Dhaga only heard mentioned in a note, no people who arrived
by import, and nobody with no name. A file you are about to hand to iCloud is
the wrong place to discover that distinction, and it is the same reason those two
kinds of person are never pushed out by sync either.
provider= then drops anyone already linked on that platform — including
people you deleted there, so seeding in bulk cannot quietly resurrect someone
you removed from your phone.
What the seed file carries
Name, nickname, job title, company, emails, phones, links, postal addresses, birthdays and other important dates — every field sync itself moves. That matters more than it sounds: a field the seed file drops looks to the next sync like a field you deleted, so the export deliberately carries all of them.
Two honest gaps, neither of which loses a value outright:
- A custom label on an email, phone or link is not preserved.
Home,WorkandOthersurvive the round trip; something likeNewslettercomes back unlabelled, and a later sync can then clear the label in Dhaga. The value itself is never lost. Custom labels on addresses and important dates do survive. - The free-text location (the "Pune" or "Bay Area" on a contact) is not in the vCard at all. It is a display field, not a postal address, and writing it into one would invent a street address you never entered. The CSV and JSON exports still carry it.
For the people it does contain, the card carries name, nickname, job title, company, emails, phones, links, the contact's location and their important dates — including birthdays, which land in the address book's own birthday slot. The one synced field it does not carry is the separate addresses list; those stay in Dhaga and reach the phone through sync as usual.
This does not narrow the export you leave with
The plain /api/export/vcard, /api/export/csv and
/api/export/json downloads are unchanged and still contain everything,
whatever its provenance. scope=authored is an extra, opt-in filter for seeding
an address book — a different job with a different safe answer. Leaving Dhaga
with all of your data never goes through it.
How conflicts are handled
Dhaga does a three-way merge: it keeps a snapshot of each contact as it stood at the last successful sync, then compares both sides against that base. Whichever side moved away from the base is the side that edited the field.
Why not just take the newest change?
No platform — iOS, Android, Google People, or Microsoft Graph — exposes a modification time per field, only per record. So "newest wins" can't tell you added a phone number on your phone from you renamed the contact in Dhaga; it would just clobber one with the other. Comparing against a base needs no clocks at all.
What that means in practice:
- Only one side changed → that change is applied to the other side. No prompt.
- You added an email / phone / link / address / date → it's a union, not a conflict. Both sides end up with both entries.
- You removed an entry and the other side didn't touch it → the removal is honoured.
- A removal races an edit on the same entry → the entry is kept and reported. Deleting contact data on a guess is unrecoverable, so Dhaga won't.
- Both sides edited the same field → reported as a conflict.
Conflicts are listed at the end of the run — "3 contacts need you to decide" — naming the contact, the field, and whether it was changed in both places or changed in one place, removed in the other.
Two rules hold in every conflict case:
- A contested field is never pushed outward. Whatever the disagreement, Dhaga will not write its version over yours in your address book.
- The first sync of a contact never overwrites Dhaga. With no base snapshot yet, Dhaga can't tell who edited what, so it keeps its own value, pushes nothing, and flags it for you.
Deciding a conflict, on the web
When both sides genuinely moved the same field, Dhaga adopts the phone's value so the edit you just made on the handset survives — and keeps the value it had so you can take it back.
Open Dhaga on the web and go to More › Sync conflicts. Each undecided field is one row, showing what Dhaga had and what your phone had, with two choices:

- Restore Dhaga's value — puts it back on the contact. Dhaga is then the side that moved, so the next sync carries it out to your phone, and both sides end up agreeing on it.
- Keep the phone's — accepts what already won and clears the row.
A conflict sits there until you decide it, or until it stops mattering: if you re-type the Dhaga value on the phone yourself, the next sync sees no disagreement left and drops the row on its own. Nothing accumulates, and nothing is thrown away while it is still undecided.
Deciding the same thing once, for everyone
A big import meeting an address book you already had does not produce many different questions — it produces one question many times, like a company name that Dhaga spells out and your phone abbreviates. When several contacts have the same field with the same two values, they are collapsed into a single card that says how many contacts share it and offers the two choices once. The biggest one is at the top; a disagreement only one contact has stays a plain row.
Open the "51 of 51 selected" line on that card to see everyone in it by name and tap anyone you want left out. The buttons then apply to whoever is still selected, and the contacts you excluded stay on the page, still undecided, so you can answer them separately.
Nothing is ever deleted in Dhaga
Deletion is deliberately asymmetric, in both directions.
A contact you delete on your phone is not deleted in Dhaga. The link between the two is marked broken and the person, their notes and their facts all stay. Deleting a phone contact is not consent to destroy everything hanging off it in your graph. If that contact reappears later, the link is picked back up.
A contact you delete in Dhaga is not deleted from your phone either — Dhaga never removes anything from an address book. It also will not quietly come back: Dhaga remembers that you removed that person, so the next sync leaves the phone record alone instead of re-importing it as somebody new. That memory holds only the address-book id, never a name or an email, because keeping those would make "forget this person" untrue. Add the person back in Dhaga and the two re-link normally.
If a contact disappears from your address book, Dhaga does not delete the person, their notes, or their facts — it only marks the link between the two as broken. Deleting a phone contact is not consent to destroy everything hanging off it in your graph. If that contact reappears later, the link is picked back up.
iPhone: which account your contacts land in
Apple publishes no server API for iCloud Contacts, so there's no "Connect iCloud" button anywhere in Dhaga — and there doesn't need to be one. On iOS, Dhaga looks at the containers your address book is made of and writes into the first one that syncs off the device: a CardDAV container (that's iCloud, and also Google if you added the account in iOS Settings) or an Exchange one (Outlook / Microsoft 365).
Writing there is enough. iOS itself propagates the change up to iCloud or Google — Dhaga hands the edit to the operating system and the account's own sync carries it to your Mac, your iPad, and the web. No extra OAuth, no Google write permission, no third-party server in the middle.
If your contacts only live in a local container ("On My iPhone"), sync still runs — but the screen tells you plainly that your changes stay on the handset until you turn on iCloud Contacts or add a Google/Exchange account in iOS Settings. Dhaga decides this from the container's type, never its name, since anyone can name a local container "iCloud".
iOS syncs one container at a time
Only contacts in the container Dhaga picked take part in a run. If your address book is split across several accounts, the others sit it out.
Android: edits sync, new contacts don't
This one is a firm limitation, not a rough edge:
- Edits to contacts you already have are written onto the existing record, so they ride whatever account already owns it — a contact owned by your Google account syncs up to Google as usual.
- Contacts Dhaga creates stay on the phone. They will not appear in Google Contacts, and they won't survive a factory reset or move to a new device on their own.
The reason: Android gives apps no way to choose which account a new contact belongs to. The account/container API is iOS-only, and a contact created through the Android API is inserted with no account attached, which the OS files as device-local.
The sync screen says this on every Android run rather than hiding it. If you want Dhaga-created people in your Google account on Android, add them to Google Contacts yourself, then sync — from then on they're an existing contact and edits flow normally.
Large address books
Contacts go up in batches of 1,000; the screen shows "batch 3 of 7" while it works. Batches are sent one after another, never in parallel.
Each run reads the whole address book — neither iOS nor Android offers a "what changed since last time?" query, so there's no incremental fast path. A very large address book takes as long on the tenth run as on the first. (Connected Google and Outlook accounts do have one — see It only reads what changed.)
Google and Outlook, without a phone
Phone sync needs a phone. If you want Dhaga to keep an address book in step on its own — or you're on Android, where a contact Dhaga creates never leaves the handset — connect the account directly instead.
Open Contacts & sync and connect Google Contacts or Outlook Contacts. Dhaga then talks to that account's API from the server, using the same three-way merge as phone sync: nothing is overwritten silently, contested fields are never pushed, and disagreements land in Sync conflicts exactly as they do from a handset.
Press Sync now to run it on the spot. It also runs on its own, so an account you connected keeps itself current without you having to remember — that's the Sync on its own switch, per connection, on by default and off in one click.
A scheduled run is the same run at a different moment, not a wider permission. It touches only accounts you connected, and it writes back to exactly those you answered two-way for when you connected them. An account connected one-way is only ever read, on a schedule or otherwise — Dhaga has no permission to do anything else to it.
How often depends on where Dhaga is running. The hosted service runs it once a
day, from the same nightly job as everything else. A self-hosted enterprise
deployment can run it as often as its administrator schedules
(/api/jobs/contact-sync — see Self-hosting), with one
floor either way: a connection that synced less than six hours ago is
skipped, so pointing a minute-by-minute cron at it doesn't hammer Google.
When an account stops working
Access can be withdrawn from outside Dhaga — you revoke it in your Google or Microsoft account settings, or the sign-in behind it lapses. From Dhaga's side that looks like the account simply refusing to answer, and there is nothing it can do about it except tell you.
So it does. The connection is marked needs reconnecting rather than left looking healthy, Sync now says so instead of finishing quietly, and a scheduled run puts a notification in the bell naming the account — once, when it breaks, not on every run afterwards. Reconnecting the account from Contacts & sync puts it straight back to work; nothing in your graph is lost in the meantime, and the next run picks up where the last one left off.
A run that fails for an ordinary reason — the provider is busy, the network dropped — is not treated this way and asks nothing of you. The next run simply tries again.
It only reads what changed
The first run reads the whole account. After that Dhaga keeps the bookmark Google and Microsoft hand back, and the next run asks only for what changed since — so a ten-thousand-contact account costs one small request instead of thousands.
One consequence is worth knowing. A contact you delete in Google or Outlook is unlinked in Dhaga only on a run that reads the whole account again, which happens when the provider expires the bookmark (both do, on their own schedule), and after any run that couldn't finish writing everything it had to. Until then the link just sits there. Nothing is lost either way: unlinking never deletes the person, their notes or their facts from Dhaga — see Nothing is ever deleted in Dhaga.
The four switches
- Keep this account in step — on by default once connected. Turn it off and Dhaga stops syncing while keeping the connection; nothing already in your address book is removed.
- Sync on its own — on by default, and shown only while the switch above is on. This is the schedule. Turn it off and nothing stops working — the account simply waits to be asked, and Sync now still runs it. It is its own switch precisely because "sync this when I ask" and "sync this without me" are two different questions, and the second one deserves an answer you chose.
- Two-way sync — set by the answer you gave when you connected the account. Off, Dhaga reads the account and changes nothing in it, ever. On, edits you make in Dhaga are also written back to contacts the account already holds. You can turn it off here at any time; turning it on here is only possible for an account you connected two-way, because an account connected one-way never granted the permission — the card says so, and offers to reconnect.
- Add Dhaga-only people to this account — off by default, and appears only once two-way is on. Turn it on and people who exist only in Dhaga (scanned cards, contacts you typed here) are copied into the account too. Most people don't want every business card they ever scanned landing in their Google contacts, so you have to ask for it. People Dhaga only heard mentioned in a note are never copied either way.
Turning two-way on later loses nothing. While it is off Dhaga keeps track of where its copy and the account's have drifted apart, so the first run after you switch it on carries all of that outward at once.
This is the Android fix
On Android, expo-contacts creates a contact with no account attached, so it stays on the handset forever. Connecting Google here can write to the account directly and Android syncs it down — the reverse of the iOS relay, where the OS does the carrying. If you're on Android and want Dhaga-created people in your Google contacts, this is the way — connect the account two-way, then turn on Add Dhaga-only people to this account. Neither happens unless you ask for it.
What Outlook can't carry
Google Contacts round-trips all nine fields. Microsoft Graph cannot: it gives a contact one website slot, one birthday, and three fixed address slots rather than lists. So for Outlook connections, links, important dates and addresses do not sync in either direction — they stay in Dhaga, untouched, and Dhaga never writes them to Outlook.
That's a deliberate refusal rather than a partial mapping. Storing two links in one slot would mean the field read back short, and on the next sync Dhaga would have taken that shortfall for a deletion you made and removed the rest. Names, nicknames, job titles, companies, emails and phone numbers sync both ways as normal.
Things that don't survive the round trip
A few values have no clean equivalent on both sides:
- Verbatim dates. Dhaga lets you record "spring 2019"; an address book only holds real calendar dates. These are dropped rather than guessed at — they stay in Dhaga.
- Unlabelled dates on the phone are skipped rather than merged into a nameless bucket. Birthdays use the phone's dedicated birthday slot.
- Name particles. Dhaga stores one display name; address books store first / middle / last separately. Splitting is a heuristic and gets names like van der Berg wrong.
- Labels are title-cased coming in (
mobile→ Mobile) and lowercased going out, defaulting tootherwhen a value has no label.
Not yet tested on a real device
The merge logic, field mapping, container selection and batching are unit-tested, and the server side is covered by database tests. But no part of this feature has been run on a physical iPhone or Android handset yet — the platform I/O (reading the address book, writing a patch, creating a contact) has no test that executes it. Treat the first run as a trial: sync a phone whose contacts you have a backup of.
On a self-hosted enterprise deployment
Sync ships with every deployment, and phone sync needs no configuration at all — it authenticates with the same per-user API key your mobile app already uses (see Settings). The iOS relay is why: because the operating system carries the change to iCloud or Google for you, phone sync never needs a Google or Microsoft contacts-write scope.
Connecting Google or Outlook accounts server-side ships too, but it is the one part that does need credentials — see the callout below.
Google and Outlook need their own credentials
Connecting an account server-side uses the same OAuth app as the calendar
integration, so it needs Google and Microsoft client credentials configured on
the deployment. Two extra steps go with them: enabling the People API
(Google) or Contacts.ReadWrite (Microsoft) on that app, and registering
/api/contact-sync/callback/google and /api/contact-sync/callback/microsoft
as redirect URIs. Your administrator handles both. Without credentials the
Contacts & sync simply says no providers are configured, and phone sync keeps working.
Importing contacts
Add contacts from a LinkedIn ZIP, vCard, or CSV, and manage connected Google or Outlook accounts in one place.
Bulk actions & merging
Act on many people at once from the People table — merge duplicates into one record, add a company, tag, star, or forget in a batch — let Dhaga surface likely duplicates for you, and tell it when a set isn't one.