# Resource Model

This page explains how the Kordiam API is organized and how the main resources fit together.

Use this page for the technical resource model. Use the [API reference](/api) for the exact
field-level contract.

If you have used an earlier Kordiam API, some business terms may look familiar. Use this API's
own request, response, and write contracts as the source of truth.

For the editorial meaning behind stories, tasks, publications, statuses, and planning in Kordiam,
see [Editorial Concepts](/docs/getting-started/editorial-concepts).

## Content Production

The core of Kordiam is the editorial workflow: an **Element** aggregate contains **Tasks** (work
assignments) and **Publications** (scheduled outputs).

<Mermaid chart={`erDiagram
    Element ||--o{ Task : contains
    Element ||--o{ Publication : contains
    Element }o--o{ Group : "belongs to"

    Task }o--o| User : "assigned to"
    Task }o--o{ Publication : "linked to"

    Publication }o--|| Platform : "published on"
    Publication }o--o| Category : has
    Publication }o--o| Type : has`}
/>

- An **Element** is the main editorial aggregate. Depending on its contents, it can represent a
  story, a group-based planning entry, or a task-only assignment. Story-like elements can carry
  event data directly on the element.
- A **Task** is a work assignment within an element: writing, photography, video production, and
  similar editorial work. A task can be assigned to a **User** and can contain content fields such
  as headline, lead, and text.
- A **Publication** is a scheduled or published output of an element. Every publication belongs to a
  **Platform**. It can optionally have a **Category** and a **Type**, both defined within that
  platform.
- A task can be linked to multiple publications, and a publication can have multiple tasks. This
  many-to-many link represents which tasks contribute to which outputs.

## Element Aggregate

- **Element** is the top-level (parent) resource of the editorial model. Clients typically start from
  `/api/v2/elements` and treat the returned element body as the canonical current state.
- Read endpoints are aggregate-oriented: `GET /api/v2/elements` and `GET /api/v2/elements/{id}`
  return element documents with tasks, publications, and custom-field values inline.
- **Task** and **Publication** writes are nested under `/api/v2/elements/{id}` because both belong
  to one parent element.
- After creating or updating a nested task or publication, clients should treat the returned
  element body as the refreshed state of the parent element.

### Element Content Requirement

An element must contain **at least one** of the following to be valid:

- **Publication** - at least one publication (on any platform)
- **Task assigned to a registered user** - a task only counts if it is assigned to a registered user (`user.id` is set). Tasks assigned to non-registered users (identified only by name and email) do not satisfy this requirement.
- **Group** - at least one group association

This rule matters for element creation and root element write flows such as `POST /api/v2/elements`
and root element `PUT` or `PATCH`. For nested task and publication operations, rely on the exact
endpoint contract in [API reference](/api) instead of assuming that every nested write re-validates
the same root rule. For method-level behavior such as create defaults, replacement semantics,
partial updates, and deletes, see [Write Semantics](/docs/guides/api-conventions/write-semantics).

### Task Assignees

Task writes accept either a registered user ID or an unregistered assignee:

- provide `user.id` to assign a registered user; it takes precedence over `name` and `email`.
- when `id` is omitted, `user.email` is resolved to an accessible registered user when possible.
- if the email does not resolve, the API uses or creates an unregistered assignee from `name` and
  `email`.

Only tasks assigned to registered users count toward the element content requirement above.

## Organization & Configuration

Platforms, groups, users, and reference data form the configuration layer that the content model
references.

<Mermaid chart={`erDiagram
    Platform ||--o{ Category : contains
    Platform ||--o{ Type : contains

    Category }o--o| Category : "parent"

    Group }o--o| Group : "parent"
    User }o--o{ Group : "member of"
    User ||--o{ Absence : has
    User ||--o{ Shift : has`}
/>

- A **Platform** represents a publication channel such as a website, print edition, newsletter,
  mobile app, or social media account. Each platform contains its own **Categories**, **Types**,
  and optionally **Time Slots**.
- **Categories** organize content within a platform, such as Politics, Sports, or Culture, and
  support one level of nesting.
- **Groups** are organizational units such as editorial desks or departments and can reference a
  parent group.
