Manage blog posts, articles, and other text-based content. Contents support rich text editing (TipTap), markdown input, categories, tags, SEO metadata, revision history, scheduled publishing, and multi-language translations.

Status workflow: draftscheduledpublishedarchived

Content body can be submitted as markdown via bodyMarkdown and is stored internally as TipTap JSON, then served as HTML to frontends.

18 endpoints

GET /contents

List contents

Returns a paginated list of contents for your site. Supports filtering by status, category, tag, locale, date range, and full-text search.

Results include content summaries (no body) for performance. Use GET /contents/{id} to retrieve the full body.

Parameters

Name

In

Type

Required

Description

page

query

number

No

Number

limit

query

number

No

Number <= 100

type

query

string

No

Resource type

status

query

"draft" | "published" | "archived" | "scheduled"

No

One of: draft, published, archived, scheduled

contentMode

query

"standard" | "structured"

No

One of: standard, structured

sortBy

query

"created_at" | "updated_at" | "published_at" | "title" | "slug"

No

One of: created_at, updated_at, published_at, title, slug

sortOrder

query

"asc" | "desc"

No

One of: asc, desc

categoryIds

query

string[]

No

Array of strings

tagIds

query

string[]

No

Array of strings

authorId

query

string

No

Author user ID

dateFrom

query

string

No

Filter from date (ISO 8601)

dateTo

query

string

No

Filter until date (ISO 8601)

publishedFrom

query

string

No

Filter by publish date from (ISO 8601)

publishedTo

query

string

No

Filter by publish date until (ISO 8601)

slug

query

string

No

URL-friendly identifier, unique per site and locale

slugs

query

string[]

No

Array of strings

ids

query

string[]

No

Array of strings

excludeIds

query

string[]

No

Array of strings

hasCategory

query

boolean

No

Boolean

hasTag

query

boolean

No

Boolean

locale

query

string

No

BCP 47 locale code (e.g. "en", "fr")

translationGroupId

query

string

No

Translation group UUID (links translations across locales)

groupByTranslation

query

boolean

No

Boolean

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

Examples

Bash
# List published articles, page 1
curl "https://api.lynkow.com/v1/contents?status=published&page=1&limit=10" \
  -H "Authorization: Bearer $API_TOKEN"

Lynkow SDK (server-side)

JavaScript
// The SDK's public API only fetches published content.
// Use the V1 API directly for draft/archived access.
const response = await fetch('https://api.lynkow.com/v1/contents?status=draft', {
  headers: { Authorization: `Bearer ${apiToken}` }
})

Response Example

JSON
{
  "data": [
    {
      "id": 42,
      "title": "Getting Started with Lynkow",
      "slug": "getting-started-with-lynkow",
      "type": "post",
      "status": "published",
      "excerpt": "Learn how to set up your first headless CMS with Lynkow.",
      "locale": "en",
      "featuredImage": {
        "url": "https://cdn.lynkow.com/sites/abc123/hero.jpg",
        "alt": "Lynkow dashboard screenshot"
      },
      "categories": [
        {
          "id": 1,
          "name": "Tutorials",
          "slug": "tutorials"
        }
      ],
      "tags": [
        {
          "id": 5,
          "name": "Getting Started",
          "slug": "getting-started"
        }
      ],
      "author": {
        "id": 1,
        "fullName": "Jane Doe"
      },
      "publishedAt": "2025-03-15T10:30:00.000Z",
      "createdAt": "2025-03-14T08:00:00.000Z",
      "updatedAt": "2025-03-15T10:30:00.000Z"
    }
  ],
  "meta": {
    "total": 42,
    "perPage": 15,
    "currentPage": 1,
    "lastPage": 3
  }
}

POST /contents

Create a content

Creates a new content in draft status by default. You must provide at least a title and type (currently only "post" is supported).

The slug is auto-generated from the title if not provided. If a slug collision occurs, a numeric suffix is appended (e.g., my-post-2).

