Home

Awesome

Archived: This information has been relocated to the cloud_controller_ng repo

Cloud Controller API v3 Style Guide

Table of Contents

<!-- START doctoc generated TOC please keep comment here to allow auto update --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> <!-- END doctoc generated TOC please keep comment here to allow auto update -->

Overview

This document serves as a style guide for version 3 of the Cloud Controller API. It is intended to act as a repository for patterns and best practices when designing and developing new API endpoints.

This is a living document; It will change over time as we learn more about our users and develop features.

Guiding Principles

API Technologies

API Design Inspirations

Overview Example

Here is an example request to retrieve apps:

GET /v3/apps?names=dora,kailan&order_by=created_at&page=1&per_page=2

Note: To make the examples in this style guide more human-readable, the urls in this style guide to not encode query strings. All requests and responses MUST contain correctly encoded characters. For more information see Query Parameters.

Here is the respective response body:

{
  "pagination": {
    "total_results": 3,
    "total_pages": 2,
    "first": {
      "href": "http://api.example.com/v3/apps?names=dora,kailan&order_by=created_at&page=1&per_page=2"
    },
    "last": {
      "href": "http://api.example.com/v3/apps?names=dora,kailan&order_by=created_at&page=2&per_page=2"
    },
    "next": {
      "href": "http://api.example.com/v3/apps?names=dora,kailan&order_by=created_at&page=2&per_page=2"
    },
    "previous": null
  },
  "resources": [
    {
      "guid": "guid-00133700-abcd-1234-9000-3f70a011bc28",
      "name": "dora",
      "state": "STOPPED",
      "created_at": "2015-08-06T00:36:20Z",
      "updated_at": "2015-08-06T00:36:20Z",
      "links": {
        "self": {
          "href": "https://api.example.org/v3/apps/guid-00133700-abcd-1234-9000-3f70a011bc28"
        },
        "space": {
          "href": "https://api.example.org/v3/spaces/ab09cd29-9420-f021-g20d-123431420768"
        },
        "processes": {
          "href": "https://api.example.org/v3/apps/guid-00133700-abcd-1234-9000-3f70a011bc28/processes"
        },
        "routes": {
          "href": "https://api.example.org/v3/apps/guid-00133700-abcd-1234-9000-3f70a011bc28/routes"
        },
        "packages": {
          "href": "https://api.example.org/v3/apps/guid-00133700-abcd-1234-9000-3f70a011bc28/packages"
        },
        "droplets": {
          "href": "https://api.example.org/v3/apps/guid-00133700-abcd-1234-9000-3f70a011bc28/droplets"
        },
        "start": {
          "href": "https://api.example.org/v3/apps/guid-00133700-abcd-1234-9000-3f70a011bc28/start",
          "method": "POST"
        },
        "stop": {
          "href": "https://api.example.org/v3/apps/guid-00133700-abcd-1234-9000-3f70a011bc28/stop",
          "method": "POST"
        }
      }
    },
    {
      "guid": "guid-bd7369a8-deed-ff1a-2315-77410293a922",
      "name": "kailan",
      "state": "STOPPED",
      "created_at": "2015-08-07T00:40:52Z",
      "updated_at": "2015-08-07T00:40:52Z",
      "links": {
        "self": {
          "href": "https://api.example.org/v3/apps/guid-bd7369a8-deed-ff1a-2315-77410293a922"
        },
        "space": {
          "href": "https://api.example.org/v3/spaces/881029ab-4edd-4920-af10-6386967209d1"
        },
        "processes": {
          "href": "https://api.example.org/v3/apps/guid-bd7369a8-deed-ff1a-2315-77410293a922/processes"
        },
        "routes": {
          "href": "https://api.example.org/v3/apps/guid-bd7369a8-deed-ff1a-2315-77410293a922/routes"
        },
        "packages": {
          "href": "https://api.example.org/v3/apps/guid-bd7369a8-deed-ff1a-2315-77410293a922/packages"
        },
        "droplets": {
          "href": "https://api.example.org/v3/apps/guid-bd7369a8-deed-ff1a-2315-77410293a922/droplets"
        },
        "start": {
          "href": "https://api.example.org/v3/apps/guid-bd7369a8-deed-ff1a-2315-77410293a922/start",
          "method": "POST"
        },
        "stop": {
          "href": "https://api.example.org/v3/apps/guid-bd7369a8-deed-ff1a-2315-77410293a922/stop",
          "method": "POST"
        }
      }
    }
  ]
}

