API Style Guide

Overview

The goal of this Style Guide for Jamf APIs is to provide a standard framework to facilitate the consistent creation and evolution of Jamf APIs. This Style Guide represents Jamf’s best efforts to provide a standard framework and we do not promise that it is error free. Further, this Style Guide provides one way of doing things. There may be another way, or even a better way. Jamf is not responsible for the use of the Jamf APIs for different purposes than the ones described in this Style Guide.

API Style Guide Version 4.0.0

Language conventions

The keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Terminology

  • Resource refers to a collection of endpoints which share the same {resource} path component and tag. See the URI section for details.
  • Endpoint refers to a single unique API path, up to but not including the query string.

Architecture

Each Jamf public API is intended to be RESTful, unless otherwise stated. Jamf RESTful APIs SHOULD be designed to adhere as closely as possible to the REST Architectural Style.

HTTP

Status Codes

In order to simplify client processing, the following status codes are RECOMMENDED:

  • 200 - OK
    • The request has succeeded.
    • The information returned with the response is dependent on the method used in the request:
      • GET - an entity corresponding to the requested resource is sent in the response;
      • POST - MAY return an entity describing or containing the result of the service invocation.
  • 201 - Created
    • The request has been fulfilled and resulted in a new resource being created.
    • The origin server MUST create the resource before returning the 201 status code.
    • If the service invocation cannot be carried out immediately, the server SHOULD respond with 202 (Accepted) response instead.
  • 202 - Accepted
    • The request has been accepted for processing, but the processing has not been completed.
    • The entity returned with this response SHOULD include an indication of the request's current status and either a pointer to a status monitor.
  • 204 - No Content
    • The server has fulfilled the request but does not need to return an entity-body, and might want to return updated meta-information.
    • The 204 response MUST NOT include a message-body.
  • 400 - Bad Request
    • The request could not be understood by the server due to malformed syntax.
    • The client SHOULD NOT repeat the request without modifications.
  • 401 - Unauthorized
    • The 401 status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.
    • If the request included authentication credentials, then the 401 response indicates that authorization has been refused for those credentials.
    • The user agent MAY repeat the request with a new or replaced credentials.
  • 403 - Forbidden
    • The 403 status code indicates that the server understood the request but refuses to authorize it.
    • If authentication credentials were provided in the request, the server considers them insufficient to grant access.
    • The client SHOULD NOT automatically repeat the request with the same credentials.
  • 404 - Not Found
    • The server has not found anything matching the Request-URI.
    • No indication is given of whether the condition is temporary or permanent.
  • 409 - Conflict
    • The request could not be completed due to a conflict with the current state of the resource.
    • Allowed only when user is able to resolve the conflict and resubmit the request.
    • The response body SHOULD include enough information for the user to recognize the source of the conflict.
  • 412 - Precondition Failed
    • The 412 status code indicates that one or more conditions given in the request header fields evaluated to false when tested on the server.
  • 422 - Unprocessible Entity
    • A request has correct syntax, but has a field with a bad value, such as an ID which does not exist, an illegal enum value, or a field is missing entirely.
    • Each response error SHOULD contain information about which field caused the error and error code.
  • 429 - Too Many Requests
    • The 429 status code indicates that the user has sent too many requests in a given amount of time ("rate limiting").
    • This status code MUST be returned when a rate limit has been exceeded.
    • The response representations SHOULD include details explaining the condition, and MAY include a Retry-After header indicating how long to wait before making a new request.
  • 500 - Internal Server Error
    • The server encountered an unexpected condition which prevented it from fulfilling the request.
  • 503 - Service Unavailable
    • The server is currently unable to handle the request due to a temporary overloading or maintenance of the server.

Methods

To facilitate a uniform RESTful interface when used with HTTP(S) the following HTTP methods SHALL be used:

GET - Read

  • Used for retrieving resources.
  • MAY be used for filtering, paging, sorting, and other object search functions.

PUT - Replace

  • Used for replacing resources with an entirely new body.
  • MUST NOT be used for partial updates of a resource.

POST - Service Invocations and Object Creation

  • Used for creating resources / invoking services.
  • MAY be used for complex searching.
  • MUST NOT be used for partial updates of a resource.

DELETE - Delete

  • Used for deleting individual resources.

PATCH - Update

  • Used to selectively update fields within a resource.
  • SHOULD be used where PUT method cannot be used.
  • Not currently implemented for all resources but available to implement if needed.
  • Standard PATCH (JSON Merge Patch) MUST be used for public Jamf API endpoints.
  • A PATCH request containing a field with a value of null MUST attempt to save the actual value as null.