Submit the body as markdown via bodyMarkdown. The API converts it to TipTap JSON internally. Alternatively, submit raw TipTap JSON via body for advanced formatting control.

Request Body

Content-Type: application/json

Field

Type

Required

Description

type

"post"

Yes

Required. Resource type. One of: post

title

string

Yes

Required. Display title. 1-500 characters

slug

string

Yes

Required. URL-friendly identifier, unique per site and locale. 1-500 characters

excerpt

string

No

Short summary for previews

body

object

No

Rich text content as TipTap JSON

bodyMarkdown

string

No

Content as Markdown (converted to TipTap JSON on save)

status

"draft" | "published" | "archived" | "scheduled"

No

One of: draft, published, archived, scheduled

featuredImage

string

No

Featured image URL

scheduledAt

string

No

Scheduled publication datetime (ISO 8601)

metaTitle

string

No

Custom meta title for SEO. Max 255 characters

metaDescription

string

No

Meta description for search engines. Max 500 characters

keywords

string

No

Comma-separated SEO keywords. Max 500 characters

canonicalUrl

string

No

Canonical URL override. Max 1000 characters

ogImage

string

No

Open Graph image URL for social sharing. Max 1000 characters

meta

object

No

Additional metadata

categoryIds

string[]

No

Min 1 character. Array of strings

tagIds

string[]

No

Array of strings

customData

object | null

No

Custom fields data (structured content)

locale

string

No

BCP 47 locale code (e.g. "en", "fr")

translationGroupId

string

No

Translation group UUID (links translations across locales)

faqSettings

object

No

FAQ detection and JSON-LD settings

Responses

Status

Description

201

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

422

Validation error

Notes: - scheduledAt is required when status is "scheduled" and must be in the future.

  • categoryIds must reference existing category UUIDs for the same site.

  • customData is available only for contents whose category uses structured content schemas.

Examples

Bash
curl -X POST https://api.lynkow.com/v1/contents \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "post",
    "title": "Getting Started with Lynkow",
    "bodyMarkdown": "# Welcome\n\nThis is your first article.",
    "status": "draft",
    "categoryIds": ["uuid-of-category"]
  }'

Response Example

JSON
{
  "data": {
    "id": 43,
    "title": "Getting Started with Lynkow",
    "slug": "getting-started-with-lynkow",
    "type": "post",
    "status": "draft",
    "excerpt": null,
    "bodyHtml": "<h1>Welcome</h1><p>This is your first article.</p>",
    "locale": "en",
    "featuredImage": null,
    "categories": [
      {
        "id": 1,
        "name": "Tutorials",
        "slug": "tutorials"
      }
    ],
    "tags": [],
    "meta": {
      "metaTitle": null,
      "metaDescription": null,
      "canonicalUrl": null,
      "ogImage": null
    },
    "author": {
      "id": 1,
      "fullName": "Jane Doe"
    },
    "publishedAt": null,
    "createdAt": "2025-04-06T12:00:00.000Z",
    "updatedAt": "2025-04-06T12:00:00.000Z"
  }
}

Search contents

Full-text search across content titles, slugs, and bodies. Returns matching contents with highlighted search terms.

More powerful than the search query param on the list endpoint — this performs full-text search with ranking.

Parameters

Name

In

Type

Required

Description

page

query

number

No

Number

limit

query

number

No

Number <= 100

type

query

string

No

Resource type

status

query

"draft" | "published" | "archived" | "scheduled"

No

One of: draft, published, archived, scheduled

contentMode

query

"standard" | "structured"

No

One of: standard, structured

sortBy

query

"created_at" | "updated_at" | "published_at" | "title" | "slug"

No

One of: created_at, updated_at, published_at, title, slug

sortOrder

query

"asc" | "desc"

No

One of: asc, desc

categoryIds

query

string[]

No

Array of strings

tagIds

query

string[]

No

Array of strings

authorId

query

string

No

Author user ID

dateFrom

query

string

No

Filter from date (ISO 8601)

dateTo

query

string

No