Requests

URL Structure

All endpoints MUST be prefixed with /v3/.

Collections of resources are referenced by their resource name (plural)
Pattern: /v3/:resource_name
Example: /v3/apps

Individual resources are referenced by their resource name (plural) followed by the resource's guid
Pattern: /v3/:resource_name/:guid
Example: /v3/apps/25fe21b8-8de2-40d0-93b0-c819101d1a11

GET

Retrieve a single resource or a list of resources. MUST be idempotent.

Examples

Show Individual Resource
GET /v3/apps/:guid
List Collection of Resources
GET /v3/apps

Responses

Show Resource
ScenarioCodeBody
Authorized User200Resource
Unauthorized User404Error
Nonexistent Resource404Error
List Collection
ScenarioCodeBody
User With Complete Visibility200List of All Resources
User With Partial Visibility200List of Visible Resources
User With No Visibility200Empty List

POST

Used to create a resource, upload a file, or trigger an action.

Examples

Create a Resource
POST /v3/apps
{
  "name": "example_app"
}
Trigger an Action
POST /v3/processes/:guid/actions/scale
{
  "instances": 100,
  "memory_in_mb": 2048
}

Responses

Create Resource
ScenarioCodeBody
Authorized User (Synchronous)201Created Resource
Authorized User (Asynchronous)202Empty w/ Location Header -> Job
Unauthorized User403Error
Trigger Action
ScenarioCodeBody
Authorized User (Synchronous)200Resource
Authorized User (Asynchronous)202Empty w/ Location Header -> Job
Read-Only User403Error
Unauthorized User404Error
Nonexistent Resource404Error

PUT

Not used. To update a resource, use PATCH

PATCH

Used to update a portion of a resource.

Examples

Partially Update a Resource
PATCH /v3/apps/:guid
{
  "name": "new_app_name"
}

Responses

Update a Resource
ScenarioCodeBody
Authorized User (Synchronous)200Updated Resource
Authorized User (Asynchronous)202Empty w/ Location Header -> Job
Read-Only User403Error
Unauthorized User404Error
Nonexistent Resource404Error

DELETE

Used to delete a resource.

† Some load balancers remove bodies from DELETE requests. Since the API could be running behind any load balancer, we cannot depend on DELETE requests with bodies.

Examples

Delete a Resource
DELETE /v3/apps/:guid

Responses

Delete Resource
ScenarioCodeBody
Authorized User (Asynchronous)202Empty w/ Location Header -> Job
Authorized User (Synchronous)204Empty
Read-only User403Error
Unauthorized User404Error
Nonexistent Resource404Error

Response Codes

Successful Requests

Status CodeDescriptionVerbs
200 OKThis status MUST be returned for synchronous requests that complete successfully and have a response body. This MUST only be used if there is not a more appropriate 2XX response code.GET, PATCH, POST (for actions)
201 CreatedThis status MUST be returned for synchronous requests that result in the creation of a new resource.POST
202 AcceptedThis status MUST be returned for requests that have been successfully accepted and will be asynchronously completed at a later time. See more in the async section.POST, PATCH, DELETE
204 No ContentThis status MUST be returned for synchronous requests that complete successfully and have no response body.DELETE

Redirection

Status CodeDescriptionVerbs
302 FoundThis status MUST be returned when the cloud controller redirects to another location. Example: Downloading a package from an external blob store.GET

Client Errors

Status CodeDescriptionVerbs
400 Bad RequestThis status MUST be returned for requests that provide malformed or invalid data. Examples: malformed request body, unexpected query parameters, or invalid request fields.GET, PATCH, POST, DELETE
401 UnauthenticatedThis status MUST be returned if the requested resource requires an authenticated user but there is no OAuth token provided, or the OAuth token provided is invalid.GET, POST, PATCH, DELETE
403 ForbiddenThis status MUST be returned if the request cannot be performed by the user due to lack of permissions. Example: User with read-only permissions to a resource tries to update it.POST, PATCH, DELETE
404 Not FoundThis status MUST be returned if the requested resource does not exist or if the user requesting the resource has insufficient permissions to view the resource.GET, POST, PATCH, DELETE
422 Unprocessable EntityThis status MUST be returned if the request is syntactically valid, but performing the requested operation would result in a invalid state. Example: Attempting to start an app without assigning a droplet.POST, PATCH, DELETE

