Session-less Free/Busy Interfaces deprecated

This article describes the two interfaces that expose free/busy data without an end user session: the internet free/busy servlet, targeting clients like Microsoft Outlook, and the administrative free/busy REST end-point, targeting inter-service queries.

Introduction

Free/busy data answers a single question: when is a calendar user busy? It deliberately says nothing about why - the appointments causing a busy period stay out of scope, which makes free/busy the natural currency whenever availability has to be shared beyond the boundaries of a user's own calendar view.

Within App Suite, free/busy data is normally requested by a signed-in user through the chronos?action=freebusy request of the HTTP API, for example when the scheduling dialog of a meeting shows the availability of the invited attendees. That request acts on behalf of the session user and enriches each busy period with as much event data as the user is permitted to see.

However, there are consumers that have no user session at all, and there are two interfaces to serve them:

Internet free/busy servlet Administrative free/busy REST end-point
Intended for Third-party clients, most prominently Microsoft Outlook Other services of the deployment, e.g. a booking service performing a calendar conflict check
Path /servlet/webdav.freebusy /preliminary/chronos/v1/freebusy/…
Format iCalendar (VFREEBUSY) JSON
Users per request One One or many
User referenced by Mail address, split into user and server part Internal user identifier or mail address
Authentication None HTTP Basic Authentication
Must be enabled Yes, per context No, available out of the box
Exposure May be published to the internet on purpose Internal use only

Both interfaces bypass the notion of a requesting user, so neither of them can apply the per-user permissions that the HTTP API relies on. Instead, both of them are limited to the plain availability information, and both honor the free/busy visibility of the queried users as described below.

Free/Busy Visibility

Users may restrict who is allowed to inspect their availability through their freeBusyVisibility setting, whose default is configured with com.openexchange.calendar.freeBusyVisibility.default - see chapter Free/Busy Visibility of the calendar implementation details for the general picture.

Since neither of the two interfaces acts on behalf of a user of the queried context, both of them serve free/busy data only for users whose visibility is set to all. Users that limited their availability to their own context (internal-only) or hid it entirely (none) are treated as if they had no busy times at all. Independently of that, individual appointments that carry a secret classification never contribute to free/busy results.

Internet Free/Busy Servlet

Clients like Microsoft Outlook are able to discover the availability of a user or resource by querying a specific endpoint on a server. This endpoint is the internet free/busy servlet. The free/busy data of a user or resource is requested via a plain HTTP GET request and is returned in iCalendar format.

The servlet is disabled by default and must be enabled by the administrator. In addition, the free/busy data of a context is published only when activated in the configuration.

Note: The servlet answers without any authentication to anyone who can reach it. Only publish it through a public ingress if internet free/busy for clients like Microsoft Outlook is actually wanted - and keep in mind that doing so exposes the availability of all users of the published contexts.

Configuration

First of all the servlet must be enabled generally, otherwise it won't be registered during server startup:

com.openexchange.calendar.enableInternetFreeBusy=true

After changing the property a restart is necessary.

To get free/busy data for a user in a context, the property for publishing must be enabled explicitly. This can either be done globally, or using the config-cascade down to the "context" level:

com.openexchange.calendar.publishInternetFreeBusy=true

Furthermore it is possible to configure the maximum time range that is allowed to be requested. By default this maximum time range is 12 weeks into the past and 26 weeks into the future. The time range is given in weeks. A negative value disables the limitation.

com.openexchange.calendar.internetFreeBusyMaximumTimerangePast=13
com.openexchange.calendar.internetFreeBusyMaximumTimerangeFuture=27

Requesting free/busy data

This section provides information about how to use the servlet.

Parameters

The request to the servlet has required and optional parameters. The context id, the user name and the server name are necessary for requesting free/busy data. Optionally, the time range to be queried can be specified. Set parameter simple to true if the resulting iCalendar data should not contain the free/busy type and free information (necessary to get readable data for Microsoft Outlook).

Required parameters:

  • contextId (Integer): The context id of the context in which the requested user is located.

  • userName (String): The name of the user. Typically the local part of the email address.

  • server (String): The name of the server. Typically the domain part of the email address.

Optional parameters:

  • weeksIntoPast (Integer): The requested time range into the past in weeks. If this value is greater than the configured maximum (see property above), the free/busy times are only requested to configured maximum. Default value is 1 week into the past.

  • weeksIntoFuture (Integer): The requested time range into the future in weeks. If this value is greater than the configured maximum (see property above), the free/busy times are only requested to configured maximum. Default value is 4 weeks into the future.

  • simple (Boolean): true if the VFREEBUSY data should not contain free/busy type and free information, false otherwise. Default value is false.

