Calendar & Contacts
EUnifyer supports both standards-based protocols (CalDAV, CardDAV) and a REST API for calendar and contacts access. For external integrations, the standards-based path is recommended — it works with any compatible client out of the box.
Which path to use
| Use case | Recommended |
|---|---|
| Connect Apple Calendar, Thunderbird, GNOME Calendar | CalDAV |
| Connect Apple Contacts, Thunderbird, GNOME Contacts | CardDAV |
| Build a server-side integration or sync job | CalDAV / CardDAV or REST |
| Mobile app syncing calendars/contacts | CalDAV / CardDAV |
| REST programmatic access (events, contacts) | REST API |
CalDAV (Calendar)
Endpoint base: https://api.eunifyer.com/caldav/
Standard: RFC 4791 (CalDAV) + RFC 6638 (scheduling extensions)
Auth: HTTP Basic (username:password) or Authorization: Bearer <token>
Service discovery: https://api.eunifyer.com/.well-known/caldav (RFC 6764)
Connect a calendar client
Give users this URL for calendar client setup:
https://api.eunifyer.com/caldav/Most clients (Apple Calendar, Thunderbird, GNOME Calendar) auto-configure from the well-known URL. After discovery, the client reads user calendars via PROPFIND and syncs events via REPORT.
Programmatic CalDAV access
Read events in a date range:
curl -X REPORT "https://api.eunifyer.com/caldav/{username}/{calendar-uid}/" \
-u "username:password" \
-H "Content-Type: application/xml; charset=utf-8" \
-H "Depth: 1" \
-d '<?xml version="1.0" encoding="utf-8"?>
<C:calendar-query xmlns:D="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop><D:getetag/><C:calendar-data/></D:prop>
<C:filter>
<C:comp-filter name="VCALENDAR">
<C:comp-filter name="VEVENT">
<C:time-range start="20260401T000000Z" end="20260430T235959Z"/>
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>'Create or update an event:
curl -X PUT "https://api.eunifyer.com/caldav/{username}/{calendar-uid}/{event-uid}.ics" \
-u "username:password" \
-H "Content-Type: text/calendar; charset=utf-8" \
-d 'BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Example//EN
BEGIN:VEVENT
UID:event-uid@example.com
DTSTART:20260420T100000Z
DTEND:20260420T110000Z
SUMMARY:Team standup
END:VEVENT
END:VCALENDAR'Delete an event:
curl -X DELETE "https://api.eunifyer.com/caldav/{username}/{calendar-uid}/{event-uid}.ics" \
-u "username:password"REST Calendar API
Base path: /api/v1/calendar/
Auth: Bearer token (PAT, Keycloak JWT)
Scopes: calendar:read (read), calendar:write (create/update/delete)
Endpoints
GET /api/v1/calendar/calendars → list of CalendarInfo
GET /api/v1/calendar/events?start=…&end=… → events across all calendars
GET /api/v1/calendar/calendars/{cal_id}/events → events in one calendar
GET /api/v1/calendar/calendars/{cal_id}/events/{uid} → single event
POST /api/v1/calendar/calendars/{cal_id}/events → create event
PUT /api/v1/calendar/calendars/{cal_id}/events/{uid} → replace event
DELETE /api/v1/calendar/calendars/{cal_id}/events/{uid} → delete event
POST /api/v1/calendar/freebusy → free/busy query
POST /api/v1/calendar/calendars/{cal_id}/import → import ICS file
GET /api/v1/calendar/calendars/{cal_id}/export.ics → export calendar as ICSExample — list upcoming events
curl "https://api.eunifyer.com/api/v1/calendar/events?start=2026-04-01T00:00:00Z&end=2026-04-30T23:59:59Z" \
-H "Authorization: Bearer $TOKEN"Example — create an event
curl -X POST https://api.eunifyer.com/api/v1/calendar/calendars/$CAL_ID/events \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{
"title": "Quarterly review",
"start": "2026-04-20T09:00:00Z",
"end": "2026-04-20T10:00:00Z",
"description": "Q1 results discussion",
"attendees": ["alice@example.com", "bob@example.com"]
}'Import and export
# Import ICS file
curl -X POST https://api.eunifyer.com/api/v1/calendar/calendars/$CAL_ID/import \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: text/calendar" \
--data-binary @events.ics
# Export as ICS
curl https://api.eunifyer.com/api/v1/calendar/calendars/$CAL_ID/export.ics \
-H "Authorization: Bearer $TOKEN" \
-o calendar.icsCardDAV (Contacts)
Endpoint base: https://api.eunifyer.com/carddav/
Standard: RFC 6352 (CardDAV)
Auth: HTTP Basic or Authorization: Bearer <token>
Service discovery: https://api.eunifyer.com/.well-known/carddav
ETag support: Active — use If-Match for safe updates
Connect a contacts client
Give users this URL for contacts client setup:
https://api.eunifyer.com/carddav/Clients (Apple Contacts, Thunderbird, GNOME Contacts) auto-configure from the well-known URL.
Programmatic CardDAV access
List contacts in an address book:
curl -X REPORT "https://api.eunifyer.com/carddav/{username}/{book-uid}/" \
-u "username:password" \
-H "Content-Type: application/xml; charset=utf-8" \
-H "Depth: 1" \
-d '<?xml version="1.0" encoding="utf-8"?>
<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>'Create or update a contact (VCard):
curl -X PUT "https://api.eunifyer.com/carddav/{username}/{book-uid}/{contact-uid}.vcf" \
-u "username:password" \
-H "Content-Type: text/vcard; charset=utf-8" \
-d 'BEGIN:VCARD
VERSION:3.0
UID:contact-uid@example.com
FN:Alice Chen
N:Chen;Alice;;;
EMAIL:alice@example.com
TEL:+1-555-0100
END:VCARD'REST Contacts API
Base path: /api/v1/contacts/
Auth: Bearer token
Scopes: contacts:read / contacts:write
Endpoints
GET /api/v1/contacts/books → list of AddressBook
GET /api/v1/contacts/books/{book_id} → list of ContactSummary
GET /api/v1/contacts/books/{book_id}/{contact_id} → ContactDetail
POST /api/v1/contacts/books/{book_id} → create contact
PUT /api/v1/contacts/books/{book_id}/{contact_id} → replace contact
DELETE /api/v1/contacts/books/{book_id}/{contact_id} → delete contact
GET /api/v1/contacts/search?q=… → search contacts
GET /api/v1/contacts/directory → organisation directoryExample — search contacts
curl "https://api.eunifyer.com/api/v1/contacts/search?q=alice" \
-H "Authorization: Bearer $TOKEN"Webhook events
| Event | Fires when |
|---|---|
calendar.event.created | A calendar event is created |
calendar.event.updated | A calendar event (or single occurrence) is updated |
Subscribe via the Webhooks API to receive real-time notifications.