404 vs 403

If a resource does not exist OR a user does not have read permissions for it, then 404 MUST be returned for PATCH/DELETE requests. This is to prevent leaking information about what resources exist by returning 403s for resources that exist, but a user does not have read permissions for. Explicitly:

No PermissionsRead-Only PermissionsRead/Write Permissions
Resource Exists4044032XX
Resource Does Not Exist404404404

Server Errors

Status CodeDescription
500 Internal Server ErrorThis status MUST be returned when an unexpected error occurs.
502 Bad GatewayThis status MUST be returned when an external service failure causes a request to fail. Example: Being unable to reach requested service broker.
503 Service UnavailableThis status MUST be returned when an internal service failure causes a request to fail. Example: Being unable to reach Diego or CredHub.

Resources

A resource represents an individual object within the system, such as an app or a service. It is represented as a JSON object.

A resource MUST contain the following fields:

A resource MAY contain additional fields which are the attributes describing the resource.

A resource MUST contain a links field containing a links object, which is used to provide URLs to associated resources, relationships, and actions for the resource.

A resource MUST include a self link object in the links field.

Example

{
  "guid": "00112233-4455-6677-8899-aabbccddeeff",
  "created_at": "2015-07-06T23:22:56Z",
  "updated_at": "2015-07-08T23:22:56Z",

  "name": "dora",
  "description": "an example app",

  "links": {
    "self": {
      "href": "http://api.example.com/v3/apps/00112233-4455-6677-8899-aabbccddeeff"
    }
  }
}

Pseudo-Resources

Pseudo-Resources are API endpoints that are nested under other resources and do not have a unique identifier. Their lifecycles are tied directly to their parent resource. These endpoints MAY require different permissions to operate on than their parent resource.

Example

GET /v3/apps/:guid/environment_variables

Actions

Actions are API requests that are expected to initiate change within the Cloud Foundry runtime. This is differentiated from requests which update a record, but require additional updates — such as restarting an app — to cause changes to a resource to take affect.

Actions MUST use use POST as their HTTP verb.

Actions MUST be nested under the /actions path for a resource

Actions MAY accept a request body.

Actions MUST be listed in the links for the related resource.

Example

POST /v3/apps/:guid/actions/start

Field Names

Resource Fields MUST include ONLY the following characters:

Resource fields that accept multiple values MUST be pluralized.

Example


{
  "guid": "guid-1",
  "color": "red",
  "animals": [
    "fish",
    "lion"
  ]
}

Links

Links provide URLs to associated resources, relationships, and actions for a resource. Links are represented as a JSON object.

Each member of a links object is a "link".
A link MUST be a JSON object. A link MUST contain a href field, which is a string containing the link's relative URL. A link MAY contain a method field, containing the HTTP verb used for the URL. If the method field is not included then the link MUST be available using GET.

Example

{
  "links": {
    "self": {
      "href": "http://api.example.com/v3/apps/00112233-4455-6677-8899-aabbccddeeff"
    },
    "space": {
      "href": "http://api.example.com/v3/spaces/123e4567-e89b-12d3-a456-426655440000"
    },
    "current_droplet": {
      "href": "http://api.example.com/v3/apps/00112233-4455-6677-8899-aabbccddeeff/relationships/current_droplet"
    },
    "start": {
      "href": "http://api.example.com/v3/apps/00112233-4455-6677-8899-aabbccddeeff/start",
      "method": "PUT"
    }
  }
}

Collections

A collection is a list of multiple Resources. A collection is represented as a JSON object.

A collection MUST contain a resources field. The resources field is an array containing multiple Resources.

A collection MUST contain a pagination field containing a pagination object.

Example

