Read this guide first. Core dna is hosted, so there is nothing to install; your repository holds only templates and front-end assets; and a new kind of page starts in the admin, with a layout. In half a day, from what the platform is to a page you built, deployed and rendered in Smarty or Twig.
L0
Unit 01 · What you are building on
The URL surfaces of a site
What you are building onPage 5 of 28Updated
Every Core dna site answers on the same paths: the public site at /, the admin at /cdna-admin, the REST API at /cdna-api, GraphQL at /cdna-api/graphql, custom endpoints at /cdna-api/webhook and the MCP server at /cdna-mcp. A real request to each, and what each answers when something is wrong.
Who this is for. Everyone, and above all whoever will call a site from code. After this page you will recognise each part of a site by its URL, and know what each one answers when a credential, a method or a path is wrong.
These are not separate systems. Each is a door into the same site, on the site's own hostname, answering from the same database. The requests below were sent to this documentation site; in the plain HTTP examples its hostname is shown as www.example.com.
The public site: /
Every path that does not start with one of the prefixes below belongs to the public site, the frontend: pages, blog posts, products, forms, the member area. Most of its URLs come from records rather than from routes anyone wrote: a page's URL is its place in the page tree. Files in the site's own folder, such as /dist/app.css, are served as they are, and uploaded media is served from /files/….
HTTP
GET /
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Security-Policy: script-src 'self' https://cdnjs.cloudflare.com 'nonce-9PqAxZeVwUAO6B3lrd0oIh6k+hk='
The Content-Security-Policy header carries a new nonce on every request. An inline script without that nonce does not run, which is why templates print it into every <script> tag.
The admin: /cdna-admin
The backend, where content, layouts, users and settings are managed. Signed out, it sends you to its sign-in page.
HTTP
HEAD /cdna-admin
HTTP/1.1 302 Found
Location: https://www.example.com/cdna-admin/login
Signing in needs a backend user on that site. A member's account is refused at the admin's sign-in, even with the right password.
The REST API: /cdna-api
URLs have the shape /cdna-api/<module>/<entity>: a site's pages are /cdna-api/pages/pages, and one page is /cdna-api/pages/page/{id}. /cdna-api/pages on its own is a 404.
List two pages, with and without a credential
GET /cdna-api/pages/pages?properties={id,name,fullPath}&limit=2 HTTP/1.1
Host: hub.coredna.com
Authorization: Bearer YOUR_ACCESS_TOKEN
Accept: application/json
Most requests need a credential: an OAuth access token, which is the preferred kind, or an API key, the older kind, sent as Authorization: Bearer …; or the session of someone signed in. With none, the answer is 401. Authentication and authorisation says which to use, and the API reference lists every endpoint.
GraphQL: /cdna-api/graphql
Queries only, sent as a POST with the query in a JSON body, with the same credentials as REST. GraphQL on Core dna reads data; writes go through the REST API. A GET answers 405.
A GraphQL query, and the same URL fetched with GET
{
"result": "error",
"message": "Access to the specified record has been denied. Please contact your administrator if you think this is a mistake."
}
Real responses from this documentation site; the debug element a development environment adds to the 405 is left out
Read the status, not the message: the 405 above carries the platform's generic access-denied text rather than a method-specific one.
Custom endpoints: /cdna-api/webhook/<slug>
An endpoint you define in the admin without platform code: a job whose trigger is a web event, with a slug, the methods it accepts, and whether it requires authentication. A synchronous job answers the request itself, with whatever its actions produce; any other job is queued and runs afterwards. Anything in the path after the slug is passed to the job.
A web event with Authentication required on admits backend users only: an OAuth access token, an API key or a signed-in admin session. Anyone else, a signed-in member included, gets a 403. A slug that does not exist, or a method the web event does not accept, answers 404.
A synchronous job's endpoint on this site, which requires authentication
The site's MCP server, for AI assistants such as the one in your editor. It is protected by OAuth: a client without a token is told where to start, signs you in on the site, and from then on works as you, with your permissions. Only a backend user can authorise it.
HTTP
GET /cdna-mcp
HTTP/1.1 401 Unauthorized
Content-Type: application/json; charset=utf-8
WWW-Authenticate: Bearer resource_metadata="https://www.example.com/.well-known/oauth-protected-resource/cdna-mcp"
{"error":"unauthorized","error_description":"Missing or invalid Authorization header. Initiate OAuth using the resource_metadata URL in the WWW-Authenticate header."}
Every site is also an OAuth authorisation server, which is where the tokens for the API and the MCP server come from: /oauth/authorize signs a person in and asks their consent, /oauth/token issues the tokens, and /.well-known/oauth-authorization-server describes the rest. OAuth clients walks through a complete flow.
What is never served
Some paths under the site's folder are never sent as files: the templates/ folder, any path with a part that starts with a dot (.git, .settings.json), files such as package.json and composer.json, and any file type outside the allowed list of styles, scripts, images, fonts, icons, source maps, text, CSV and XML. A request for one of them goes on to the public site, which usually answers 404.
A site can add file types to the allowed list with a .static-assets.json file at the site root, an allowedTypes map of extension to MIME type. Script and server-configuration types, such as php, can never be added.