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 02 · Writing content

Custom URLs and slugs

A record's address comes from its slug, or from a custom URL that replaces it. How slugs are made and checked, how to set and clear a custom URL, and three things to get right: clearing a custom URL, setting one only after the record is created, and starting it with a slash.

Who this is for. The integrations engineer creating records in bulk, or moving content from another system and keeping its URLs.

Slugs

A slug is one segment of a path. A page's address is its parent's path followed by its slug, so /developer-docs/api-reference is the page with slug api-reference under the page with slug developer-docs. Top-level pages are children of the home page. The read-only fullPath gives the address.

  • The characters: letters, digits, hyphens and underscores. A space or punctuation is refused.
  • Left out on create, a page's slug is made from its name, with -1, -2 and so on added when that slug is taken.
  • Unique among siblings. A page cannot take a slug another page under the same parent has: a 422, "This value is already used." on slug.
  • Changing a slug moves the record and everything under it, and the platform adds no redirect from the old address. If the old address was public, add a rewrite.

Checking a slug before you write

Pages, books, book units, product descriptions and other types with slugs have a check-slug route beside their list, which tells you whether a slug is free without saving anything. It answers 200 whatever the outcome: read result, not the status.

Check a page slug under a parent
POST /cdna-api/pages/pages/check-slug HTTP/1.1
Host: hub.coredna.com
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Content-Type: application/json

{
  "slug": "api-reference-v2",
  "parent": 72
}
Response
{
  "result": "success"
}
{
  "result": "error",
  "message": "A record with this slug already exists"
}
{
  "result": "error",
  "message": "Invalid name for slug"
}
{
  "result": "error",
  "message": "message.error.slug.parent_empty"
}
Real responses from this documentation site; each label names the body that was sent
  • Pages need parent, since a slug only has to be unique among siblings. Without it the answer is the untranslated key message.error.slug.parent_empty. For a top-level page, send the home page's id.
  • When renaming, send the record's id as well, so its own slug does not count as taken.
  • Books, product descriptions and other types without a parent take the slug alone: POST /cdna-api/books/books/check-slug {"slug": "start-here"}.
  • A check is a moment in time. Another write can take the slug before yours, so handle the 422 on the write as well.

Custom URLs

A custom URL replaces the address made from the slugs. Pages (books, book units and book pages among them), blogs and blog posts, FAQ pages and questions, custom entity records and product descriptions can have one. When set, fullPath returns it, and originalFullPath keeps the address without it.

Set it with an object holding url:

Set a custom URL
PUT /cdna-api/pages/page/1885?properties={id,fullPath,customUrl{id,url}} HTTP/1.1
Host: hub.coredna.com
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Content-Type: application/json

{
  "customUrl": {
    "url": "/api-sandbox-custom-url"
  }
}
Response
{
  "result": "success",
  "data": {
    "id": 1885,
    "fullPath": "/api-sandbox-custom-url",
    "customUrl": {
      "id": 4,
      "url": "/api-sandbox-custom-url"
    }
  },
  "message": "The Page has been saved successfully."
}
{
  "result": "error",
  "validationErrors": {
    "customUrl.url": [
      {
        "message": "This value is already used.",
        "code": "23bd9dbf-6b9b-41cd-a99e-4844bcf3077f",
        "propertyPath": "customUrl.url"
      }
    ]
  },
  "message": "A validation error occurred; please check the submitted fields for errors."
}
Real responses from this documentation site, on its API sandbox page

A custom URL is unique on the site, across every record type: the 422 above came from giving the page the URL a custom entity record already had. A record has one custom URL at most, so a second write replaces the first.

To remove it, send an empty url; the record goes back to its own address:

Clear a custom URL
PUT /cdna-api/pages/page/1885?properties={id,fullPath,customUrl{id,url}} HTTP/1.1
Host: hub.coredna.com
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Content-Type: application/json

{
  "customUrl": {
    "url": ""
  }
}
Response 200 OK
{
  "result": "success",
  "data": {
    "id": 1885,
    "fullPath": "/api-sandbox",
    "customUrl": null
  },
  "message": "The Page has been saved successfully."
}
Real response from this documentation site, on its API sandbox page

Three things to get right

  • Clear a custom URL with {"url": ""}, not null.
  • A custom URL cannot be set on create. Create the record, then send customUrl in a second request with the new id.
  • Start the URL with /. It is stored and returned in fullPath exactly as sent.

One more thing to know when reading: a record whose type has no address of its own, such as a custom entity record whose type has no page, has fullPath "" when it has no custom URL. Treat an empty fullPath as "no address", never as the site's root.

Keeping old URLs when moving content in

When the old system's URLs differ from the ones Core dna would make, either give each record a custom URL in a second request after creating it, or let the records take their own addresses and add rewrites from the old URLs. Custom URLs keep the old address as the real one; rewrites redirect from it.

Go deeper

Was this page helpful?

Your answer goes to the docs team.