{
  "pagination": {
    "total_results": 2,
    "total_pages": 1,
    "first": {
      "href": "http://api.example.com/v3/apps?page=1&per_page=10"
    },
    "last": {
      "href": "http://api.example.com/v3/apps?page=1&per_page=10"
    },
    "next": null,
    "previous": null
  },
  "resources": [
    {
      "guid": "a-b-c",
      "created_at": "2015-07-06T23:22:56Z",
      "updated_at": "2015-07-08T23:22:56Z",

      "links": {
        "self": {
          "href": "http://api.example.com/v3/apps/a-b-c"
        }
      }
    },
    {
      "guid": "d-e-f",
      "created_at": "2015-07-06T23:22:56Z",
      "updated_at": "2015-07-08T23:22:56Z",

      "links": {
        "self": {
          "href": "http://api.example.com/v3/apps/d-e-f"
        }
      }
    }
  ]
}

Pagination

Pagination MAY be used by Collections to limit the number of resources returned at a time. Pagination is requested by a client through the use of query parameters. Pagination is represented as a JSON object.

Pagination MUST include a total_results field with an integer value of the total number of records in the collection.

Pagination MUST include a total_pages field with an integer value of the total number of pages in the collection at the current page size.

Pagination MUST include the following fields for pagination links:

Pagination links MAY be null. For example, if the page currently being displayed is the first page, then previous link will be null.

When pagination links contain a URL, they MUST be a JSON object with a field named href containing a string with the URL for the next page.

The following query parameters MUST be supported for pagination:

When collections are ordered by a subset of fields, each field MAY be prepended with "-" to indicate descending order direction. If the field is not prepended, the ordering will default to ascending.

If there are additional pagination query parameters, the parameters MUST have names that conform to the acceptable query parameter names.

Pagination URLs MUST include all query parameters required to maintain consistency with the original pagination request. For example, if the client requested for the collection to be sorted by a certain field, then the pagination links MUST include the proper query parameter to maintain the requested sort order.

Example

{
  "pagination": {
    "total_results": 20,
    "total_pages": 2,
    "first": {
      "href": "http://api.example.com/v3/apps?order_by=-created_at&page=1&per_page=10"
    },
    "last": {
      "href": "http://api.example.com/v3/apps?order_by=-created_at&page=2&per_page=10"
    },
    "next": {
      "href": "http://api.example.com/v3/apps?order_by=-created_at&page=2&per_page=10"
    },
    "previous": null
  }
}

Query Parameters

Query Parameters MUST include ONLY the following characters:

Query parameters that accept multiple values MUST be pluralized.

If any request receives a query parameter it does not understand, the response MUST be a 400 Bad Request.

All query parameters MUST be properly url-encoded. If a single query parameter value includes the comma (,) character, the comma MUST be double encoded.

Note: For readability purposes, the examples throughout this document do not show encoded query strings.

Examples

Single value: GET /v3/apps?names=firstname

Multiple values: GET /v3/apps?names=firstname,secondname

Single value with comma: GET /v3/apps?names=comma%2Cname

Filtering

Filtering is the use of query parameters to return a subset of resources within a Collection.

Filter query parameters MUST have names that conform to the acceptable query parameter names.

Filters MUST allow a client to request resources matching multiple values by accepting a comma-delimited list of possible values.

Filter parameters MUST be able to be combined with other filters on the same collection.

When multiple filters are provided, the results MUST match all specified filters.

Empty filters (/v3/apps?names= or /v3/buildpacks?stack=cflinuxfs2,) matches on Active Support's definition of blank meaning that nil, "" and [] will be valid matches for the empty filter.

Examples

Single value request: GET /v3/apps?names=the_name

This will return all apps with name the_name.

Multiple value request: GET /v3/apps?names=first_name,second_name

This will return all apps with name the_name OR second_name.

Combined filters: GET /v3/apps?names=the_name&state=STARTED

This will return all apps with name the_name AND state STARTED.

Empty filters when resource has NULLs: GET /v3/buildpacks?stack=

This will return all buildpacks with stack NULL.

Empty filters when resource has empty strings (""): GET /v3/routes?path=pepper,,tabi

This will return all routes with path "pepper", "" OR "tabi".

Filtering on Inequalities (Proposal)

Note: This is a proposal and is not currently implemented on any API endpoints

Resources MAY support filtering on inequalities for some fields.