Data representation

The API encodes information by default using JavaScript Object Notation (JSON) as defined by ECMA-404.
This format allows for relatively efficient data transfer and is easy to manipulate on many platforms.

  • If no Accept header is provided in a request, Jamf APIs MUST return JSON in a response if possible.
  • Unless it cannot be represented as JSON, API requests and responses SHOULD be a JSON object as described in RFC 7159.
  • All operations which take or return a body MUST have full Open API object schema defined.
  • All fields of a response schema MUST be returned in every response.
  • Fields which can have a null value MUST have nullable: true documented in Swagger.
  • If a field is being returned in a response, null SHOULD be used as a value to indicate the value is unset or unknown, while an empty/blank string ('') SHOULD be used when a value is set, known, and blank.

Examples:
1. A blank string might be an illegal value for a UUID in a scenario, but null might be appropriate before the UUID has been set.
2. An email message body might be modeled by a blank string when set, and null before it has been set.

  • Formats used for schema fields SHOULD already exist. An example of a non-standard schema format used in the API is the UUID format:
  properties:
     id:
       type: string
       format: uuid

File Upload Format

  • Large files SHOULD be sent as multipart (e.g. images and packages).
  • Small files SHOULD be sent as a base-64 encoded String inside a JSON object (e.g. certificates and text files).
  • To be compliant with FIPS 140-2 and for consistency, the SHA-512 hashing algorithm MUST be used for file hashes, for endpoints which return hashes.
    Other hash results MAY be provided in addition to SHA-512.
  • HTTP response code for successful file upload MUST be 201.

Date / Time Format

  • Dates and Times MUST use UTC in responses.

  • Dates and times SHOULD be represented as ISO 8601 standard formatted dates.

    • Dates and Times returned SHOULD match one of the following examples:
      • 2020-02-20
      • 2020-02-20T10:10:10Z
      • 2020-02-20T10:10:10.123Z
      • 10:10:10Z
      • 10:10:10.123Z

CSV (Comma Separated Values)

All endpoints that interact with CSV data MUST adhere to the RFC 4180 CSV format and MUST use the ‘text/csv’ MIME type.

URIs

URIs are used to access specific objects and MAY, depending on the object, allow access to specific properties or methods within an object.
As a general form of URLs, URIs provide for the ability to accommodate schemes beyond the common HTTP/HTTPS.

The main pattern for interacting with the API is:

/{version}/{resource}[/{id}][/{sub-resource|service-invocation}][/{sub-resource-id}][?{params}]

  • Repealed: URIs MUST have a base path of /api/.

  • URIs MUST be written in kebab case, except query parameter values and URI template variable names. URI template variable names SHOULD be written in camel case.

  • Commonly used word shortenings and acronyms are acceptable. App and Apps SHOULD be preferred over Application and Applications, to follow Apple’s verbiage.

Examples:

  • /v3/keystores/3/certificates/1

    • v3 is the version
    • keystore is the resource
    • 3 is the id
    • certificates is the sub-resource
    • 1 is the id of the sub-resource
  • /v1/smtp-server

  • /v2/system/initialize

  • /v1/ldap-servers/4

  • /v3/keystores?type=jwks

Versioning

  • Version is placed in the URI immediately following the base path.
  • Versioning is done by resource. The version of a resource is incremented whenever there is a breaking change to any endpoint within that resource.
    For more information see the Extensibility section.
  • All HTTP operations on a given resource are versioned at the same time and MUST share a version.
    For example, if a breaking change needs to occur in one of the /v1/keystore endpoints,
    then those /v1 endpoints will remain and /v2/keystore endpoints will be created for all keystore endpoints.
  • All endpoints for a given version of a given resource SHOULD be compatible with one another, when they are interoperable.

Preview

Preview was a set of guidelines around releasing beta versions of endpoints for early feedback from customers.
It was repealed in favor of either using the next numerical version or not releasing endpoints not ready for consumption. Repealed in 3.0.0 on 2021-08-17

Resource

  • Resource names MUST be written in Kebab case.
  • Resource names MUST be plural unless it represents a singleton object.
    • For example, if an API only supports a single SMTP Server object, the path would be /smtp-server and not /smtp-servers.

Examples:

  • /v1/computer-groups
  • /v1/computer-management/self-service is a singleton resource, so it makes sense to be singular

