Content Types (CPT)

Custom Post Types (CPTs) are one of the most powerful features of Ágora CMS. They let you define your own data structures with custom fields — beyond the standard blog — and expose them through the API using the same REST pattern you already know. With CPTs you can manage any structured content your site needs directly from the panel.

A CPT is a content template with custom fields: text, URL, boolean, number, date, image, and more. Common use cases include: products or services in a catalogue, case studies or portfolio projects, FAQs with question and answer, client testimonials, events with date and location, team members with role and photo. Any repeating structure you need to manage from the CMS can be modelled as a CPT.

GET /content-types — List available types

Returns the active content types for a website. Use it to discover which CPTs are available, get their slug (which you will use in the following endpoints) and obtain the field definitions. This endpoint is especially useful for generating dynamic code or validating the field schema before consuming entries.

Parameter Type Required Description
web_id string Yes Website ID
curl "https://your-panel.com/api/v1/content-types?web_id=abc-123" \
  -H "X-API-Key: your-api-key"
Expected response 200 OK
{
  "data": [
    {
      "id": "cpt-001",
      "nombre": "Projects",
      "slug": "projects",
      "description": "Project portfolio",
      "fields": [
        { "name": "client", "type": "text", "label": "Client", "required": true },
        { "name": "url", "type": "url", "label": "Project URL", "required": false },
        { "name": "highlight", "type": "boolean", "label": "Featured", "required": false }
      ]
    }
  ]
}

The fields array on each type describes the custom field structure: the key name (name), data type (type), human-readable label (label) and whether it is required (required). You can use this information to validate or type entries in your frontend. The content type id is also available here if you need it for internal references, but always use the slug in the URLs of the following endpoints.

GET /content-types/{type} — Entries of a type

{type} is the slug of the content type (e.g. projects, testimonials, faqs). Returns published entries with pagination, exactly like the posts endpoint. Custom fields appear in the fields object as key-value pairs where the key matches the name defined in the type.

Parameter Type Required Description
web_id string Yes Website ID
lang string No Language code
page integer No Page number. Default: 1
limit integer No Results per page. Default: 20
curl "https://your-panel.com/api/v1/content-types/projects?web_id=abc-123&lang=en&page=1" \
  -H "X-API-Key: your-api-key"
Expected response 200 OK
{
  "data": [
    {
      "id": "entry-001",
      "titulo": "Corporate website redesign",
      "slug": "corporate-website-redesign",
      "status": "published",
      "published_at": "2026-02-01T09:00:00Z",
      "featured_image_url": "https://your-panel.com/uploads/.../image.jpg",
      "fields": {
        "client": "Company XYZ",
        "url": "https://company-xyz.com",
        "highlight": true
      }
    }
  ],
  "meta": { "total": 8, "page": 1, "limit": 20, "pages": 1 }
}

Custom fields appear in the fields object as key-value pairs. The keys match the name of the field defined in the content type.

GET /content-types/{type}/{slug} — Single entry

Returns a specific CPT entry by its slug. Use this endpoint on detail pages for each entry — individual portfolio item, expanded FAQ, full testimonial, etc. The fields object contains all the custom fields for that entry.

curl "https://your-panel.com/api/v1/content-types/projects/corporate-website-redesign?web_id=abc-123&lang=en" \
  -H "X-API-Key: your-api-key"
Expected response 200 OK
{
  "data": {
    "id": "entry-001",
    "titulo": "Corporate website redesign",
    "slug": "corporate-website-redesign",
    "status": "published",
    "published_at": "2026-02-01T09:00:00Z",
    "featured_image_url": "https://...",
    "meta_title": null,
    "meta_description": null,
    "fields": {
      "client": "Company XYZ",
      "url": "https://company-xyz.com",
      "highlight": true
    }
  }
}

Response fields (entries)

Field Type Description
idUUIDUnique identifier of the entry
titulostringEntry title
slugstringURL-friendly string, unique per type and language
statusstringAlways published in the public API
published_atISO 8601Publication date
featured_image_urlstring | nullFeatured image URL
meta_titlestring | nullSEO title (single entry endpoint only)
meta_descriptionstring | nullSEO description (single entry endpoint only)
fieldsobjectCustom CPT fields as key-value pairs

Always validate the fields in fields before rendering them. Even if the CPT schema defines a field as required, it may arrive as null if the entry was created before that field was added, or if panel validation was not completed correctly. A defensive check prevents errors in production:

// Example: testimonials CPT
const { data: testimonials } = await fetch(
  `/api/v1/content-types/testimonials?web_id=${WEB_ID}`,
  { headers: { 'X-API-Key': API_KEY } }
).then(r => r.json())

// Render with defensive validation
testimonials.forEach(t => {
  const author = t.fields?.author ?? 'Anonymous'
  const role = t.fields?.role ?? ''
  const text = t.fields?.text ?? t.titulo
  const photo = t.featured_image_url ?? '/img/avatar-placeholder.jpg'
  // ... render
})

Use cases

Testimonials section. Create a "Testimonials" CPT with fields author (text), role (text), company (text) and text (textarea). Call the endpoint from your server component and render the testimonial cards. Content editors can add new testimonials from the panel without touching any code.

Product or service catalogue. Model each product as a CPT entry with fields for price, short description, product category, purchase URL and a "featured" boolean. Filter featured items on the frontend with entry.fields.highlight === true.

CMS-managed FAQs. Create a "FAQs" CPT with question and answer fields. The entry titulo can be the question itself. Render an accordion in the frontend that loads all published FAQs from the API, with no deploys needed to add new questions.

Team / people. A "Team" CPT with role, linkedin, bio and order (number) fields. Sort by the order field on the client to control each person's position on the page.

Project portfolio. Use a "Projects" CPT with client, technologies, live_url, repo_url and highlight fields. The featured_image_url serves as the main project image. Generate static routes by slug for detail pages.

Tips and best practices

  • Create one CPT per semantically distinct content type. Don't use a generic "Content" CPT with a type field to differentiate records — you lose all the benefits of well-defined types.
  • CPT fields arrive in the fields object as flat key-value pairs. The keys are the ones you defined in the panel when creating the type — document the field schema in your own project so other team developers know it.
  • Always validate fields before rendering them. They may be null even if the field is required in the schema — use the ?? operator or explicit checks to provide fallback values.
  • The content type slug (projects, testimonials…) is part of the endpoint URL. If you rename a CPT in the panel its slug will change and you will need to update all integrations — do this carefully in production.
  • For types with few records (FAQs, team, testimonials), load everything without pagination using limit=100. For large catalogues, implement pagination.

Common errors

Code Likely cause Fix
401 API Key not included or revoked Check the X-API-Key header in Settings → API
404 on listing The type slug does not exist or the CPT is disabled Check the exact slug by calling GET /content-types first to see the available types
404 on single entry The entry slug does not exist, is not published, or belongs to a different type Verify the entry is published in the panel and the slug is correct
400 Missing web_id parameter Include web_id in the query string of all requests

See also

  • Posts — for standard editorial content (blog, news, articles)
  • Media — image fields in CPTs use media manager URLs
  • Introduction — authentication, pagination and general API concepts