Strictly Less Than: GET /v3/processes?instances[lt]=5

This will return all processes with less than 5 instances.

Less Than or Equal To: GET /v3/processes?instances[lte]=5

This will return all processes with less than 5 instances or exactly 5 instances.

Strictly Greater Than: GET /v3/processes?instances[gt]=5

This will return all processes with greater than 5 instances.

Greater Than or Equal To: GET /v3/processes?instances[gte]=5

This will return all processes with greater than 5 instances or exactly 5 instances.

Filtering on Non-Equality (Proposal)

Note: This is a proposal and is not currently implemented on any API endpoints

Resources MAY support filtering on inequalities for some fields.

GET /v3/apps?names[not]=my-app

This will return all apps with names other than my-app.

Errors

Status Codes

The HTTP status code returned for errors MUST be included in the documented status codes.

Response Body

The response body MUST return a JSON object including an errors key with a list of at least one error objects.

Each error object in the list MUST include the following keys:

Example

{
  "errors": [
    {
       "detail": "Relationships is not a hash.",
       "title": "CF-UnprocessableEntity",
       "code": 10008
    },
    {
       "detail": "Name must be a string.",
       "title": "CF-UnprocessableEntity",
       "code": 10008
    }
  ]
}

Error Messages

Error messages should be descriptive and gramatically correct, so they can be surfaced by API clients without need for modification.

Each error message MUST:

Relationships

Relationships represent named associations between resources. Relationships can be used to create, read, update, and delete associations through the relationship sub resource.

A resource MAY have a relationship with exactly one instance of a resource (a to-one relationship).

A resource MAY have a relationship with multiple instances of a resource (a to-many relationship).

Resources MAY implement none, some, or all of the relationship operation listed below for each of its associations.

Relationships at Resource Creation

To-One Relationships

Create an association between the resource being created and a single, existing resource.

Example:

POST /v3/apps
{
  "name": "blah",
  "relationships": {
   "space": { "data": { "guid": "1234" }}
  }
}

To-Many Relationships

Create associations between the resource being created and several existing resources.

Example:

POST /v3/apps
{
  "name": "blah",
  "relationships": {
    "routes": {
      "data": [
        {"guid": "2345"},
        {"guid": "3456"}
      ]
    }
  }
}

Relationships for Existing Resources

Viewing, updating, and removing relationships for existing resources can be accessed through nested relationship resource endpoints.

To-One Relationships

Viewing

View the association between a resource and a single other resource for the given relationship.

Example:

GET /v3/apps/:app_guid/relationships/space

Response:

{
  "data": { "guid": "space-guid" }
}
Setting

Update the association for a resource to a single other resource for the given relationship.

Example:

PATCH /v3/apps/:app_guid/relationships/space
{
  "data": { "guid": "space-guid" }
}
Clearing

Remove the association between two resources for the given relationship.

Example:

PATCH /v3/apps/:app_guid/relationships/space
{
  "data": null
}

To-Many Relationships

Viewing

View the associations between a resource and multiple other resources for the given relationship.

Example:

GET /v3/apps/:app_guid/relationships/routes

Response:

{
  "data": [
    { "guid": "route-guid" },
    { "guid": "other-route-guid" }
  ]
}
Adding

Add additional associations between a resource and other resources for the given relationship.

Example:

POST /v3/apps/:app_guid/relationships/routes
{
  "data": [{ "guid": "route-guid" }, { "guid": "route-guid" }]
}
Removing

Remove the association between a resource and another resource for the given relationship.

DELETE /v3/apps/:app_guid/relationships/routes/:route_guid

Note: Some load balancers remove request bodies from DELETE requests. Because of this, we cannot support atomically removing more than one resource in a single request.

Replacing All

Replace all associations between a resource and other resources for the given relationship.

PATCH /v3/apps/:app_guid/relationships/routes
{
  "data": [{ "guid": "route-guid" }, { "guid": "other-route-guid" }]
}
Clearing All

Clear all associations between a resource and other resources for the given relationship.

PATCH /v3/apps/:app_guid/relationships/routes
{
  "data": []
}

Nested Resources

Nested resources MAY be accessible through their parent resource.

GET /v3/apps/:app_guid/droplets