Sub-resource

  • A resource MAY contain sub-resources.
  • Commonly implemented sub-resources SHOULD be their own resource ().
  • Apply the same naming conventions to sub-resources as for resources.

Examples:

  • /v1/admin-accounts/4/licenses
  • /v1/admin-accounts/4/licences/1

IDs

Usage

  • In order to operate (read or update) on a single resource in a collection, the particular resource MUST be specified by ID.
  • If a resource is a singleton resource, then it doesn't have an ID.
  • IDs in sub-resources SHOULD correspond to IDs in resources to follow a hierarchical resource pattern.
    • Example: /v1/computers-inventory/{id}/attachments/{attachmentId} where attachmentId should only refer to an object tied to the computer found by id.
  • The ID field in the body of an object is ignored upon POST and PUT. These fields SHOULD be marked as readOnly: true in Swagger.
  • Objects which reference other objects MUST only reference by its product-assigned ID.
  • The name of an ID field that points to a separate resource MUST follow the pattern of <ObjectType>Id, and not be enclosed inside of its own object.

Format

  • An ID MUST either be an integral number or a UUID, with UUID being preferred.
  • IDs MUST be represented as a string data type, even if it is an integral number.
  • All numerical IDs MUST be a positive integer.

Examples:

  • /v1/admin-accounts/1/licenses returns license collection for vpp admin-account with ID 1.
  • /v1/admin-accounts/1/licenses/2 returns license with ID 2 for vpp admin-account with ID 1.
  • /v1/vpp-licenses/1 returns vpp license with ID 1, this SHOULD correspond with above ID.
  • /v1/self-service has no ID.

Query Parameters

Query parameters MAY be used in filtering, paging, and sorting query requests.

Common Query Parameters