Filter until date (ISO 8601)

publishedFrom

query

string

No

Filter by publish date from (ISO 8601)

publishedTo

query

string

No

Filter by publish date until (ISO 8601)

slug

query

string

No

URL-friendly identifier, unique per site and locale

slugs

query

string[]

No

Array of strings

ids

query

string[]

No

Array of strings

excludeIds

query

string[]

No

Array of strings

hasCategory

query

boolean

No

Boolean

hasTag

query

boolean

No

Boolean

locale

query

string

No

BCP 47 locale code (e.g. "en", "fr")

translationGroupId

query

string

No

Translation group UUID (links translations across locales)

groupByTranslation

query

boolean

No

Boolean

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

Examples

Bash
curl "https://api.lynkow.com/v1/contents/search?q=getting+started&status=published" \
  -H "Authorization: Bearer $API_TOKEN"

Response Example

JSON
{
  "data": [
    {
      "id": 42,
      "title": "Getting Started with Lynkow",
      "slug": "getting-started-with-lynkow",
      "type": "post",
      "status": "published",
      "excerpt": "Learn how to set up your first headless CMS with Lynkow.",
      "locale": "en",
      "categories": [
        {
          "id": 1,
          "name": "Tutorials",
          "slug": "tutorials"
        }
      ],
      "tags": [
        {
          "id": 5,
          "name": "Getting Started",
          "slug": "getting-started"
        }
      ],
      "author": {
        "id": 1,
        "fullName": "Jane Doe"
      },
      "publishedAt": "2025-03-15T10:30:00.000Z",
      "createdAt": "2025-03-14T08:00:00.000Z",
      "updatedAt": "2025-03-15T10:30:00.000Z"
    }
  ],
  "meta": {
    "total": 3,
    "perPage": 15,
    "currentPage": 1,
    "lastPage": 1
  }
}

POST /contents/bulk/publish

Bulk publish contents

Publishes multiple contents at once. Provide an array of content IDs. Contents must be in draft or scheduled status. Already published contents are silently skipped.

Request Body

Content-Type: application/json

Field

Type

Required

Description

ids

string[]

Yes

Required. 1-100 characters. Array of strings

Responses

Status

Description

201

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

422

Validation error

Notes: - Maximum 50 IDs per request.

  • Triggers content.published webhook for each content.

Examples

Bash
curl -X POST https://api.lynkow.com/v1/contents/bulk/publish \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids": [42, 43, 44, 45, 46]}'

Response Example

JSON
{
  "published": 5
}

POST /contents/bulk/archive

Bulk archive contents

Archives multiple published contents at once. Archived contents are hidden from the public API but preserved for future reference.

Request Body

Content-Type: application/json

Field

Type

Required

Description

ids

string[]

Yes

Required. 1-100 characters. Array of strings

Responses

Status

Description

201

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

422

Validation error

Notes: - Maximum 50 IDs per request.

  • Triggers content.archived webhook for each content.

Examples

Bash
curl -X POST https://api.lynkow.com/v1/contents/bulk/archive \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids": [42, 43, 44]}'

Response Example

JSON
{
  "archived": 3
}

POST /contents/bulk/delete

Bulk delete contents

Permanently deletes multiple contents. This action is irreversible.

Request Body

Content-Type: application/json

Field

Type

Required

Description

ids

string[]

Yes

Required. 1-100 characters. Array of strings

Responses

Status

Description

201

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

422

Validation error

Notes: - Maximum 50 IDs per request.

  • Associated revisions, translations, and category/tag associations are also deleted.

Examples

Bash
curl -X POST https://api.lynkow.com/v1/contents/bulk/delete \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"ids": [50, 51]}'

Response Example

JSON
{
  "deleted": 2
}

GET /contents/slug/:slug

Get content by slug

Retrieves a single content by its URL slug. Useful when you know the slug but not the numeric ID (e.g., from a URL path).

Returns the full content including HTML body, categories, tags, SEO metadata, and structured data.

Parameters

Name