This will be equivalent to

GET /v3/droplets?app_guids=:app_guid

Including Related Resources

This is a mechanism for including multiple related resources in a single response.

Resources and collections MAY accept an include query parameter with a list of resource paths.

Each resource path MUST be a series of period-separated relationship names. For example: app.space.organization

The list of resource paths MUST be comma delimited. For example: include=space,space.organization

Included resources MUST be returned in an included object on the primary resource or collection.

Duplicate included resources MUST NOT be repeated. For example: Listing multiple apps in the same space with include=space will only return the space once.

GET /v3/apps?include=space,space.organization
{
  "pagination": {
    "total_results": 2,
    "total_pages": 1,
    "first": {
      "href": "http://api.example.com/v3/apps?include=space,space.organization&page=1"
    },
    "last": {
      "href": "http://api.example.com/v3/apps?include=space,space.organization&page=1"
    },
    "next": null,
    "previous": null
  },
  "resources": [
    {
      "guid": "app1-guid",
      "relationships": {
        "space": {"guid": "space2-guid"}
      }
    },
    {
      "guid": "app2-guid",
      "relationships": {
        "space": {"guid": "space1-guid"}
      }
    }
  ],
  "included": {
    "spaces": [
      {
        "guid": "space1-guid",
        "relationships": {
          "organization": {"guid": "org1-guid"}
        }
      },
      {
        "guid": "space2-guid",
        "relationships": {
          "organization": {"guid": "org1-guid"}
        }
      }
    ],
    "organizations": [
      {
        "guid": "org1-guid"
      }
    ]
  }
}

Pagination of Included Resources (Proposal)

Note: This is a proposal and is not currently implemented on any API endpoints

When including to-many relationships, there can be more related resources than can be returned in a single response. In that case, only the first page of the included resources will be returned.

GET /v3/spaces/:guid?include=apps
{
  "guid": "space-guid",
  "...": "...",
  "relationships": {
    "apps": {
      "data": [
        {"guid": "app-guid-1"},
        "...",
        {"guid": "app-guid-100"}
      ]
    }
  },
  "included": {
    "apps": {
      "resources": [
        {"guid": "app-guid-1"},
        "...",
        {"guid": "app-guid-10"}
      ]
    }
  }
}

The included resources are paginated using pagination filters for each included resource:

GET /v3/spaces/:guid?include=apps&page[apps]=10
{
  "guid": "space-guid",
  "...": "...",
  "relationships": {
    "apps": {
      "data": [
        {"guid": "app-guid-1"},
        "...",
        {"guid": "app-guid-100"}
      ]
    }
  },
  "included": {
    "apps": {
      "resources": [
        {"guid": "app-guid-90"},
        "...",
        {"guid": "app-guid-100"}
      ]
    }
  }
}

Pagination Links

The pagination filters for included resources are included in the top-level pagination links. The next pagination links will page through all included resources before moving to the next page of the root resource.

{
  "pagination": {
    "...": "...",
    "first": {
      "href": "https://api.example.com/v3/spaces?include=apps&page=1&page[apps]=1"
    },
    "last": {
      "href": "https://api.example.com/v3/spaces?include=apps&page=10&page[apps]=10"
    },
    "next": {
      "href": "https://api.example.com/v3/spaces?include=apps&page=1&page[apps]=2"
    },
    "previous": null
  }
}

GUID Hiding

Resources that are visible to any given user can have relationships/links with other resources that that user does not have read access for.

In these cases, the following rules apply:

  1. If the resource is scoped to a particular organization/space and is shared to a different organization/space, then the owning organization/space guid will be visible.
  2. In all other cases, users will not be able to see the guids of resources that they otherwise do not have read access for.

Asynchronicity

Individual endpoints are responsible for behaving either asynchronously (return 202 status code) or synchronously (return non-202 status code).

Since Cloud Controller collaborates with multiple external Cloud Foundry components, many endpoints will have asynchronous side effects that do not necessarily need to be represented by an async job on the API. For example, scaling the number of instances of a process will trigger their asynchronous creation in the runtime. However, the user's action was to increment the instance count in the CC's data store, which is not an asynchronous operation. The CC then works behind the scenes to make the system consistent by creating the LRPs (much as it would if the runtime lost state and CC needed to recover the desired state).