- **Users** have **Absences** (time-off records) and **Shifts** (work schedule definitions).
  Users may be internal staff members or external contributors, depending on the organization setup.
  Public v2 user endpoints are read-only; absences and shift day notes have dedicated write
  endpoints.

## Statuses

Kordiam provides three independent status systems that operate at different levels of the
editorial model. Available statuses are configured per organization.

| Status type          | Applies to    | Endpoint                       | Typical use                      |
|----------------------|---------------|--------------------------------|----------------------------------|
| Element Status       | Element       | `/api/v2/element-statuses`     | Editorial decision pipeline (pitch → publish) |
| Publication Status   | Publication   | `/api/v2/publication-statuses` | Per-channel production tracking  |
| Task Status          | Task          | `/api/v2/task-statuses`        | Assignment workflow (requested → approved) |

- **Element statuses** apply to the whole element regardless of tasks or platforms. Common for
  decision-making (accepted/rejected) and priority flagging. Element writes use the organization's
  active default status when `elementStatusId` is omitted or `null`; if status selection is disabled
  or no active default exists, the element is assigned the special **No Status** value.
- **Publication statuses** are per publication. One element with three publications can carry three
  different statuses. Each platform can define which statuses are available and which default
  publication status product workflows may preselect. Public REST create and patch semantics for
  omitted or `null` `statusId` are method-specific; use the endpoint schema as the source of truth.
- **Task statuses** track individual assignment progress. Task-format metadata can expose different
  status lists for different formats. Tasks have no **No Status** value; omitted or `null` write
  behavior is method-specific.

Each **Task Format** (text, picture, video, audio, other) exposes task-format metadata such as its
configured task-status list. Public v2 task-format endpoints are read-only under
`/api/v2/task-formats`.

