Name: http-api-design
Owner: GoCardless
Description: HTTP Design Guidelines
Created: 2014-07-09 09:57:46.0
Updated: 2018-01-06 17:21:31.0
Pushed: 2016-02-12 09:55:32.0
Homepage: null
Size: 41
Language: null
GitHub Committers
User | Most Recent Commit | # Commits |
---|
Other Committers
User | Most Recent Commit | # Commits |
---|
This document provides guidelines and examples for GoCardless APIs, encouraging consistency, maintainability, and best practices.
All endpoints must follow the core JSON API spec
Changes from JSON API:
200 OK
with the full resource to simplify internal logic.Example of keying the resource by type:
/posts/1
son
osts": {
"id": 1
The API should only support JSON.
Reference: http://www.mnot.net/blog/2012/04/13/json_or_xml_just_decide
GET
, POST
, PUT
, DELETE
) to operate on resources./payments?subscription=xyz
rather than
/subscriptions/xyz/payments
. Nested resources enforce relationships that could
change and makes clients harder to write.api.gocardless.com
GET https://api.gocardless.com/payments
GET https://api.gocardless.com/payments?status=failed&sort=-created
GET https://api.gocardless.com/payments?sort=created
GET https://api.gocardless.com/payments/1234
GET https://api.gocardless.com/subscription_amendments?subscription=1234
GET https://api.gocardless.com/payments/1234?include=events
GET https://api.gocardless.com/payments/1234?fields=amount
GET https://api.gocardless.com/payments/1234,444,555,666
POST https://api.gocardless.com/payments/1234/actions/cancel
GET https://api.gocardless.com/payment
GET https://api.gocardless.com/payment/123
GET https://api.gocardless.com/payment/action
GET https://api.gocardless.com/payment/create
GET https://api.gocardless.com/subscriptions/1234/amendments
GET https://api.gocardless.com/payments/desc
GET https://api.gocardless.com/payments?id[]=11&id[]=22
Here's an example of how HTTP verbs map to create, read, update, delete operations in a particular context:
| HTTP METHOD | POST | GET | PUT | PATCH | DELETE | |:————- |:————– |:———— |:———— |:———— |:———— | | CRUD OP | CREATE | READ | UPDATE | UPDATE | DELETE | | /plans | Create new plan| List plans | Bulk update | Error | Delete all plans | | /plans/1234 | Error | Show Plan If exists | If exists, full/partial update Plan; If not, error | If exists, update Plan using JSON Patch format; If not, error | Delete Plan |
Avoid resource actions. Create separate resources where possible.
/refunds?payment=ID&amount=1000
/payments/ID/refund
Where special actions are required, place them under an actions
prefix.
Actions should always be idempotent.
/payments/ID/actions/cancel
Don?t set values in keys.
s": [
id": "125", "name": "Environment"},
id": "834", "name": "Water Quality"}
s": [
125": "Environment"},
834": "Water Quality"}
Always return string ids. Some languages, like JavaScript, don't support big ints. Serialize/deserialize ints to strings if storing ids as ints.
Error responses should include a message for the user, an internal error type (corresponding to some specific internally determined constant represented as a string), and links to info for developers.
There must only be one top level error. Errors should be returned in turn. This makes internal error logic and dealing with errors as a consumer of the API easier.
Validation and resource errors are nested in the top level error under errors
.
The error is nested in error
to make it possible to add, for example, deprecation errors on successful requests.
The HTTP status code
is used as a top level error, type
is used as a sub error, and nested
errors
may have more specific type
errors, such as invalid_field
.
Formatting errors should be separate from errors handled by the integration.
Formatting errors include things like field presence and length, and are returned when incorrect data is sent to the API.
An example of an error that should be handled by the integration is attempting to create a payment against a mandate that has expired. This is an edge case that the API integration needs to handle. Do not mask these errors as validation errors. Return them as a top level error instead.
request_id
, type
, reason
, code
and message
.type
MUST relate to the reason
. For example, use it to categorise the error: reason: api_error
.reason
MUST be specific to the error.message
MUST be specific.documentation_url
, request_url
and id
.id
for server errors (5xx). The id
should point to the exception you track internally.
rror": {
"documentation_url": "https://api.gocardless.com/docs/beta/errors#access_forbidden",
"request_url": "https://api.gocardless.com/requests/REQUEST_ID",
"request_id": "REQUEST_ID",
"id": "ERROR_ID",
"type": "access_forbidden",
"code": 403,
"message": "You don't have the right permissions to access this resource"
reason
and message
.reason
MUST be specific to the error.field
.
rror": {
"top level errors": "...",
"errors": [{
"field": "account_number",
"reason": "missing_field",
"message": "Account number is required"
}]
200 OK
- everything worked as expected.400 Bad Request
- e.g. invalid JSON.401 Unauthorized
- no valid API key provided.402 Request Failed
- parameters were valid but request failed.403 Forbidden
- missing or invalid permissions.404 Not Found
- the requested item doesn?t exist.422 Unprocessable Entity
- parameters were invalid/validation failed.500, 502, 503, 504 Server errors
- something went wrong on GoCardless? end.The versioning scheme is designed to promote incremental improvement to the API and discourage rewrites.
Server initiated events such as webhooks should not contain serialised resources. If a resource changed, provide its id instead and let the client request it using a version.
Versions should be dated as ISO8601 (YYYY-MM-DD)
Maintain old API versions for at least 6 months.
The API version must be set using a custom HTTP header. The API version must not be defined in the
URL structure (e.g. /v1
) because it makes incremental change impossible.
GoCardless-Version: 2014-05-04
Enforce the header on all requests.
Validate the version against available versions. Do not allow dates up to a version.
The API changelog must only contain backwards-incompatible changes. All non-breaking changes are automatically available to old versions.
Reference: https://stripe.com/docs/upgrades
The use of X-Custom-Header
has been deprecated.
Resource filters MUST be in singular form.
Multiple ids should be supplied to a filter as a comma separated list, and should be translated into an OR
query. Chaining multiple filters with &
should be translated into an AND
query.
/refunds?payment=ID1,ID2&customer=ID1
/refunds?payments=ID1,ID2&customer=ID1
All list/index endpoints must be paginated by default. Pagination must be reverse chronological.
Only support cursor or time based pagination.
limit=50
after=NEWEST_RESOURCE
before=null
limit=500
Parameters:
| Name | Type | Description |
| ————- |:————-:| —–:|
| after
| string | id to start after |
| before
| string | id to start before |
| limit
| string | number of records |
Paginated results are always enveloped:
eta": {
"cursors": {
"after": "abcd1234",
"before": "wxyz0987"
},
"limit": 50
ayments": [{
?
Full or partial updates using PUT
should replace any parameters passed and ignore fields not submitted.
/items/id_123
d": "id_123",
eta": {
"created": "date",
"published": false
/items/id_123 { "meta": { "published": true } }
d": "id_123",
eta": {
"published": false
PATCH is reserved for JSON Patch operations.
POST
, PUT
and PATCH
expect JSON bodies in the request. Content-Type
header MUST be set to application/json
.
For unsupported media types a 415
(Unsupported Media Type) response code is returned.
Most responses return an ETag
header. Many responses also return a Last-Modified
header. The
values of these headers can be used to make subsequent requests to those resources using the
If-None-Match
and If-Modified-Since
headers, respectively. If the resource has not changed, the
server will return a 304 Not Modified
. Note that making a conditional request and receiving a 304
response does not count against your rate limit, so we encourage you to use it whenever possible.
Cache-Control: private, max-age=60
ETag: <hash of contents>
Last-Modified: updated_at
The following header values must be declared in the Vary header: Accept
, Authorization
and Cookie
.
Any of these headers can change the representation of the data and should invalidate a cached version. This can be useful if users have different accounts to do admin, each with different privileges and resource visibility.
Reference: https://www.mnot.net/cache_docs/
All responses should support gzip.
See JSON-API: http://jsonapi.org/format/#fetching-filtering
JSON responses should be pretty printed.
Explicitly provide an ISO8601 timestamp with timezone information (DateTime in UTC).
Use the exact timestamp for API calls that allow a timestamp to be specified.
These timestamps look something like 2014-02-27T15:05:06+01:00
. ISO 8601 UTC format: YYYY-MM-DDTHH:MM:SSZ.
All endpoints must be rate limited. The current rate limit status is returned in the HTTP headers of all API requests.
-Limit-Limit: 5000
-Limit-Remaining: 4994
-Limit-Reset: Thu, 01 Dec 1994 16:00:00 GMT
ent-Type: application/json; charset=utf-8
ection: keep-alive
y-After: Thu, 01 May 2014 16:00:00 GMT
Limit-Reset uses the HTTP header date format: RFC 1123 (Thu, 01 Dec 1994 16:00:00 GMT)
Exceeding rate limit:
29 Too Many Requests
"message": "API rate limit exceeded.",
"type": "rate_limit_exceeded",
"documentation_url": "http://developer.gocardless.com/#rate_limit_exceeded"
Support Cross Origin Resource Sharing (CORS) for AJAX requests.
Resources:
Any domain that is registered against the requesting account is accepted.
rl -i https://api.gocardless.com -H "Origin: http://dvla.com"
/1.1 302 Found
ss-Control-Allow-Origin: *
ss-Control-Expose-Headers: ETag, Link, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset, OAuth-Scopes, Accepted-OAuth-Scopes
ss-Control-Allow-Credentials: false
ORS Preflight request
PTIONS 200
ss-Control-Allow-Origin: *
ss-Control-Allow-Headers: Authorization, Content-Type, If-Match, If-Modified-Since, If-None-Match, If-Unmodified-Since, Requested-With
ss-Control-Allow-Methods: GET, POST, PATCH, PUT, DELETE
ss-Control-Expose-Headers: ETag, RateLimit-Limit, RateLimit-Remaining, RateLimit-Reset
ss-Control-Max-Age: 86400
ss-Control-Allow-Credentials: false
All API request MUST be made over SSL, including outgoing web hooks. Any non-secure requests
return ssl_required
, and no redirects are performed.
/1.1 403 Forbidden
ent-Length: 35
essage": "API requests must be made over HTTPS",
ype": "ssl_required",
ocs": "https://developer.gocardless.com/errors#ssl_required"
See JSON-API: http://jsonapi.org/format/#fetching-includes
See JSON-API: http://jsonapi.org/format/#fetching-sparse-fieldsets
Set a Request-Id
header to aid debugging across services.