In general, good signs an endpoint should return an async job are:

  1. Updating state in the CC will take significant time/processing. Example: recursive deletes
  2. Updating state in the CC requires talking to the blobstore. Example: file uploads

Keep in mind that only the user/client that issued the request triggering the async job will have the link to track the job. If other users or system components care about the operation, it is helpful to surface state elsewhere -- either on the resource itself or through the creation of another resource.

Triggering Async Actions

The CC will return a 202 with a Location header pointing to the async job.

DELETE /v3/resource/:guid
202 Accepted
Location: /v3/jobs/123

Monitoring Async Actions

GET requests made to the job resource MUST return 200 with information about the status of the job.

GET /v3/jobs/123
200 OK
{
  "state": "PROCESSING",
  "operation": "service_instance.create",
  "status": "Warming the shards",
  "warnings": [],
  "links": {
    "self": {
      "href": "https://api.example.org/v3/jobs/123"
    }
  }
}

The job resource MAY include links to resources affected by the operation.

GET /v3/jobs/123
200 OK
{
  "state": "COMPLETE",
  "operation": "splines.reticulate",
  "status": "Splines successfully reticulated",
  "warnings": [],
  "links": {
    "self": {
      "href": "https://api.example.org/v3/jobs/123"
    },
    "splines": {
      "href": "https://api.example.org/v3/splines/456"
    }
  }
}

Viewing Errors from Async Actions

The job resource MUST surface any errors that occur during the async operation.

GET /v3/jobs/123
200 OK

{
  "state": "FAILED",
  "operation": "sun.fly_to",
  "status": "Failed to fly to the sun",
  "errors": [
      {
       "detail": "Wings are too waxy.",
       "title": "CF-UnprocessableEntity",
       "code": 10008
    },
    {
       "detail": "Hubris is too high.",
       "title": "CF-UnprocessableEntity",
       "code": 10008
    }
  ],
  "warnings": [],
  "links": {
    "self": {
      "href": "https://api.example.org/v3/jobs/123"
    }
  }
}

Viewing Warnings from Async Actions

The job resource MAY surface any warnings that occur during the async operation.

GET /v3/jobs/123
200 OK

{
  "state": "COMPLETED",
  "operation": "hitchhiking.galaxy",
  "status": "done",
  "warnings": [
      {
       "detail": "don't panic",
      },
      {
       "detail": "bring your towel",
      },
  ],
  "links": {
    "self": {
      "href": "https://api.example.org/v3/jobs/123"
    }
  }
}

When there are no warnings, warnings field will have a value of an empty array.

Requesting Specific Fields Resources (Proposal)

Note: This is a proposal and is not currently implemented on any API endpoints

Sparse Fields

Clients could wish to see only a specific set of fields from the API.

The fields query parameter MAY be provided.

The fields parameter MUST be a comma-delimited list of field names.

If the fields parameter is present, the API MUST return only the specified fields.

GET /apps/:guid?fields=guid,name
{
  "guid": "some-guid",
  "name": "Zach"
}

Hidden Fields

Certain resources MAY not return some fields by default. For example, a field might be computationally expensive, or require a certain permission to return.

The fields query parameter MAY be provided.

The fields parameter MUST be a comma-delimited list of field names.

If the fields parameter is present, the API MUST return the the specified fields in addition to the default set of fields.

Without Fields Parameter:

GET /apps/:guid
{
  "guid": "some-guid"
}

With Fields Parameter:

GET /apps/:guid?fields=expensive_field
{
  "guid": "some-guid",
  "expensive_field": "$$$$"
}

Proposal: Fields For Sub-Resources

If we want to be able to filter the fields of included resources, we could do something like:

GET /v3/apps/:guid?fields=guid,name&fields[droplet]=guid
{
  "guid": "some-guid",
  "name": "my-app",
  "included": {
    "droplet": {
      "guid": "droplet-guid"
    }
  }
}

Proposal: Mass Deletes

Currently, users can only delete resources one-by-one or as part of a cascading delete. This would enable users to delete multiple matching resources with a single request.

Example:

DELETE /v3/spaces/:guid/routes

202 Accepted