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

# Custom fields

> Define your own fields, such as a project code or a contract value, and set their values on documents

In this guide, you define a custom field for the workspace and set its value on documents. Custom fields hold structured data that's yours, such as a project code, a cost center, or a renewal date. You can use the values in invitation messages and to show or hide content.

## Before you begin

* Store your API key in the `SAJN_API_KEY` environment variable. To create a key, go to workspace settings in the sajn app, then **Utvecklare** (Developer) > **API-nycklar** (API keys).
* Have a document to set values on. For more information, see [Create a document](/guides/documents/create-document).

## How custom fields work

A custom field definition has the following properties:

* `name`: the name shown in the sajn app, such as `Project code`.
* `type`: what the field belongs to. `DOCUMENT` fields hold values on documents, and `CONTACT` fields hold values on contacts.
* `inputType`: the kind of value: `TEXT`, `TEXTAREA`, `NUMBER`, `DATE`, `EMAIL`, `PHONE`, `URL`, `BOOLEAN`, `SELECT`, or `JSON`.
* `required`: whether the field needs a value.
* `options`: for `SELECT`, the choices as an array of strings, such as `["HR", "Sales"]`.
* `defaultValue`: optional.

Values are always strings. Send a number as `"50000"`, a date as `"2026-12-31"`, and a boolean as `"true"` or `"false"`.

The API sets values on documents. `CONTACT` field values are set in the sajn app.

## Define a field and set its value

<Steps>
  <Step title="Create the definition">
    Send a `POST` request to `/api/v1/custom-fields`:

    <CodeGroup>
      ```bash curl theme={null}
      curl -X POST https://app.sajn.se/api/v1/custom-fields \
        -H "Authorization: Bearer $SAJN_API_KEY" \
        -H "Sajn-Version: 2026-10" \
        -H "Content-Type: application/json" \
        -d '{
          "name": "Department",
          "type": "DOCUMENT",
          "inputType": "SELECT",
          "options": ["HR", "Sales", "Engineering"],
          "required": false
        }'
      ```

      ```javascript Node.js theme={null}
      const response = await fetch("https://app.sajn.se/api/v1/custom-fields", {
        method: "POST",
        headers: {
          Authorization: `Bearer ${process.env.SAJN_API_KEY}`,
          "Sajn-Version": "2026-10",
          "Content-Type": "application/json",
        },
        body: JSON.stringify({
          name: "Department",
          type: "DOCUMENT",
          inputType: "SELECT",
          options: ["HR", "Sales", "Engineering"],
          required: false,
        }),
      });
      const body = await response.json();
      if (!response.ok) {
        throw new Error(`${body.code}: ${body.message} (request ${body.requestId})`);
      }
      console.log(body.id);
      ```

      ```python Python theme={null}
      import os

      import requests

      response = requests.post(
          "https://app.sajn.se/api/v1/custom-fields",
          headers={
              "Authorization": f"Bearer {os.environ['SAJN_API_KEY']}",
              "Sajn-Version": "2026-10",
          },
          json={
              "name": "Department",
              "type": "DOCUMENT",
              "inputType": "SELECT",
              "options": ["HR", "Sales", "Engineering"],
              "required": False,
          },
      )
      body = response.json()
      if not response.ok:
          raise RuntimeError(f"{body['code']}: {body['message']} (request {body['requestId']})")
      print(body["id"])
      ```
    </CodeGroup>

    The response is the definition:

    ```json theme={null}
    {
      "id": "cm4k2xe9t0006abcd1357yzab",
      "name": "Department",
      "type": "DOCUMENT",
      "inputType": "SELECT",
      "defaultValue": null,
      "required": false,
      "options": ["HR", "Sales", "Engineering"],
      "createdAt": "2026-10-01T09:00:00.000Z",
      "updatedAt": "2026-10-01T09:00:00.000Z"
    }
    ```

    Definitions are shared by the workspace. To find existing ones, call [List all custom fields](/api-reference/list-all-custom-fields), optionally with `type=DOCUMENT`. The list is in `data`; while `hasMore` is `true`, pass `nextCursor` as `cursor` to get the next page.
  </Step>

  <Step title="Set the value on a document">
    Send `customFields` with the definition's `id` as `customFieldId`. You can send it when you create the document, or with a `PATCH` request:

    ```bash theme={null}
    curl -X PATCH https://app.sajn.se/api/v1/documents/DOCUMENT_ID \
      -H "Authorization: Bearer $SAJN_API_KEY" \
      -H "Sajn-Version: 2026-10" \
      -H "Content-Type: application/json" \
      -d '{
        "customFields": [
          { "customFieldId": "cm4k2xe9t0006abcd1357yzab", "value": "Engineering" }
        ]
      }'
    ```

    Replace `DOCUMENT_ID` with the document ID. To clear a value, send `"value": null`.
  </Step>

  <Step title="Read the values">
    Get the document. Its `customFields` array has every value, with the field's `name` and `inputType` as `type`:

    ```bash theme={null}
    curl https://app.sajn.se/api/v1/documents/DOCUMENT_ID \
      -H "Authorization: Bearer $SAJN_API_KEY" \
      -H "Sajn-Version: 2026-10"
    ```

    The following example shows only `customFields`:

    ```json theme={null}
    {
      "customFields": [
        {
          "id": "cm4k2xe9t0006abcd1357yzab",
          "name": "Department",
          "type": "SELECT",
          "value": "Engineering"
        }
      ]
    }
    ```
  </Step>
</Steps>

## Use the values

* In the invitation message: `{{custom.department}}` inserts the value of the field named `Department`. The slug is the field name in lowercase with hyphens. For more information, see [Send a document for signing](/guides/documents/send-for-signing#personalize-the-invitation-message).
* To show or hide content: a `TEXT` or `HTML` field can have a `visibilityRule` with a `customFieldId`, a value in `equals`, and a `mode` of `SHOW_WHEN` or `HIDE_WHEN`.
* By document type: a document category (Dokumenttyp) scopes which custom fields apply to its documents. Set `documentMeta.documentCategoryId`, and call [List document categories](/api-reference/list-document-categories) to see each category's `customFieldIds`.
* From a CRM: send `integrationLink` when you create a document, and sajn prefills mapped custom fields from the linked record. For more information, see [CRM field integration](/guides/integrations/crm-field-integration).

## Change or delete a definition

To rename a field or change its options, send a `PATCH` request to `/api/v1/custom-fields/CUSTOM_FIELD_ID`. Every property is optional, and `type` can't change. Changing `inputType` can make existing values invalid for the new type.

To delete a field, send a `DELETE` request to the same path. The response is `{ "id": "CUSTOM_FIELD_ID", "deleted": true }`. Deleting a definition removes its values from every document and contact. To keep a copy of the definition, get it before you delete it.

## Handle errors

* `400 VALIDATION_FAILED`: `name`, `type`, `inputType`, or `required` is missing or unknown, or `options` is a JSON-encoded string instead of an array.
* `404 NOT_FOUND`: no custom field has the ID.

For the error shape and every code, see [Errors](/api-fundamentals/errors).

## Next steps

<CardGroup cols={2}>
  <Card title="Organize with tags" icon="tags" href="/guides/documents/organizing-with-tags">
    Group documents without structured values.
  </Card>

  <Card title="CRM field integration" icon="arrows-rotate" href="/guides/integrations/crm-field-integration">
    Prefill custom fields from a CRM record.
  </Card>

  <Card title="Create a custom field" icon="code" href="/api-reference/create-a-custom-field-definition">
    See the endpoint in the API reference.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.