For further information see OX REST API documentation: https://documentation.open-xchange.com/components/middleware/rest/latest/index.html.

URI

The servlet can be reached via the following path:

http://[server]:[port]/servlet/webdav.freebusy?

[server] and [port] are placeholder and must be replaced by the information in the App Suite installation. The parameters are concatenated with '&' and appended on the URI.

Response

On success, the servlet responds with a text/calendar document holding a single VFREEBUSY component for the requested user, offered for download as freebusy.vfb:

BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Open-Xchange//[version]//EN
BEGIN:VFREEBUSY
DTSTAMP:20260728T101500Z
DTSTART:20260721T000000Z
DTEND:20260825T000000Z
ATTENDEE:mailto:jane.doe@example.com
FREEBUSY;FBTYPE=BUSY:20260728T080000Z/20260728T090000Z
FREEBUSY;FBTYPE=BUSY-TENTATIVE:20260728T110000Z/20260728T120000Z
END:VFREEBUSY
END:VCALENDAR

The servlet answers with HTTP status 404 Not Found if the internet free/busy feature is not published for the context or the requested user, if the context or the user does not exist, or if the user restricted the visibility of its free/busy data. A malformed parameter is answered with 400 Bad Request.

Example for Microsoft Outlook

After activating the servlet for a specific context as outlined above, the following URI can be configured in the free/busy options of Microsoft Outlook, so that the free/busy data of users and resources of the specified context can be requested. [server], [port] and [cid] are placeholder and must be replaced by the information from the App Suite installation and the context id. Outlook substitutes %NAME% and %SERVER% in the template with the email address, split at the '@' character.

http://[server]:[port]/servlet/webdav.freebusy?contextId=[cid]&userName=%NAME%&server=%SERVER%&simple=true

For further information about how to request free/busy data in Microsoft Outlook see: https://support.microsoft.com/en-us/help/291621/how-to-use-the-internet-free-busy-feature-in-outlook.

Administrative Free/Busy REST API

Backend services that need to know when the users of a context are busy can query their free/busy periods through a dedicated administrative REST end-point. A typical consumer is a booking service that performs a calendar conflict check while deriving the bookable slots of a resource owner.

The end-point answers the same question as the client-facing chronos?action=freebusy request and uses the same JSON model for its results, but differs in two important aspects:

  • It does not act on behalf of a session user, but is invoked administratively for a context.
  • It is reduced to the pure free/busy periods. The events behind a busy period are never serialized - not even in their anonymized form - so nothing about the underlying appointments is disclosed.

Compared to the internet free/busy servlet described above it answers in JSON instead of iCalendar, accepts a batch of users per request, accepts internal user identifiers as an alternative to mail addresses, is protected by HTTP Basic Authentication rather than relying on not being reachable from the outside, and does not require the internet free/busy feature to be enabled and published. This makes it the preferred choice for service-to-service queries within a deployment.

The end-point is available out of the box with open-xchange-core, no configuration is needed to enable it.

Authentication

Access is protected via HTTP Basic Authentication, using the credentials configured through the properties com.openexchange.rest.services.basic-auth.login and com.openexchange.rest.services.basic-auth.password.

As with all other administrative end-points, the REST interface should not be exposed publicly.

Requesting free/busy data

Two flavors are offered - a batch end-point that takes any number of users as query parameter, and a single user convenience end-point that references one user through a path segment:

GET /preliminary/chronos/v1/freebusy/{contextId}?attendees={attendees}&from={from}&until={until}&merge={merge}
GET /preliminary/chronos/v1/freebusy/{contextId}/{userId}?from={from}&until={until}&merge={merge}

They only differ in how the queried users are referenced and in the shape of the response - the lookup itself, the accepted time range notations and the reported data are identical.

The end-points are preliminary, i.e. their contract may still change in future releases.

Parameters

Required parameters:

  • contextId (Integer, path): The identifier of the context the queried users belong to.

  • attendees (String, batch variant only): The users to query the free/busy data for. Each value is either the numerical internal user identifier, or one of the user's email addresses - including aliases, and optionally already given as mailto:-URI. Multiple users can be requested as comma-separated list, by repeating the parameter, or by any combination of both. Booking-relevant resources and rooms can be queried alongside the users by passing their email address; they are reported with their cuType set to RESOURCE or ROOM accordingly.

  • userId (Integer, path, single user variant only): The identifier of the user to query the free/busy data for. Since the user is referenced through a path segment here, only its numerical internal user identifier is accepted - use the batch variant to reference a user by its email address.

  • from (String): The start of the requested time range.

  • until (String): The end of the requested time range. Must be after from.

