API ReferenceStart here

Requests and responses

The parameters every endpoint shares, shaping a response with properties, what every response looks like, the status codes, and the failures that still answer 200.

Parameters every endpoint shares

ParameterOnDoes
propertiesEvery endpointThe fields and relations to return; below
limitListsRecords per response. Without it a list returns every match, except jobs and custom entity records, which page by 100 and refuse more than 500
pageListsThe page of limit records, counting from 1
offsetListsRecords to skip. It wins over page
orderByListsfield:ASC or field:DESC, comma-separated. Stored fields only
retrieveUnpublished=1ListsIncludes unpublished records, for backend users. One record read by its id comes back either way
Anything elseListsA filter on a stored field or a relation; below

A limit, page or offset that is not a whole number is a 400 that names it.

Filters

WriteMatches
slug=apisEqual
!slug=apisNot equal
>id=100, <id=100Greater, less. There is no greater-or-equal
%25name=guideContains (%, encoded as %25)
%7Cid=3,4,9Any of a list (|, encoded as %7C)
parent.id=12A field of a relation
parent=NULLAn empty relation

Filter on stored fields, named exactly as in the record's model: not a computed field such as fullPath, not >=, and not a suffix form such as id_gt. Compare the totalCount with the unfiltered list's to confirm a filter applied. On pages, posts, books, FAQ and custom entity records the name lives on the revision, so filter on activeRevision.name.

Shaping the response with properties

WriteReturns
{id,name,fullPath}Those fields
{id,parent{id,name}}A relation, with its own fields
{id,title:activeRevision.name}A field under another name, following a dotted path
{id,created(format:"Y-m-d")}A getter given an argument, by name
{id,activeRevision{intro:areaText(intro)}}An area's text; areas belong to the revision
{pages{id,name}}A list under its plural, data.pages

Separate fields with commas. Spaces are removed before the list is split, so {id name} asks for one field called idname, which comes back as "Error: Getter for property idname … not found" in a 200. The names are the model's, listed in the Reference.

On a POST or PUT, properties goes in the query string. On a GET whose selector is too long for a URL, send it as the body, raw and in braces, with the header X-Body-Has-Parameters: 1; the example at the foot of this page does. Only properties is read from a GET body: filters, limit and orderBy stay in the query string, and a JSON body on a GET is ignored. Send the selector with its braces.

Request bodies

Send JSON with Content-Type: application/json, or a form. A body that is not valid JSON is read as empty, so a PUT with a stray comma answers success and changes nothing. Send Accept: application/json on every request, so that errors come back as JSON too.

To set a relation, sendThe API
7 or {"id": 7}Points it at record 7
{"id": 7, "name": "Guides"}Points it at record 7 and edits record 7
{"name": "Guides"}Creates a record and points at it
nullClears it. 0 is not an id
[3, 4] on a to-many relationReplaces the whole set; it does not add

published is a date and time: true publishes now, a future time schedules, false or null unpublishes. Making a revision live never changes it.

The response

KeyHolds
resultsuccess or error, always
dataThe record or records
totalCountOn a list, the matches before paging
messageWhat happened, on writes and errors
validationErrorsThe fields that failed, on a 422
Requestdata is
A list with a bare selector, {id,name}The list
A list without properties, or with {pages{…}}An object holding the list under the plural: data.pages
One record with a bare selectorThe record
One record without propertiesAn object holding it under the singular: data.page
A create or an editThe record under the singular, with a message

A few routes answer outside the envelope: the file upload, the OAuth endpoints and synchronous web events. Their pages show what they return.

Status codes

StatusMeans
200Success, creates included
400A parameter the API cannot read, such as a limit that is not a whole number, or a bad list of ids
401No credential was found
403A credential was found and refused, a CSRF token is missing, or the user may not do this
404No such route or record. Also what a signed-in member gets on a route that is not for members
405The route does not take this method, such as GET /cdna-api/graphql
409A conflict: deleting the live revision, or continuing a form submission that is already complete
422Validation failed; validationErrors says where
500The server could not complete the request

Form submissions add 207 (stored, a later step still has errors) and 202 (a payment is pending); Form submissions has the details.

Errors

A failed validation names each field you sent, with a list of the rules it failed:

{
  "result": "error",
  "validationErrors": {
    "email": [
      { "message": "This value should not be blank.", "code": "c1051bb4-d103-4f74-8988-acbcafc7fdc3", "propertyPath": "email" }
    ]
  },
  "message": "This action failed validation"
}

Read the keys of validationErrors to put each message next to its field. Log message, but do not show it: it can hold class names. On a development site an error can also carry a trace.

When a 200 is not what you expected

SymptomCause
A filtered list returns every recordThe filter names a field the record does not store; Filters
A field is "Error: Getter for property … not found"A misspelt field, or two fields with only a space between them
An edit succeeds and the site does not changeThe new revision is not live. Send "activeRevision": true inside the patchRevision or revisions object (at the top level it takes a revision id); and send originRevision with patchRevision, or it copies the newest revision, not the live one
A PUT succeeds and nothing changedThe body was not valid JSON; or, on a custom entity record, an area was set at the top level instead of in patchRevision
A patch changed the wrong componentPositions drift after a removal; Collection operations
A list has a totalCount and no rowsCustom entity records listed by the singular identifier; lists take the plural
A date area holds the wrong dateDates are stored as sent, in any format
A job never startsIts entity event's method is not one the record fires; Entity Events

Routes not documented here

The admin uses routes of its own under /cdna-api, such as its record picker and tree views. They are internal, change without notice, and are left out of this reference.

Send the selector in a GET body
GET /cdna-api/pages/page/1885 HTTP/1.1
Host: hub.coredna.com
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Content-Type: text/plain
X-Body-Has-Parameters: 1

{id,name}
Response 200 OK
{
  "result": "success",
  "data": {
    "id": 1885,
    "name": "API sandbox"
  }
}
Real response from this documentation site