ParameterFormatExampleNotes
Pagepage=<page_number>page=3Default value 0 (the first page of results. Value is 0-based.
Page Sizepage-size=<results_per_page>page-size=10Default number of results per page is 100.
Sortsort=<field_name>[:sort_direction][,<secondary_sort_field_name>[:sort_direction]]*sort=category,name:descDefault sort direction is 'asc' (Ascending). Use 'desc' for Descending ordering. Additional sort parameters are supported and determine order of results that have equivalent values for previous sort parameters

Service Call / Service Invocation

When the primary function of an endpoint is to invoke an action, the endpoint SHOULD be called using a POST. Also:

  • The base form of a verb SHOULD be used to specify which operation to invoke, e.g., review, initialize.
  • Path in swagger SHOULD be annotated with 'x-action' vendor extension.

Examples:

  • /v1/system/initialize
  • /v1/deploy-package

Common Endpoints

The following URI patterns SHOULD be used for these common CRUD endpoints:

Endpoint CategoryHTTP MethodURI PatternQuery Parameters / BodySuccess responseExampleNotes
Create a resourcePOST/{version}/{resource}Body - resource data201 HTTP status code with a resource location/v1/buildings
Read a resource by IDGET/{version}/{resource}/{id}200 HTTP status code with a resource/v1/buildings/4
Read collection of resourcesGET/{version}/{resource}[?{queryParams}]Query Parameters - optional filtering, sorting, and paging parameters200 HTTP status code with a search result/v1/buildings?page=1&page-size=10
Replace a resourcePUT/{version}/{resource}/{id}Body - resource data200 HTTP status code/v1/buildings/3
Update part of a resourcePATCH/{version}/{resource}/{id}Body - resource data only containing fields to be updated200 HTTP status code/v1/buildings/3
Delete a resourceDELETE/{version}/{resource}/{id}204 HTTP status code/v1/builidngs/2
Delete multiple resourcesPOST/{version}/{resource}/delete-multipleBody - IDs to Delete204 HTTP status code/v1/builidngs/delete-multiple
Invoke a servicePOST/{version}/{resource}/[{id}/]{service}Body - service parameters202 HTTP status code with an ID of operation/v1/buildings/1/refreshPath in swagger SHOULD be annotated with 'x-action' vendor extension.
Information ExchangePOST/PUT/{version}/{resource}/[{id}/]Body - resource data or service parameters202 HTTP status code with an ID of operation or 200 HTTP status code with a resource/v1/device-enrollment/{id}/upload-tokenEndpoints where the request and response bodies are different schemas.

Filter Response

Object definition of filter response SHOULD be in the following format:

{
    "results": [
        {resourceWithMetadata},
        {resourceWithMetadata}
    ],
    "totalCount": 2
}

Object Creation Response

  • The response of requests which create an object MUST be in the form denoted below:
{
    "id":"1",
    "href":"<Object_Location_Link>"
}
  • The response of requests which create an object MUST contain the Location header with a value of the URL for the created resource.
    • In the special case when multiple objects are created, the response body MUST be an array of the above JSON objects, and the location header MUST be omitted.
      These special cases MUST be clearly documented.

Deleting Multiple Resources

  • Operations which delete multiple objects MUST be implemented as a service invocation that takes a body containing the IDs to be deleted.
  • All endpoints which delete multiple objects MUST have an service invocation name of delete-multiple, e.g., /v1/buildings/delete-multiple

Headers

The following sections describe how headers SHOULD be used:

Standard Request Headers

HeaderTypeDescription
AuthorizationstringAuthorization header for the request
AcceptContent typeThe requested content type for the response. (e.g. application/json)
Content-TypeContent typeMime type of request body (PUT/POST/PATCH)

Standard Response Headers

HeaderRequiredDescription
Content-TypeAll ResponsesThe content type

Custom Headers

  • Custom headers MUST NOT be required in requests for the basic operation of a given endpoint.
  • It is RECOMMENDED that query parameters be used instead of custom headers, however there MAY be some scenarios where custom headers make more sense.
  • Use of x- as the naming convention for custom headers is not allowed per RFC 6648.
HeaderTypeDescriptionExampleNotes
DeprecationstringUsed to indicate the date when the endpoint was deprecated.Deprecation: date="date"

OpenAPI

  • Jamf APIs use OpenAPI to document functionality.
  • Swagger files MUST be valid according to the OAS3 specification.
  • OpenAPI is widely used and supported by Swagger, a set of open-source tools that can help you design, build, document and consume REST APIs.

General OpenAPI Conventions

  • Attributes or parameters that support a specific set of values MUST be documented with enums (e.g. webhook events: [ComputerAdded, ComputerCheckIn, etc.]).
  • Value constraints (e.g. required, min value, max value, string length) on each parameter and field MUST be documented.
  • References should be used for commonly referenced objects. For example:
    • Multiple objects MAY reference and contain the same Scope definition.
    • Multiple endpoints MAY return or accept the same Script definition.

Operations/Endpoints

  • All operations MUST contain summary and description attributes.
  • All operation parameters MUST be documented and MUST contain a description attribute.
  • All fields that are required for a POST operation MUST be marked as required.
  • Individual endpoints SHOULD NOT document 401, 403, 404, 5xx since these are the same for all endpoints.
  • Endpoint summaries SHOULD:
    • Be a brief single sentence.
    • Not end in a period.
    • Have every word that is represented by a schema be capitalized.
    • Use the present tense imperative, e.g., {{Get the Thing}} and not {{Gets the Thing}}.

Schemas/Models/Object Definitions

  • All schemas MUST be documented and each MUST contain a description attribute.

  • All fields MUST include a data type consistent with the supported values for that attribute or object.

  • Default values MUST be used to specify the value for an optional parameter when that parameter is not included in the request to the server.

  • Example values MUST be included for all attributes that do not have a default value and should be included for those that do.

  • All object definitions use upper-case camel case names, e.g., CacheSettings.

  • All schema property names use lower-case camel case (pascalCase), e.g., firstName

  • All schemas MUST be defined under definitions section. Definitions MUST NOT be defined in-line in operation. The exception is when a request or response only includes a file.

  • Acronyms only have the first letter capitalized, for instance UUID becomes Uuid, and iOS becomes Ios.

  • Swagger enums SHOULD be used for any field for which a static finite known set of known values are acceptable.

  • Endpoints which use the oneOf operator to allow or return different schemas MUST give examples of each schema.

    • Example endpoint:
    requestBody:
      content:
        'application/json':
          schema:
            oneOf:
              - $ref: '#/components/schemas/Schema1'
              - $ref: '#/components/schemas/Schema2'
            discriminator:
              propertyName: appleType
          examples:
            Schema1:
              appleType: McIntosh
              schema1Field: Foo
            Schema2:
              appleType: Caramel
              schema2Field: Bar
    

Error Handling

Errors are returned from endpoints in a consistent manner to simplify client-side detection and processing.
This is done through a combination of HTTP status codes and a defined error object.
An array of errors is returned, one for each problem in the request.
A subset of allowable HTTP status codes are utilized with the error being mapped to the most logical HTTP status, and an error object returned in the document body provides more detail about the specific error.

Optimistic Resource Locking

Optimistic Locking MAY be used by Jamf API resources.
Each time an object with optimistic locking is updated, its version is updated as well.
This version is included in each GET and PUT request.
When a PUT request includes a stale version, the PUT request is denied.
The client is then expected to GET the latest version of the object and try the PUT again.

  • VersionId field MUST be in an object's metadata to hold the object version.
  • 409 Conflict status code MUST be returned when a PUT contains a stale version.
  • OPTIMISTIC_LOCK_FAILED Reason Code MUST be returned when a PUT contains a stale version.

Error Object

When the API returns an HTTP status other than 2xx or 3xx, the document body SHALL consist of an ApiError object that contains additional information regarding the error.
The object itself contains several fields - see the object definition below.

    ApiError:
      type: object
      properties:
        httpStatus:
          type: integer
          description: HTTP status of the response
        traceId:
                type: string
                format: uuid
                example: '7AF8CEAB-F7EE-45C3-B573-38A4F93C6E7E'
        errors:
          type: array
          items:
            required:
            - field
            type: object
            properties:
              code:
                type: string
                description: Error-specific code that can be used to identify localization
                  string, etc.
                example: "INVALID_FIELD"
              field:
                type: string
                description: Name of the field that caused the error.
                example: Name
              description:
                type: string
                description: A general description of error for troubleshooting/debugging.
                  Generally this text should not be displayed to a user; instead refer
                  to errorCode and it's localized text
                example: I've just picked up a fault in the AE35 unit. It's going
                  to go 100% failure in 72 hours
              id:
                type: string
                description: id of object with error. Optional.
                example: "3"

Example:

{
  "traceId": "7AF8CEAB-F7EE-45C3-B573-38A4F93C6E7E",
  "errors": [
    {
      "id": "3",
      "code": 8675309,
      "field": "name",
      "description": "I've just picked up a fault in the AE35 unit. It's going to go 100% failure in 72 hours"
    }
  ]
}

Security

Handling Sensitive Data

  • Jamf APIs SHALL NOT provide secrets via the API in plain text except in special cases such as generating client credentials or private keys, and at most return the secret only once at creation.
  • In the case of requests containing secrets, they SHALL be included in either a POST, PUT, or PATCH requests.
  • Resources that accept secrets SHOULD be handled with PATCH, so the secret does not need to be included in each request.
  • Secrets MUST NOT be passed as query parameters.
  • Secret fields MUST be write-only, MUST NOT appear in responses, and MUST be documented as such in Swagger.
  • Fields that store secrets MUST be documented with a Swagger string format value of password .

Privileges

  • Endpoints SHOULD NOT return different 2xx responses based on client privileges.

Authentication / Authorization

Authentication and Authorization are currently beyond the scope of this document.

CSRF / XSRF

Cross-site request forgeries can be mitigated by the client and server implementations, but the exact mechanism is outside the scope of the REST API.

Extensibility

Jamf APIs MUST honor the contract with existing API consumers while still allowing for iteration. If breaking changes are introduced on a resource then the major version of the resource MUST be incremented.

What Constitutes a Breaking Change

  • A field is removed.
  • A field changes types.
  • A required field is added.
  • An existing field becomes required.
  • A path changes.
  • Additional required parameters are added.
  • Required headers are changed.
  • An HTTP method on a resource is removed.
  • Removal or changing of response codes.

Guidelines

  • Data types of existing fields MUST NOT change. A new field SHOULD be created, and the old field deprecated.
  • If an endpoint was removed for security reasons, it is exempt from this process.

Deprecation Process

  • Mark endpoints deprecated in Swagger with deprecated: true and x-deprecation-date: <year-month-day> where month and day are two digits.
  • Add deprecation of endpoint to release notes.
  • Deprecated public endpoints SHOULD remain for at least one year.
  • The Deprecation header as specified in this RFC draft MUST be included on deprecated endpoints and also documented in Swagger.
    • Example version: 10.12
    • Example date: Fri, 11 Nov 2018 23:59:59 GMT

Exceptions to Deprecation Process

  • Endpoints MAY be removed without following the deprecation process and SHOULD be documented publicly when possible. Examples of when this exception process may apply include, but are not limited to:
    • Removal of functionality from a third party application that Jamf APIs rely upon.
    • Issues related to security of the endpoint or product.

Changelog