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

# Custom Fields

> Extend Marble posts with structured workspace-specific metadata like release dates, priorities, or product names and read them from the API.

Custom fields let you add your own metadata to every post in a workspace. Instead of forcing everything into tags or description, you can model structured values like release dates, reading levels, priorities, product names, or any internal metadata your team needs.

## Why use custom fields

* Extend Marble's default post schema without changing your frontend code structure each time.
* Keep metadata consistent across all posts with typed inputs.
* Return custom field values directly in API responses under a single `fields` object.

## Create and manage fields

<Steps>
  <Step title="Open Custom Fields">
    Open your workspace settings, then select the **Custom Fields** section
    under the developer settings area.
  </Step>

  <Step title="Create a field">
    Add a field name, key, type, and optional description. Mark it as required
    if editors must provide a value.
  </Step>

  <Step title="Use it in the editor">
    Open any post and set values in the **Metadata** tab. Every post can store
    a value for each custom field.
  </Step>
</Steps>

You can also manage field definitions through the API, SDK, or MCP server.
Agents should call `get_fields` before writing post field values, create any
missing field with `create_field`, then pass values under `fields` when creating
or updating a post.

## Field types

| Type          | Input in Marble  | API value                                      |
| ------------- | ---------------- | ---------------------------------------------- |
| `text`        | Multi-line text  | `string \| null`                               |
| `number`      | Numeric input    | `number \| null`                               |
| `boolean`     | Toggle/switch    | `boolean \| null`                              |
| `date`        | Date picker      | `string \| null` (ISO date, e.g. `2026-04-02`) |
| `richtext`    | Rich text editor | `string \| null` (HTML string)                 |
| `select`      | Single option    | `string \| null`                               |
| `multiselect` | Multiple options | `string[] \| null`                             |

<Note>
  `select` and `multiselect` fields require predefined options. Option values
  are what you receive in the API and what you send when writing post values.
</Note>

## Writing fields through the API

Create field definitions first:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "key": "audience",
  "name": "Audience",
  "type": "multiselect",
  "required": false,
  "options": [
    { "value": "developers", "label": "Developers" },
    { "value": "founders", "label": "Founders" }
  ]
}
```

Then pass values by field key when creating or updating posts:

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "title": "Launch Notes",
  "content": "<p>Hello world</p>",
  "description": "What changed this week",
  "slug": "launch-notes",
  "categoryId": "cat_123",
  "status": "draft",
  "fields": {
    "audience": ["developers", "founders"]
  }
}
```

Unknown field keys, wrong value types, and option values that do not match a
configured `select` or `multiselect` option are rejected with `400` responses.

## API response shape

Custom fields are exposed on posts as `post.fields` (single post) and `posts[].fields` (list posts).
In the current public API, custom fields are returned in `GET` post responses.

### Example response

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "post": {
    "id": "cm9x...",
    "slug": "shipping-custom-fields",
    "title": "Shipping Custom Fields",
    "fields": {
      "release_date": "2026-04-02",
      "priority_score": 7,
      "evergreen": true,
      "target_channels": ["blog", "newsletter"],
      "editor_note": "<p>Republish in Q3</p>"
    }
  }
}
```

### Unset fields

If a field exists in your workspace but a post has no value for it yet, Marble returns that key with `null`.

```json theme={"theme":{"light":"github-light","dark":"github-dark"}}
{
  "fields": {
    "release_date": null,
    "priority_score": null
  }
}
```

## Important behavior

* Field keys are workspace-scoped and must be unique.
* Post writes never create fields implicitly. Create or update field definitions
  first, then write values to posts.
* Field type and options are effectively schema choices. Plan them early to avoid migration work later.
* Deleting a field removes that field and its stored values from all posts.
