Skip to content

API Overview

Clients communicate with ConfigHub Cloud via a HTTP REST API. This page gives you an overview of how the API is structured. You can find the complete reference for the API in the OpenAPI Spec at https://hub.confighub.com/api/docs.

Authentication

For endpoints that require authentication, the API requires a valid JWT bearer token in the Authorization header of the HTTP request. There is currently no official documentation for how to obtain a token, but you can read the source code of the CLI to see how it obtains a token.

Writing clients

We generate Go and Typescript client libraries from our OpenAPI spec. The Go client is used in the CLI, cub, which you can use as an example client. You can find the client libraries and cub source code in the https://github.com/confighub/sdk repo.

All details for writing clients are encoded in the OpenAPI Spec at https://hub.confighub.com/api/docs and will not be repeated here. Please use the spec to generate clients or refer to the spec for details when hand writing clients.

JSON format conventions

JSON structures use PascalCase for field names and string constant names. For example:

{
  "OrganizationID": "123",
  "Slug": "a-record",
  "CreatedAt": "..."
}

URL Structure

The URL structure of the REST API reflects the nested collections topology of entities in ConfigHub. Collection names are singular nouns rather than plural. IDs are used as identifiers in URLs. The organization comes from the authentication context, since one logs in as a member of a specific organization.

This will fetch a unit:

GET /api/space/55ec4079-5b96-44f6-9a02-d6bf74286e1e/unit/9872639e-373e-4839-8566-51a928bb8f3d

ConfigHub checks that the URL pattern matches the current state of an entity. For example, in the URL above, if the Unit with UUID 9872639e-373e-4839-8566-51a928bb8f3d exists but is not in the space with UUID 55ec4079-5b96-44f6-9a02-d6bf74286e1e, then the request returns 404 not found even though the Unit does exist.

ConfigHub will also check access permissions and return 403 forbidden if an operation is not permitted by the currently authenticated principal.

Configuration data

Configuration data is not a field of the entity that owns it. A Unit, a Revision, and a Release each expose their bulk fields as subresources, so that fetching or listing the entity does not carry a copy of every configuration:

GET /api/space/{space_id}/unit/{unit_id}/data
PUT /api/space/{space_id}/unit/{unit_id}/data
GET /api/space/{space_id}/unit/{unit_id}/mutation_sources
GET /api/space/{space_id}/unit/{unit_id}/revision/{revision_id}/data
GET /api/space/{space_id}/unit/{unit_id}/revision/{revision_id}/mutation_sources
GET /api/space/{space_id}/release/{release_id}/data

The request and response bodies of the Unit and Revision data endpoints are the configuration itself, as text, rather than a JSON object with the configuration inside it. A GET returns the DataHash as an ETag, and honors If-None-Match with a 304; a PUT honors If-Match on that same hash, so a write can be made conditional on nobody having written first. PUT .../data is the only way configuration reaches a Unit other than a clone, which copies it server-side; it routes through the normal update path, so it creates a Revision and runs gates, triggers, and link resolution like any other change. It accepts the update query parameters that do not name another source for the data, such as last_change_description and change_set_id. A Release's data is the gzip tarball it published, and its ETag is the Release's Digest.

A write to a Unit — POST /api/space/{space_id}/unit, PUT or PATCH on one, and the bulk forms — answers with UnitCreateOrUpdateResponse rather than the Unit itself. The Unit is in its Unit field. That is also where the configuration an operation produced can be returned, which is the only way to see the result of a dry run: a dry run stores nothing, so there is no data endpoint to read it back from. Ask for it with include:

PUT /api/space/{space_id}/unit/{unit_id}?dry_run=true&upgrade=true&include=ConfigData,MutationSources

ConfigData is the configuration the operation produced and MutationSources is what set each value in it. Both are returned only when named, on a real write as well as a dry run; naming something that is neither of these nor an expandable field is a 400.

Because these fields are not part of the entity, they cannot be named in a select clause — select=Data is a 400. What the entity does carry is DataHash, the hash of the configuration, and DataSize, its length in bytes.

Reading one Unit at a time does not scale to a whole Space, so there are org-scoped bulk counterparts that take a where clause and answer for every entity it selects, each with its DataHash and DataSize:

GET /api/unit_data
GET /api/revision_data
GET /api/unit_mutation_sources
GET /api/revision_mutation_sources