In

Type

Required

Description

slug

path

string

Yes

URL-friendly identifier, unique per site and locale

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

404

Not found

Examples

Bash
curl https://api.lynkow.com/v1/contents/slug/getting-started-with-lynkow \
  -H "Authorization: Bearer $API_TOKEN"

Response Example

JSON
{
  "data": {
    "id": 42,
    "title": "Getting Started with Lynkow",
    "slug": "getting-started-with-lynkow",
    "type": "post",
    "status": "published",
    "excerpt": "Learn how to set up your first headless CMS.",
    "bodyHtml": "<h1>Welcome</h1><p>This is your first article...</p>",
    "locale": "en",
    "featuredImage": {
      "url": "https://cdn.lynkow.com/sites/abc123/hero.jpg",
      "alt": "Lynkow dashboard screenshot"
    },
    "categories": [
      {
        "id": 1,
        "name": "Tutorials",
        "slug": "tutorials"
      }
    ],
    "tags": [
      {
        "id": 5,
        "name": "Getting Started",
        "slug": "getting-started"
      }
    ],
    "meta": {
      "metaTitle": "Getting Started with Lynkow | My Blog",
      "metaDescription": "A step-by-step guide to setting up Lynkow.",
      "canonicalUrl": null,
      "ogImage": null
    },
    "author": {
      "id": 1,
      "fullName": "Jane Doe"
    },
    "publishedAt": "2025-03-15T10:30:00.000Z",
    "createdAt": "2025-03-14T08:00:00.000Z",
    "updatedAt": "2025-03-15T10:30:00.000Z"
  }
}

GET /contents/:id

Get a content

Retrieves a single content by its numeric ID. Returns the full object including HTML body, categories, tags, SEO metadata, structured data (FAQ, Article JSON-LD), and featured image with CDN variants.

Parameters

Name

In

Type

Required

Description

id

path

string

Yes

Unique identifier

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

404

Not found

Examples

Bash
curl https://api.lynkow.com/v1/contents/42 \
  -H "Authorization: Bearer $API_TOKEN"

Response Example

JSON
{
  "id": 42,
  "title": "Getting Started with Lynkow",
  "slug": "getting-started-with-lynkow",
  "type": "post",
  "status": "published",
  "excerpt": "Learn how to set up your first headless CMS.",
  "bodyHtml": "<h1>Welcome</h1><p>This is your first article...</p>",
  "locale": "en",
  "featuredImage": {
    "url": "https://cdn.lynkow.com/sites/abc123/hero.jpg",
    "alt": "Lynkow dashboard screenshot",
    "variants": {
      "thumbnail": "https://cdn.lynkow.com/sites/abc123/hero.jpg?w=150",
      "card": "https://cdn.lynkow.com/sites/abc123/hero.jpg?w=400",
      "content": "https://cdn.lynkow.com/sites/abc123/hero.jpg?w=800",
      "medium": "https://cdn.lynkow.com/sites/abc123/hero.jpg?w=1200"
    }
  },
  "categories": [
    {
      "id": 1,
      "name": "Tutorials",
      "slug": "tutorials"
    }
  ],
  "tags": [
    {
      "id": 5,
      "name": "Getting Started",
      "slug": "getting-started"
    }
  ],
  "meta": {
    "metaTitle": "Getting Started with Lynkow | My Blog",
    "metaDescription": "A step-by-step guide to setting up Lynkow.",
    "canonicalUrl": null,
    "ogImage": null
  },
  "author": {
    "id": 1,
    "fullName": "Jane Doe"
  },
  "publishedAt": "2025-03-15T10:30:00.000Z",
  "createdAt": "2025-03-14T08:00:00.000Z",
  "updatedAt": "2025-03-15T10:30:00.000Z",
  "alternates": [
    {
      "locale": "fr",
      "id": 43,
      "slug": "premiers-pas-avec-lynkow"
    }
  ]
}

PUT /contents/:id

Update a content

