FHIR RESTful API

Note

The features described on this page are available in all Firely Server editions.

Firely Server supports most of the features in the FHIR RESTful API.

FHIR Versions

All the operations below can be called for FHIR STU3, FHIR R4 or FHIR R5. Firely Server supports the fhirVersion mimetype parameter and fhir version endpoint mappings for that purpose. See Multiple versions of FHIR for more information.

Create, read, update, patch, delete

These five operations to manage the contents of the Firely Server, commonly referenced by the acronym CRUD, are implemented as per the specification. Patch is implemented as FHIR Patch, as this is the most versatile one. A few limitations apply.

Firely Server enables create-on-update: If you request an update and no resource exists for the given id, the provided resource will be created under the provided id. It is possible to disable this feature by setting AllowCreateOnUpdate to false in the FhirCapabilities configuration.

Firely Server can reject a resource based on Validating FHIR resources.

A conditional update whose search criteria match no resource, but whose payload contains an id, is handled as follows:

  • If no resource exists with that id, the resource is created under that id. With AllowCreateOnUpdate set to false, Firely Server returns HTTP 404 Not Found instead.

  • If a resource with that id exists but has been deleted, the resource is brought back to life: the update is stored as a new version under that id, and Firely Server returns HTTP 200 OK.

  • If a resource with that id exists, is not deleted and does not match the search criteria, or exists in another FHIR version, the update is rejected with HTTP 409 Conflict.

Configuration

A conditional delete interaction may match multiple resources. You can configure the server to delete all matches, or reject the operation (effectively only allowing single matches to be deleted). Allowing multiple deletes requires support for transactions on the database (MongoDb, SQL Server or SQLite). If you allow for multiple deletes, you have to specify a maximum number of resources that can be deleted at once, to save you from accidentally deleting too many resources.

"FhirCapabilities": {
    "AllowCreateOnUpdate": true|false
    "ConditionalDeleteOptions": {
        "ConditionalDeleteType": "Single", // Single or Multiple,
        "ConditionalDeleteMaxItems": 1
    }
}

Versioned update with If-Match

An update, a conditional update or a patch can be made conditional on the current version of the resource, as described in Managing Resource Contention. Send the version that you expect to be current in the If-Match header, in the weak ETag format that Firely Server returns in the ETag header:

If-Match: W/"<versionId>"

In a batch or transaction, use entry.request.ifMatch for the same purpose, see Conditional headers.

  • If the version in If-Match is the current version of the resource, the update is performed.

  • If it is not, Firely Server rejects the request with HTTP 409 Conflict. Note that the FHIR specification asks for 412 Precondition Failed here.

  • If no resource exists with the given id, Firely Server also returns 409 Conflict, and does not create the resource, even when create-on-update is enabled.

  • A value that is not in the weak ETag format W/"<versionId>" never matches, so the request is rejected with 409 Conflict.

Conditional headers

Firely Server supports these conditional request headers. In a batch or transaction, each entry can carry them in its request element, with the same effect:

HTTP header

entry.request

Effect

If-None-Exist

ifNoneExist

Makes a create conditional. The value is a search query without the resource type, for example identifier=http://example.org|123. If no resource matches, the resource is created (201 Created). If one resource matches, nothing is created and Firely Server returns 200 OK with the Location of the existing resource. If more than one resource matches, the create is rejected with 412 Precondition Failed. In a bundle entry it only applies to a POST (create).

If-Match

ifMatch

Makes an update, a conditional update or a patch conditional on the current version of the resource, see Versioned update with If-Match.

If-None-Match

ifNoneMatch

Makes a read conditional. The value is one or more ETags, separated by commas, such as W/"3". If the current version of the resource is one of them, Firely Server returns 304 Not Modified without the resource. A value that is not a correctly formed ETag is rejected with 400 Bad Request.

If-Modified-Since

ifModifiedSince

Makes a read conditional. The value is an HTTP date, such as Wed, 21 Oct 2026 07:28:00 GMT. If the resource was not changed after that moment, Firely Server returns 304 Not Modified without the resource. A value that is not an HTTP date is rejected with 400 Bad Request. In a bundle entry, ifModifiedSince is a FHIR instant, such as 2026-10-21T07:28:00Z.

Response headers

When a response contains a single resource, for example the response to a read, create, update or patch, Firely Server includes these headers:

  • ETag: the version of the resource, as a weak ETag: W/"<versionId>". Use this value in the If-Match header of a versioned update.

  • Last-Modified: the meta.lastUpdated of the resource, as an HTTP date.

These headers are also sent when the resource is not returned in the body because the request has a Prefer: return=minimal or Prefer: return=OperationOutcome header.

After a create, an update or a conditional create or update, the Location header holds the absolute url of the new version of the resource: <firely-server-endpoint>/<resourcetype>/<id>/_history/<versionId>.

Limitations on CRUD

  1. Simultaneous conditional creates and updates are not entirely transactional safe:

    • Two conditional updates may both result in a create, although the result of one may be a match to the other.

    • Two conditional creates may both succeed, although the result of one may be a match to the other.

    • A conditional create and a simultaneous conditional update may both result in a create, although the result of one may be a match to the other.

  2. Parameter _pretty is not yet supported.

  3. XML Patch and JSON Patch, as well as version-read and conditional variations of FHIR Patch are not yet supported.

Update with no changes