Optional parameters:

  • merge (Boolean): true to merge any overlapping free/busy periods of a user into the most conflicting busy times, false to get one period per underlying appointment. Defaults to true, which is usually what is wanted when the underlying events are not part of the response anyway.

The time range boundaries from and until are accepted in three notations:

Notation Example
UTC timestamp in milliseconds 1785283200000
ISO-8601 date or date/time 2026-07-28, 2026-07-28T08:00:00Z, 2026-07-28T10:00:00+02:00
iCalendar yyyyMMdd['T'HHmmss['Z']], as used by the HTTP API 20260728, 20260728T080000Z

Values without an explicit timezone or UTC indicator are interpreted as UTC.

Response

The batch variant answers with a JSON array holding one object per requested user, in the very same order the users were requested in. The single user variant answers with that very same object directly, unwrapped. Each object carries the resolved attendee, its freeBusyTime periods, and possibly warnings - using the same field names as the HTTP API:

[
  {
    "attendee": {
      "entity": 3,
      "cuType": "INDIVIDUAL",
      "uri": "mailto:jane.doe@example.com",
      "cn": "Jane Doe",
      "email": "jane.doe@example.com"
    },
    "freeBusyTime": [
      { "startTime": 1785312000000, "endTime": 1785315600000, "fbType": "BUSY" },
      { "startTime": 1785322800000, "endTime": 1785326400000, "fbType": "BUSY-TENTATIVE" }
    ]
  },
  {
    "attendee": {
      "uri": "mailto:john.doe@example.com",
      "email": "john.doe@example.com"
    },
    "warnings": [
      {
        "code": "CAL-40312",
        "error": "No free/busy data available for the attendee john.doe@example.com.",
        "error_desc": "No free/busy data available for attendee [email john.doe@example.com] (per user configuration).",
        "error_params": [],
        "error_id": "-1959403543-52",
        "categories": "PERMISSION_DENIED",
        "category": 3,
        "version": "8.52.186"
      }
    ]
  }
]

The attendee object is the resolved one, i.e. an internal user is reported with its entity identifier, common name and mail address no matter whether it was requested by identifier or by mail address, which makes it easy to correlate the entries. Only the requested identifier is echoed for users that could not be resolved at all.

Warnings carry the usual error fields of the HTTP API; an error_stack is additionally included if com.openexchange.ajax.response.includeStackTraceOnError is enabled.

Since the periods are always reported as absolute timestamps in UTC milliseconds, no timezone needs to be passed with the request.

Examples

Query two users of context 1 at once, the first one referenced by its identifier, the second one by its mail address:

curl -u restuser:secret \
  'http://localhost:8009/preliminary/chronos/v1/freebusy/1?attendees=3,john.doe@example.com&from=2026-07-28T00:00:00Z&until=2026-08-04T00:00:00Z'

Query user 3 of context 1 through the single user variant, which yields the free/busy result object without the surrounding array:

curl -u restuser:secret \
  'http://localhost:8009/preliminary/chronos/v1/freebusy/1/3?from=2026-07-28T00:00:00Z&until=2026-08-04T00:00:00Z'
{
  "attendee": {
    "entity": 3,
    "cuType": "INDIVIDUAL",
    "uri": "mailto:jane.doe@example.com",
    "cn": "Jane Doe",
    "email": "jane.doe@example.com"
  },
  "freeBusyTime": [
    { "startTime": 1785312000000, "endTime": 1785315600000, "fbType": "BUSY" }
  ]
}

Warnings and error handling

The lookup is performed independently per requested user, and a user that cannot be served is reported with a warnings array instead of its freeBusyTime periods - so a single problematic entry does not fail the whole request. This happens in particular if

  • the requested identifier or mail address does not denote a calendar user of the context (CAL-4034),
  • the user's freeBusyVisibility setting restricts the free/busy data to internal-only or hides it entirely with none (CAL-40312), or
  • the free/busy data could not be looked up for the user (CAL-5033).

Note: The configured free/busy visibility of a user is honored deliberately - the end-point exposes exactly the free/busy data other users of the context would see, and does not override the user's own choice.

The request as a whole is rejected with

Status Reason
400 Bad Request A parameter is missing, or an identifier or time range boundary cannot be parsed
401 Unauthorized Missing or wrong Basic Authentication credentials
404 Not Found The requested context does not exist - or, for the single user variant, the requested user does not exist

The unknown user is the one behavioral difference between the two variants: since the single user variant addresses exactly one user as a resource, a user identifier that cannot be resolved yields 404 Not Found there, while the batch variant reports it as CAL-4034 warning on the corresponding array entry.