Guide 06 · Developer docs

The APIs: REST, GraphQL and custom endpoints

Every Core dna site answers three kinds of API request under /cdna-api: REST for everything, GraphQL for reads that span several entity types, and web events for endpoints you define yourself as jobs. Which fits which task, one worked request for each, and what is true of all three. Jobs were called hooks until the module was renamed Orchestration; the API's routes still use the old name.

L1–L3
Unit 01 · The REST API

Shaping the response with properties

One parameter decides what a response contains: properties, a selector that names fields, renames them, reaches into relations and calls getters with arguments. How to write one, what shape the response then takes, and how a mistake shows up, which is not as an error.

Who this is for. Every API reader. Leave properties out and a record comes back with its whole default projection: this site's How-to blog answers with more than forty fields, counts and paths included. Name what you need, every time; the response is smaller, faster and does not change shape when the model gains a field.

The selector

A selector is a list of fields in braces, separated by commas. Inside it:

  • A relation in braces returns the related record with the fields you name: parent{id,name}. A to-many relation returns a list: children{id,name}.
  • A dotted path reaches into a relation and returns one value, flat, under its last segment: activeRevision.name comes back as name.
  • An alias renames anything: title:activeRevision.name.
  • A getter with arguments takes them by name, in brackets: created(format:"j M Y").
An alias, a getter with an argument and a relation
GET /cdna-api/pages/page/72?properties={id,title:activeRevision.name,created(format:"Y-m-d"),parent{id,name}} HTTP/1.1
Host: hub.coredna.com
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Response 200 OK
{
  "result": "success",
  "data": {
    "id": 72,
    "title": "Developer Documentation",
    "created": "2026-08-18",
    "parent": {
      "id": 1,
      "name": "Home"
    }
  }
}
Real response from this documentation site

Commas, not spaces

Spaces are stripped before the list is split, so {id name} asks for one field called idname. The request still answers 200:

Two fields separated by a space
GET /cdna-api/pages/page/72?properties={id name} HTTP/1.1
Host: hub.coredna.com
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Response 200 OKFields glued together
{
  "result": "success",
  "data": {
    "idname": "Error: Getter for property idname in class Pages\\Models\\Api\\Response\\Page not found"
  }
}
Real response from this documentation site

That is also how any field the model does not have shows up, at any depth: a string starting with Error: in place of the value, inside a successful response. parent{id,nmae} returns the parent's id and an error string for nmae. A client that does not check for it will render the message on a page. In tests, fail on any string value that begins with Error: Getter for property.

Reading layout areas

On a versioned record (a page, a book page, a blog post, an FAQ, a custom entity record) the layout areas belong to the revision, not to the record, so the layout area getters are asked of activeRevision. areaText(name) returns a layout area's text, areaValues(name) a multiselect's values as a list. Nest them under the revision, or reach them with a dotted path and an alias to keep the response flat:

A layout area's text, read through the active revision
GET /cdna-api/pages/pages?parent.id=72&properties={id,name,lede:activeRevision.areaText(lede)} HTTP/1.1
Host: hub.coredna.com
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Response 200 OK
{
  "result": "success",
  "totalCount": 2,
  "data": [
    {
      "id": 818,
      "name": "API Reference",
      "lede": "Every endpoint of the Core dna REST API, with requests to copy in five languages and the real responses they return."
    },
    {
      "id": 1878,
      "name": "Reference",
      "lede": null
    }
  ]
}
Real response from this documentation site

Asked of the record itself, lede:areaText(lede) is an Error: string, because the record has no such getter. A page whose layout has no layout area of that name returns null, as page 1878 does above. To see every layout area with its type and value at once, select activeRevision{areasContent}: it is verbose, and the quickest way to learn a layout you have not met.

The shape of data

How you write the selector decides where the record sits in the response:

SelectorA listOne record
Bare: {id,name}data is the listdata is the record
Wrapped in the entity name: {pages{id,name}}, {page{id,name}}data.pagesdata.page
Nonedata.pages, every default fielddata.page, every default field

Pick one form for a client and keep to it; the bare selector gives the simplest code. totalCount stays beside data in every case. Writes answer the same way: PUT /cdna-api/pages/page/72?properties={id,name} returns the saved page as data, without one under data.page.

Where the selector goes

On a GET, in the query string, or in the request body when it is too long for a URL. On a POST or PUT, always in the query string, because the body holds the record. Query string or request body covers both. The same selector language drives the GraphQL endpoint, so what you learn here carries over.

Go deeper

Was this page helpful?

Your answer goes to the docs team.