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

Filters, ordering and paging

Everything in a list's query string that is not a shared parameter is a filter on the entity's own fields. How to write one, how to page and order a list, and the two habits that keep a filter honest: check it against a count, and always send a limit.

Who this is for. Anyone reading lists: the headless developer building a listing, the integrations engineer syncing records. The full parameter table is on Requests and responses; this page explains how to use it and where it bites.

A filter is a field name

Name a field and give it a value, and the list keeps the records whose field equals it. A dot reaches through a relation: parent.id=72 keeps the pages under page 72, and activeRevision.layout.id=92 the pages whose live revision uses layout 92. A prefix on the name changes the comparison:

PrefixMeansExample
(none)equalsparent.id=72
!not equal!slug=authentication
> <greater or less than, exclusive>position=30
%contains%activeRevision.name=Car
|any of a comma-separated list|id=818,1878

Send NULL to the bare relation name for an empty relation: parent=NULL finds the home page, the one page with no parent. Encode the prefixes in a URL (%25 for %, %7C for |); every HTTP client in the examples does it for you.

Pages under the API reference whose name contains “Car”
GET /cdna-api/pages/pages?parent.id=818&%activeRevision.name=Car&properties={id,name} HTTP/1.1
Host: hub.coredna.com
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Response 200 OK
{
  "result": "success",
  "totalCount": 1,
  "data": [
    {
      "id": 824,
      "name": "Carts"
    }
  ]
}
Real response from this documentation site

Check a filter against a count

Versioned records (pages, posts, books, FAQ, custom entity records) keep name and their areas on activeRevision, not on the record itself, so filter on activeRevision.name, not name, for these types. %name=Car compares a field pages do not have, and the same request returns all 32 pages under the API reference:

The same filter on a field pages do not have
GET /cdna-api/pages/pages?parent.id=818&%name=Car&properties={id,name}&limit=1 HTTP/1.1
Host: hub.coredna.com
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Response 200 OKFilter ignored
{
  "result": "success",
  "totalCount": 32,
  "data": [
    {
      "id": 819,
      "name": "Authentication"
    }
  ]
}
Real response from this documentation site

Filters only match stored fields, exactly as named in the record's model in the generated Reference: use >, < and % rather than >=, <= or a suffix form from another API (created_gte, name_contains), and filter on a stored field rather than a computed one such as fullPath, or on a date in YYYY-MM-DD HH:MM:SS. The habit that confirms a filter took effect: compare a filtered list's totalCount with the unfiltered list's. Confirming a request did what you asked has the rest of the checks to build in.

Ordering

orderBy takes field:ASC or field:DESC, comma-separated for more than one key: orderBy=position:ASC,id:DESC. Two things to know:

  • Stored fields only. Sort by a stored field from the record's model; a computed field such as fullPath cannot be sorted on.
  • Write ASC or DESC. Anything but ASC sorts descending. Lower case works.

Paging

limit is the page size. Then either page, counting from 1, or offset, the number of records to skip; when both are sent, offset wins. totalCount in the response is the number of matches before paging, so the number of pages is ceil(totalCount / limit).

Always send a limit. Without one, most lists return every match: this site's file list sends back 973 records. That is fine for twenty menus and a timeout for twenty thousand orders. Two families cap it instead. The job lists under /cdna-api/hooks (jobs, their actions, events and categories; the path keeps the module's old name) and the custom entity lists take 100 rows when no limit is sent and refuse more than 500 with a 400, “The limit may not exceed 500”; the job runs list allows up to 1,000. The paging how-to has a loop that pages a large set without timing out.

A limit, page or offset that is not a whole number is refused with a 400 that names the parameter. Send Accept: application/json on every request, so that this answer, like every other, comes back as JSON.

A limit that is not a number, with Accept: application/json
GET /cdna-api/pages/pages?limit=two&properties={id} HTTP/1.1
Host: hub.coredna.com
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Response 400 Bad Request
{
  "result": "error",
  "message": "The limit parameter has to be a whole number of at least 0."
}
Real response from this documentation site

Paging a list that changes

A page is computed when it is asked for. If records are added or removed between two requests, a record can appear on two pages or on none. For a one-off export, order by id:ASC and filter on >id with the last id you received instead of moving page on: each request then starts exactly where the last one stopped, whatever changed in between.

HTTP
GET /cdna-api/pages/pages?orderBy=id:ASC&limit=100&properties={id,name}
GET /cdna-api/pages/pages?orderBy=id:ASC&limit=100&%3Eid=412&properties={id,name}
Go deeper

Was this page helpful?

Your answer goes to the docs team.