Updates an existing content. Only the fields you include in the request body are modified — omitted fields remain unchanged.

Updating a published content does not unpublish it. To change status, use the dedicated publish/archive endpoints.

Parameters

Name

In

Type

Required

Description

id

path

string

Yes

Unique identifier

Request Body

Content-Type: application/json

Field

Type

Required

Description

type

"post"

No

Resource type. One of: post

title

string

No

Display title. 1-500 characters

slug

string

No

URL-friendly identifier, unique per site and locale. 1-500 characters

excerpt

string

No

Short summary for previews

body

object

No

Rich text content as TipTap JSON

bodyMarkdown

string

No

Content as Markdown (converted to TipTap JSON on save)

status

"draft" | "published" | "archived" | "scheduled"

No

One of: draft, published, archived, scheduled

featuredImage

string

No

Featured image URL

scheduledAt

string

No

Scheduled publication datetime (ISO 8601)

metaTitle

string

No

Custom meta title for SEO. Max 255 characters

metaDescription

string

No

Meta description for search engines. Max 500 characters

keywords

string

No

Comma-separated SEO keywords. Max 500 characters

canonicalUrl

string

No

Canonical URL override. Max 1000 characters

ogImage

string

No

Open Graph image URL for social sharing. Max 1000 characters

meta

object

No

Additional metadata

categoryIds

string[]

No

Min 1 character. Array of strings

tagIds

string[]

No

Array of strings

customData

object | null

No

Custom fields data (structured content)

faqSettings

object

No

FAQ detection and JSON-LD settings

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

404

Not found

422

Validation error

Notes: - Creates a new revision automatically. Use GET /contents/{id}/revisions to view history.

  • Changing the slug does not create a redirect — manage redirects separately.

Examples

Bash
curl -X PUT https://api.lynkow.com/v1/contents/42 \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "title": "Updated Title",
    "excerpt": "Updated excerpt for the article."
  }'

Response Example

JSON
{
  "data": {
    "id": 42,
    "title": "Updated Title",
    "slug": "getting-started-with-lynkow",
    "type": "post",
    "status": "published",
    "excerpt": "Updated excerpt for the article.",
    "bodyHtml": "<h1>Welcome</h1><p>This is your first article...</p>",
    "locale": "en",
    "categories": [
      {
        "id": 1,
        "name": "Tutorials",
        "slug": "tutorials"
      }
    ],
    "tags": [
      {
        "id": 5,
        "name": "Getting Started",
        "slug": "getting-started"
      }
    ],
    "author": {
      "id": 1,
      "fullName": "Jane Doe"
    },
    "publishedAt": "2025-03-15T10:30:00.000Z",
    "createdAt": "2025-03-14T08:00:00.000Z",
    "updatedAt": "2025-04-06T14:00:00.000Z"
  }
}

DELETE /contents/:id

Delete a content

Permanently deletes a content and all its revisions. This action is irreversible. Consider archiving instead if you may need the content later.

Parameters

Name

In

Type

Required

Description

id

path

string

Yes

Unique identifier

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

404

Not found

Notes: - Triggers content.deleted webhook.

  • Associated category/tag links are removed.

Examples

Bash
curl -X DELETE https://api.lynkow.com/v1/contents/42 \
  -H "Authorization: Bearer $API_TOKEN"

Response Example

JSON
{
  "message": "Content deleted"
}

POST /contents/:id/publish

Publish a content

Transitions a content from draft or scheduled to published. Sets publishedAt to the current timestamp if not already set.

Parameters

Name

In

Type

Required

Description

id

path

string

Yes

Unique identifier

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

404

Not found

422

Validation error

Notes: - Triggers content.published webhook.

  • Triggers IndexNow submission if configured.

  • Cannot publish an already archived content — restore it to draft first.

Examples

Bash
curl -X POST https://api.lynkow.com/v1/contents/42/publish \
  -H "Authorization: Bearer $API_TOKEN"

Response Example

