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 5.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.
Only HTTP 2xx codes, 429, and 412, when applicable, MUST be documented on each endpoint. When developers would like to give more context to other error codes, documentation of those codes is RECOMMENDED.
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.
- Since additional fields break PUT endpoints for clients, PATCH requests are RECOMMENDED to be used in place of PUT requests. More Info
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.
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 versionkeystore
is the resource3
is the idcertificates
is the sub-resource1
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 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
.
- For example, if an API only supports a single SMTP Server object, the path would be
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
- The ID field of an object SHOULD be named
id
. - 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}
whereattachmentId
should only refer to an object tied to the computer found byid
.
- Example:
- 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
Parameter | Format | Example | Notes |
---|---|---|---|
Page | page=<page_number> | page=3 | Default value 0 (the first page of results. Value is 0-based. |
Page Size | page-size=<results_per_page> | page-size=10 | Default number of results per page is 100. |
Sort | sort=<field_name>[:sort_direction][,<secondary_sort_field_name>[:sort_direction]]* | sort=category,name:desc | Default 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:
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.
- 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.
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
Header | Type | Description |
---|---|---|
Authorization | string | Authorization header for the request |
Accept | Content type | The requested content type for the response. (e.g. application/json ) |
Content-Type | Content type | Mime type of request body (PUT/POST/PATCH) |
Standard Response Headers
Header | Required | Description |
---|---|---|
Content-Type | All Responses | The 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.
Header | Type | Description | Example | Notes |
---|---|---|---|---|
Deprecation | string | Used 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
anddescription
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
When an endpoint has rate limits, a 429 response code MUST be documented on the endpoint denoting the rate limit has been exceeded.
Three vendor extensions MUST also be documented on the endpoint path:
- x-rate-limit-starting-requests: 100
- x-rate-limit-refill-requests-per-period: 10
- x-rate-limit-refill-period-in-seconds: 3600
- When starting-requests is equal to requests per period we have what is sometimes called an ‘All At Once’ policy, when refill-requests-per-period is equal to 1 we have what is sometimes called a ‘Roling Window’ policy
- refill-requests-per-period SHOULD be less than or equal to starting-requests
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 ofpassword
.
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 is added (breaks PUT, but not PATCH).
- 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.
- An increase in the restrictiveness of constraints, including the addition of a new constraint.
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
andx-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
- Example version:
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.
- 2024-07-31 - 5.0.0
- Rate Limits Guideline Text
- Name of
ID
Field Guideline Text - What Constitutes a Breaking Change
- New case added Guideline Text
- Not All Possible HTTP Codes Need to be Documented Per-Endpoint Guideline Text
- 2024-02-22 - 4.1.0
- PATCH and PUT SHOULD Return HTTP 204 Guideline Text
- 2023-09-11 - 4.0.0
- Behavior of Null Values in PATCH Requests Guideline Text
- Repeal URI Base Path MUST be /api Guideline Text
- Add 422 Unprocessible Entity to Recommended Status Codes Guideline Text
- RFC 4180 to be used for CSV (Comma Separated Values) Guideline Text
- Examples of Each 'oneOf' Schema MUST Be Documented Guideline Text
- Blank Fields SHOULD Have Value of Null When Appropriate Guideline Text
- 2022-10-04 - 3.2.0
- Codify Definition of an ID Guideline Text
- 2021-08-17 - 3.1.0
- Documenting Nullable Fields Guideline Text
- 2021-06-10 - 2.1.0
- Specify Example Time Format Without Date - Guideline Text
- 2021-05-13 - 2.0.0
- Versioning the Style Guide - Guideline Text
Updated 4 months ago