For the full editorial context behind statuses, see [Editorial Concepts](/docs/getting-started/editorial-concepts#statuses).

## Custom Fields

Custom fields allow extending the data model with organization-specific attributes. There are three
definition scopes, each with its own endpoint:

| Scope         | Definition Endpoint                  | Values Stored On     |
|---------------|--------------------------------------|----------------------|
| Element       | `/api/v2/custom-fields`              | Element              |
| Task          | `/api/v2/task-custom-fields`         | Task                 |
| Publication   | `/api/v2/publication-custom-fields`  | Publication          |

The public custom-field value write model supports five types:

| Type | Description |
|------|-------------|
| Text | Free-form text input. |
| Single selection | One option from a predefined list. Options can be color-coded. |
| Multiple selection | Multiple options from a predefined list. |
| Date | An additional date value beyond the publication schedule. |
| Number | Numeric value for quantities, scores, or other measurements. |

Task custom fields can be scoped to specific task formats, and publication custom fields can be scoped to specific platforms.
Some internal definition types are intentionally hidden from public definition reads and are not
writable through the public API.

Custom fields are split into two layers:

- **Definitions and options** live on the top-level custom-field endpoints and describe which fields
  are available.
- **Actual values** travel inside the `customFields` arrays on element, task, and publication
  payloads.

For selection-type fields, the custom-field endpoints manage the reusable options. Element, task,
and publication writes only send the chosen values.

For v1-to-v2 field mapping, see
[Custom-Field Migration](/docs/migration-from-v1/custom-fields).

## Publication CMS Model

Publication CMS data is intentionally grouped inside the nested `cms` object.

- `cms.articleId` identifies the publication in the connected CMS.
- `cms.publishedLink` is a link object for the public-facing content URL.
- `cms.editLink` is a link object for opening the publication in the CMS editorial UI.

This keeps CMS-specific data together instead of scattering CMS fields across the top level of the
publication payload.

## Resources

| Resource                   | Endpoint                                             | Description                                             |
|----------------------------|------------------------------------------------------|---------------------------------------------------------|
| Element                    | `/api/v2/elements`                                   | Core editorial aggregate                                |
| Task                       | `/api/v2/elements/{id}/tasks`                        | Work assignment within an element (text, photo, video)  |
| Publication                | `/api/v2/elements/{id}/publications`                 | Scheduled or published output of an element             |
| Group                      | `/api/v2/groups`                                     | Organizational unit (editorial desk, department)        |
| Category                   | `/api/v2/categories/{id}` and `/api/v2/platforms/{platformId}/categories` | Content category within a platform                      |
| Platform                   | `/api/v2/platforms`                                  | Publication channel (website, print edition, app)       |
| Type                       | `/api/v2/types/{id}` and `/api/v2/platforms/{platformId}/types` | Content type within a platform                          |
| User                       | `/api/v2/users`                                      | System user                                             |
| Absence                    | `/api/v2/absences`                                   | User availability exception                             |
| Shift                      | `/api/v2/shifts`                                     | Work shift definition                                   |
| Task Format                | `/api/v2/task-formats`                               | Task type and format metadata                           |
| Element Status             | `/api/v2/element-statuses`                           | Workflow status for elements                            |
| Task Status                | `/api/v2/task-statuses`                              | Workflow status for tasks                               |
| Publication Status         | `/api/v2/publication-statuses`                       | Workflow status for publications                        |
| Element Custom Field       | `/api/v2/custom-fields`                              | Custom field definition scoped to elements              |
| Task Custom Field          | `/api/v2/task-custom-fields`                         | Custom field definition scoped to tasks                 |
| Publication Custom Field   | `/api/v2/publication-custom-fields`                  | Custom field definition scoped to publications          |

## Identifiers

Most resources expose a Kordiam-owned numeric `id`. Many configuration and scheduling resources also
carry an optional `externalId` for integration with another system.

- Use the returned numeric `id` when linking resources in request bodies.
- Use `/external-id/{externalId}` lookup paths only where the API reference exposes them.
- External IDs are integration identifiers, not a global uniqueness guarantee across all resource
  families.
- Elements do not use a top-level `/external-id` lookup; external-system bindings are managed through
  `/api/v2/elements/{id}/external-element/*` commands.

Examples of exposed external-ID lookups include groups, platforms, categories, types, absences,
shifts, and custom-field options.

## Formats And Conventions

Common wire-format conventions:

- Date-only values use ISO 8601 calendar dates, for example `2026-01-15`.
- Date-time values use ISO 8601 date-times such as `2026-01-15T10:30:00Z` or an explicit offset.
- List query date filters document where both full instants and `yyyy-MM-dd` values are accepted.
- Task `cost.currency` uses an ISO-4217 currency code such as `EUR`.

## Element-Scoped Writes

Tasks and publications are managed as nested writes under an existing element. Common examples:

```
POST /api/v2/elements/{id}/tasks
PATCH /api/v2/elements/{id}/tasks/{taskId}
POST /api/v2/elements/{id}/publications
PATCH /api/v2/elements/{id}/publications/{publicationId}
PATCH /api/v2/elements/{id}/custom-fields
PATCH /api/v2/elements/{id}/tasks/{taskId}/custom-fields
```

Other element-scoped operations, such as external-system linking, publication publishing, schedule
updates, and batch task done-state changes, are also exposed under the addressed element. Use the
[API reference](/api) for the exact endpoint set and request contracts.

The API stays aggregate-first: clients fetch whole elements rather than separate read-only task or
publication collections. Other resources such as groups, categories, platforms, users, and
custom-field definitions remain top-level.

### Task-Publication Links

Task-publication links are explicit in the API.

- The link is publication-driven: publications declare which tasks are linked to them.
- Root element writes and standalone publication writes use different request fields for these links.

For full linking rules, examples, and `localId` constraints, see
[Task-Publication Linking](/docs/guides/editorial-workflows/task-publication-linking).

For list filtering, sorting, and pagination patterns, see
[Querying Lists](/docs/guides/api-conventions/querying-lists). For the editorial domain context
behind these resources, see [Editorial Concepts](/docs/getting-started/editorial-concepts).
For v1-to-v2 configuration mapping, see
[Configuration Resources Migration](/docs/migration-from-v1/configuration-resources).
For user, absence, shift, and shift-day-note migration details, see
[People And Scheduling Migration](/docs/migration-from-v1/people-and-scheduling).