JSON
{
  "data": {
    "id": 42,
    "title": "Getting Started with Lynkow",
    "slug": "getting-started-with-lynkow",
    "type": "post",
    "status": "published",
    "excerpt": "Learn how to set up your first headless CMS.",
    "locale": "en",
    "publishedAt": "2025-04-06T14:30:00.000Z",
    "createdAt": "2025-03-14T08:00:00.000Z",
    "updatedAt": "2025-04-06T14:30:00.000Z"
  }
}

GET /contents/:id/revisions

List content revisions

Returns the version history for a content, ordered by most recent first. Each revision captures the full state of the content at that point in time.

Revisions are created automatically on every update via the API or admin dashboard.

Parameters

Name

In

Type

Required

Description

id

path

string

Yes

Unique identifier

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

404

Not found

Examples

Bash
curl https://api.lynkow.com/v1/contents/42/revisions \
  -H "Authorization: Bearer $API_TOKEN"

Response Example

JSON
{
  "data": [
    {
      "id": 301,
      "contentId": 42,
      "title": "Updated Title",
      "changes": [
        {
          "field": "title",
          "from": "Getting Started with Lynkow",
          "to": "Updated Title"
        },
        {
          "field": "excerpt",
          "from": null,
          "to": "Updated excerpt for the article."
        }
      ],
      "createdAt": "2025-04-06T14:00:00.000Z",
      "author": {
        "id": 1,
        "fullName": "Jane Doe"
      }
    },
    {
      "id": 300,
      "contentId": 42,
      "title": "Getting Started with Lynkow",
      "changes": [
        {
          "field": "bodyMarkdown",
          "from": "# Welcome",
          "to": "# Welcome\n\nThis is your first article."
        }
      ],
      "createdAt": "2025-03-15T10:30:00.000Z",
      "author": {
        "id": 1,
        "fullName": "Jane Doe"
      }
    }
  ]
}

POST /contents/:id/revisions/:revisionId/restore

Restore a content revision

Restores a content to a previous revision. The current state is saved as a new revision before restoring, so no data is lost.

Parameters

Name

In

Type

Required

Description

id

path

string

Yes

Unique identifier

revisionId

path

string

Yes

Revision ID

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

404

Not found

422

Validation error

Examples

Bash
curl -X POST https://api.lynkow.com/v1/contents/42/revisions/300/restore \
  -H "Authorization: Bearer $API_TOKEN"

Response Example

JSON
{
  "data": {
    "id": 42,
    "title": "Getting Started with Lynkow",
    "slug": "getting-started-with-lynkow",
    "type": "post",
    "status": "published",
    "excerpt": "Learn how to set up your first headless CMS.",
    "bodyHtml": "<h1>Welcome</h1><p>Original content restored.</p>",
    "locale": "en",
    "createdAt": "2025-03-14T08:00:00.000Z",
    "updatedAt": "2025-04-06T15:00:00.000Z"
  }
}

GET /contents/:id/revisions/:revisionAId/compare/:revisionBId

Compare two revisions

Returns a diff between two revisions of the same content. Useful for reviewing what changed between versions.

Parameters

Name

In

Type

Required

Description

id

path

string

Yes

Unique identifier

revisionAId

path

string

Yes

First revision ID (for comparison)

revisionBId

path

string

Yes

Second revision ID (for comparison)

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

404

Not found

Examples

Bash
curl https://api.lynkow.com/v1/contents/42/revisions/300/compare/301 \
  -H "Authorization: Bearer $API_TOKEN"

Response Example

JSON
{
  "changes": {
    "title": {
      "before": "Getting Started with Lynkow",
      "after": "Updated Title"
    },
    "excerpt": {
      "before": null,
      "after": "Updated excerpt for the article."
    }
  }
}

POST /contents/:id/duplicate

Duplicate a content

Creates a copy of an existing content with a new auto-generated slug (suffixed with -copy). The duplicate is created in draft status regardless of the original's status.

Parameters

Name

In

Type

Required

Description

id

path

string

Yes

Unique identifier

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

404

Not found

422