Updating Firely Server with a resource that is identical to an existing resource in the database will normally create a new version of that resource, along with new AuditEvent and Provenance resources if auditing is enabled. This can put extra load on the server where this is not entirely necessary. To avoid these updates that can unnecessarily increase server load and database growth, the No-Op (No Operation) plugins can be enabled. By enabling these plugins, the server acknowledges the request without making any actual modifications to the database. If an update resulted in a No-Op scenario, this can be observed in the OperationOutcome that is returned by Firely Server.

Configuration for No-Op

To make sure Firely server uses the No-Op scenario, the UpdateNoOp plugins need to be enabled in the PipelineOptions.

"PipelineOptions": {
  "PluginDirectory": "./plugins",
  "Branches": [
    {
      "Path": "/",
      "Include": [
        "Vonk.Plugin.UpdateNoOp.UpdateNoOpConfiguration",
        "Vonk.Plugin.UpdateNoOp.PatchNoOpConfiguration",
        "Vonk.Plugin.UpdateNoOp.ConditionalUpdateNoOpConfiguration",
      ]
    }
  ]

  "UpdateNoOp": {
    "EnableForHttp": true,
    "EnableForPubSub": true,
    "AdditionalMetaToBeIgnored": [
      "security",
      "tag",
      "profile"
    ]
  }

There are three No-Op plugins available:

  • Vonk.Plugin.UpdateNoOp.UpdateNoOpConfiguration - For regular updates

  • Vonk.Plugin.UpdateNoOp.PatchNoOpConfiguration - For Patch operations

  • Vonk.Plugin.UpdateNoOp.ConditionalUpdateNoOpConfiguration - For conditional updates

By default the following meta elements are ignored during resource comparison: versionId, lastUpdated and source. You can also add security, tag and profile or any other meta element to be ignored, but it depends on your specific usage of meta. For more information see the hl7 specification.

The EnableForHttp setting (default: true) controls whether the No-Op behaviour applies to regular HTTP update requests. The EnableForPubSub setting (default: true) controls whether it applies to Update and Upsert commands received via the PubSub subscriber (Vonk.Plugin.PubSub.Sub). Both can be set to false independently to disable No-Op for that channel while keeping it active for the other.

To determine if your action resulted in a No-Op scenario, you can configure Firely Server to return an OperationOutcome. For this it is necessary to configure the Prefer Header as Firely Server does not return this response by default. The Prefer Header can be set in three ways, as per the hl7 specification:

  • return=minimal- Nothing is returned by the server

  • return=representation - The resource is returned as present in the database

  • return=OperationOutcome - Return an OperationOutcome

In the example below an OperationOutcome for a No-Op scenario is returned when the Prefer Header is set to return=OperationOutcome:

{
  "resourceType": "OperationOutcome",
  "id": "26a724d9-10e4-4a71-819e-15d52f6f821c",
  "meta": {
    "versionId": "b6063533-a93e-4cd1-bb0b-5f37381d0f20",
    "lastUpdated": "2024-02-12T11:12:40.6172822+00:00"
  },
  "issue": [
    {
      "severity": "information",
      "code": "informational",
      "details": {
        "coding": [
          {
            "system": "http://hl7.org/fhir/dotnet-api-operation-outcome",
            "code": "5025"
          }
        ],
        "text": "No changes were performed as the provided resource contains no changes to the existing resource"
      }
    },
    {
      "severity": "information",
      "code": "informational",
      "diagnostics": "The operation was successful"
    }
  ]
}

Versioning

Firely Server keeps a full version history of every resource, including the resources on the Firely Server Administration API. When SMART on FHIR is enabled, history and version reads are only available if SmartAuthorizationOptions:AllowHistory is true, and then with restrictions. See History and version reads.

History

History is supported as described in the specification, on the system, type and instance level. The _since and _count parameters are also supported. The response will be a Bundle which adheres to the BundleOptions configuration, see Search size.

Limitations on history

  1. _at parameter is not yet supported.

  2. Paging is supported, but it is not isolated from intermediate changes to resources.

  3. When SMART on FHIR is enabled, _history can only be used if SmartAuthorizationOptions:AllowHistory is true, and then with restrictions. See History and version reads.

  4. With virtual multi-tenancy enabled, _history and vread cannot be used.

Batch

Batch is fully supported on the usual endpoint. You can limit the number of entries accepted in a single batch. See Protect against large input.

Note that batches are not supported in the /administration endpoint.

Transaction

Transactions are supported, but with the following limitation:

  1. The /administration endpoint does not support transactions.

You can limit the number of entries accepted in a single transaction. See Protect against large input.

Capabilities

On the Capabilities interaction (<firely-server-endpoint>/metadata) Firely Server returns a CapabilityStatement that is built dynamically from the supported ResourceTypes, SearchParameters and interactions. E.g. if you Configure Search Parameters, the SearchParameters that are actually loaded appear in the CapabilityStatement.

CapabilityStatement.instantiates lists the CapabilityStatements that Firely Server conforms to. Firely Server fills it with the canonical url (url|version if the CapabilityStatement has a version) of every CapabilityStatement in the administration database that has mode set to server and status set to active, up to BundleOptions:MaxCount of them. To declare that Firely Server instantiates the server CapabilityStatement of an Implementation Guide, load that CapabilityStatement into the administration database, for example as part of the IG package.

Not supported interactions

These interactions are not yet supported by Firely Server:

  1. HEAD

Besides that, Firely Server does not yet return the date header as specified in HTTP return values