> ## Documentation Index
> Fetch the complete documentation index at: https://docs.parsalink.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Marketing CMS

> A headless content system inside ParsaLink — one universal entity drives your blog posts, case studies, projects, service pages, and anything else you want to publish.

<div
  style={{
background: "linear-gradient(135deg, #143D4E 0%, #1C758A 55%, #30C8CA 100%)",
borderRadius: "24px",
padding: "28px",
marginBottom: "28px",
color: "#FFFFFF",
boxShadow: "0 20px 50px rgba(20, 61, 78, 0.22)",
position: "relative",
overflow: "hidden",
border: "1px solid rgba(255,255,255,0.08)"
}}
>
  <div
    style={{
position: "absolute",
top: "-36px",
right: "-36px",
width: "170px",
height: "170px",
background: "rgba(255,255,255,0.08)",
borderRadius: "999px"
}}
  />

  <div
    style={{
position: "absolute",
bottom: "-48px",
left: "-24px",
width: "130px",
height: "130px",
background: "rgba(255,255,255,0.06)",
borderRadius: "999px"
}}
  />

  <div
    style={{
  display: "inline-flex",
  alignItems: "center",
  background: "rgba(255,255,255,0.14)",
  color: "#FFFFFF",
  padding: "7px 14px",
  borderRadius: "999px",
  fontSize: "12px",
  fontWeight: "700",
  marginBottom: "12px",
  letterSpacing: "0.08em",
  textTransform: "uppercase"
}}
  >
    MARKETING CMS
  </div>

  <p style={{ margin: 0, fontSize: "16px", lineHeight: "1.9", maxWidth: "760px", color: "#FFFFFF" }}>
    Publish blog posts, case studies, project showcases, service pages, and any other piece of marketable content from one screen — and pull them into your website via GraphQL.
  </p>
</div>

## One Entity, Many Content Types

Rather than maintain separate models for blog posts, services, case studies, and projects, ParsaLink uses a single **Content Entity** model with a flexible schema. You define your **Content Types** (Project, Blog Post, Case Study, Service, etc.), and every entity follows the schema of its type.

The legacy `Blog` and `Company` apps (Project, CaseStudy, Service) are being migrated into this system — you'll see them coexist for now, but new content should be created as Content Entities.

***

## Anatomy of a Content Entity

Each entity carries:

* **Content Type** — what kind of thing it is (Project, Blog Post, etc.)
* **Data** — structured JSON matching the type's schema (title, author, hero image, summary, dates, etc.)
* **Blocks** — Tiptap-formatted rich content for the body (paragraphs, headings, images, callouts, embeds)
* **Tags** — hierarchical taxonomy via the core Tag system
* **Status** — Draft, Published, or Archived
* **Parent** — optional hierarchical link (e.g. a case study can have a parent project)

The `data` field is type-aware: if your Project type has a `tech_stack` array field, every Project entity gets it; if your Blog Post type has an `author` text field, posts get that instead.

***

## Creating Content

<Steps>
  <Step title="Pick the Content Type">
    Go to **Marketing → New Content** and pick which type you're publishing (Project, Blog Post, etc.).
  </Step>

  <Step title="Fill in the type-specific fields">
    The form is generated from your Content Type's schema — you only fill in what's relevant.
  </Step>

  <Step title="Write the body">
    Use the block editor for paragraphs, headings, images, callouts, code blocks, embeds. Saved as Tiptap JSON so it renders consistently in any frontend.
  </Step>

  <Step title="Tag and link">
    Apply tags so it's filterable. Optionally pick a parent entity (e.g. attach a Case Study to the Project it describes).
  </Step>

  <Step title="Set status">
    Save as Draft to keep iterating, or Published to expose it on the site immediately. Archived hides it from public queries but keeps the history.
  </Step>
</Steps>

***

## Defining Content Types

Admins can add new Content Types from **Settings → Marketing → Content Types**. A type definition includes:

* The schema fields (with names, labels, types: text, number, boolean, date, list, image, etc.)
* Display order
* Whether each field is required

Once defined, the type appears in the New Content dropdown. Existing entities of older types keep working — you only need a type schema change when you want a new field on new content.

<Tip>
  Need a "Customer Story" type that's like a Case Study but with extra fields? Clone the Case Study type schema as a starting point and tweak.
</Tip>

***

## Pulling Content into Your Website (GraphQL)

The Marketing CMS exposes a GraphQL endpoint at `/api/graphql/`. One query replaces what used to be three separate REST calls:

```graphql theme={null}
query LatestPublishedContent {
  contentEntities(status: PUBLISHED, contentType: "blog_post", limit: 6) {
    uid
    title
    summary
    publishedAt
    heroImage
    tags { name }
    blocks
  }
}
```

The GraphQL endpoint requires an API key (X-API-Key header) — generate one at **Settings → API Keys**.

<Note>
  Most websites need only the published GraphQL endpoint. Authenticated mutations (create/update/delete) still go through the REST API or the in-app editor.
</Note>

***

## Migration from Legacy Models

If you're coming from the older Blog (Post / Category / Tag) or Company (Project / Case Study / Service) models:

* New content is best created as ContentEntity.
* Existing rows in the old models stay queryable.
* ParsaLink will continue migrating them over time. No action required from your side.