Validation error

Notes: - Category and tag associations are copied.

  • Revisions are NOT copied — the duplicate starts with a clean history.

Examples

Bash
curl -X POST https://api.lynkow.com/v1/contents/42/duplicate \
  -H "Authorization: Bearer $API_TOKEN"

Response Example

JSON
{
  "data": {
    "id": 44,
    "title": "Getting Started with Lynkow",
    "slug": "getting-started-with-lynkow-copy",
    "type": "post",
    "status": "draft",
    "excerpt": "Learn how to set up your first headless CMS.",
    "locale": "en",
    "categories": [
      {
        "id": 1,
        "name": "Tutorials",
        "slug": "tutorials"
      }
    ],
    "tags": [
      {
        "id": 5,
        "name": "Getting Started",
        "slug": "getting-started"
      }
    ],
    "publishedAt": null,
    "createdAt": "2025-04-06T15:00:00.000Z",
    "updatedAt": "2025-04-06T15:00:00.000Z"
  }
}

GET /contents/:id/translations

Get translation status

Returns the translation status for a content across all enabled locales. For each locale, indicates whether a translation exists and its current status.

Parameters

Name

In

Type

Required

Description

id

path

string

Yes

Unique identifier

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

404

Not found

Examples

Bash
curl https://api.lynkow.com/v1/contents/42/translations \
  -H "Authorization: Bearer $API_TOKEN"

Response Example

JSON
{
  "data": {
    "en": {
      "status": "complete",
      "contentId": 42,
      "title": "Getting Started with Lynkow"
    },
    "fr": {
      "status": "complete",
      "contentId": 43,
      "title": "Premiers pas avec Lynkow"
    },
    "es": {
      "status": "missing",
      "contentId": null,
      "title": null
    },
    "de": {
      "status": "missing",
      "contentId": null,
      "title": null
    }
  }
}

GET /contents/:id/alternates

Get alternate locale versions

Returns links to all translated versions of this content. Useful for building hreflang tags or locale switchers on the frontend.

Parameters

Name

In

Type

Required

Description

id

path

string

Yes

Unique identifier

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

404

Not found

Examples

Bash
curl https://api.lynkow.com/v1/contents/42/alternates \
  -H "Authorization: Bearer $API_TOKEN"

Response Example

JSON
{
  "data": [
    {
      "locale": "en",
      "id": 42,
      "slug": "getting-started-with-lynkow",
      "path": "/blog/getting-started-with-lynkow"
    },
    {
      "locale": "fr",
      "id": 43,
      "slug": "premiers-pas-avec-lynkow",
      "path": "/fr/blog/premiers-pas-avec-lynkow"
    }
  ]
}

POST /contents/:id/copy-to-locale

Copy content to another locale

Creates a translated copy of a content in a target locale. The copy is linked to the original via a translationGroupId, enabling locale-aware navigation.

Parameters

Name

In

Type

Required

Description

id

path

string

Yes

Unique identifier

Request Body

Content-Type: application/json

Field

Type

Required

Description

targetLocale

string

Yes

Required

Responses

Status

Description

200

Successful response

401

Unauthorized — invalid or missing API token

403

Forbidden — insufficient permissions

404

Not found

422

Validation error

Notes: - Requires locale in the request body (e.g., "fr", "es").

  • The content body and metadata are copied as-is — translation is manual.

  • Fails if a translation already exists for that locale.

Examples

Bash
curl -X POST https://api.lynkow.com/v1/contents/42/copy-to-locale \
  -H "Authorization: Bearer $API_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"locale": "fr"}'

Response Example

JSON
{
  "data": {
    "id": 99,
    "title": "Getting Started with Lynkow",
    "slug": "getting-started-with-lynkow",
    "type": "post",
    "status": "draft",
    "locale": "fr",
    "translationGroupId": "tg-uuid-1",
    "createdAt": "2025-04-06T15:00:00.000Z",
    "updatedAt": "2025-04-06T15:00:00.000Z"
  }
}