# Updating document state

With [document patch api](/embed-api/openapi/builder/patchbuildersessioncontract) you can manipulate
document's state and appearance using API. Please get used to [Get contract state endpoint](/embed-api/openapi/builder/getbuildersessioncontract)
before continuing.

**Key Features**:

- **Batch Updates**: You can perform multiple operations in a single request.
- **Sequential Execution**: Operations are applied **in the order they are defined** (top to bottom).
- **All-or-nothing by default**: If any operation fails, none of them are saved. Opt into [partial success](#partial-success) to keep the operations that succeed.
- **Appearance Updates**: Easily customize fonts, logos, and footers.
- **Document Value Updates**: Modify contract content, such as document name and any field value.
- **Realtime updates**: If the end user has the builder UI open, all changes will be reflected in their view in real time.


## Examples

### Update document name

Document name functions as a file name. It appears in the builder UI and can be edited by the end-user.
Although it is not directly displayed in the document, it is used as the PDF filename when exported.
Note that this is distinct from `/value/basic/title`, which refers to the title visible on the document itself.

```http
PATCH /api/builder/sessions/{session_id}/contract

{
  "operations": [
    {
      "op": "replace",
      "path": "/document_name",
      "value": "Share Purchase Agreement, Hooli Inc."
    }
  ]
}
```

### Update document field values

The `value` property in the state serves as the source of truth for all field values in the contract. In its simplest form, it appears as follows:

```json
{
  "basic": {
    "_type": "value",
    "_id": "01983d68-40a7-8dd9-b97e-15ffe908ad2a",
    "title": "Share Purchase Agreement"
  },
  "parties": [
    {
      "_type": "value",
      "_id": "01983d68-40a7-8dd9-b97e-15ffe908ad1b",
      "type": "company",
      "company_name": "Docue",
      "company_id": "1212",
      "first_name": "Doe",
      "last_name": "John",
      "job_title": "CEO"
    }
  ],
  "fieldset_x": {
    "_type": "value",
    "_id": "01983d68-40a7-8dd9-b97e-15ffe908ad3c",
    "nested_fieldset_y": {
      "_type": "value",
      "_id": "01983d68-40a7-8dd9-b97e-15ffe908ad3d",
      "field_z": 1212
    }
  }
}
```

The schema of the `value` depends on the template used, and each template has a different schema.
The template defines the schema and cannot be **altered at this stage**.

The value consists of `fieldsets` and `fields`. All root-level properties are the names of fieldsets.
A fieldset is a collection of fields. It can be "repeatable," meaning it can contain multiple values;
in practice, its value is an array of objects. In the example above, the `basic` fieldset is non-repeatable,
while the `parties` fieldset is repeatable. The template can define the minimum and maximum number of
instances a fieldset can have.

A fieldset contains fields. A field is typically a single HTML element in the user interface. The type of
value depends on the field type. A field can define validation rules, such as whether the value is
required and the length of certain text.

Fieldsets can also include nested fieldsets, but only three levels of nesting are supported
(two fieldsets and one field).

With patch operations, you can replace entire fieldset values, add new fieldset instances, remove existing fieldset instances, and modify any field value.

**Tip**: If you want to add a new item at the end of the fieldset value, use the path `/value/parties/-`.

**Note**: Currently, it is not possible to replace attachments via the API. Please contact us if you need this feature.

```http
PATCH /api/builder/sessions/{session_id}/contract

{
  "operations": [
    // Add new party
    {
      "op": "add",
      "path": "/value/parties/-",
      "value": {
          "_type": "value",
          "_id": "01983d68-40a7-8dd9-b97e-15ffe908ad1f", // Generate UUIDv7 for new values
          "first_name": "Doe",
          "last_name": "John"
      }
    },
    // Remove first party
    {
      "op": "remove",
      "path": "/value/parties/0"
    }
    // Update name of the first party
    {
      "op": "replace",
      "path": "/value/parties/0/first_name",
      "value": "Johnny"
    }
  ]
}
```

### Update document appearance

Document appearance, such as typography styles, logo and footer can also be updated via API. Pass the whole appearance object at once.

```http
PATCH /api/builder/sessions/{session_id}/contract

{
  "operations": [
    {
      "op": "replace",
      "path": "/appearance",
      "value": {
        "logo": {
          "enabled": true,
          // data URI with base64 encoding, jpg, png or svg
          "content": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==",
          "scale": 50 // logo size, scale from 20-100
        },
        "footer": {
          "enabled": true,
          "content": "Hooli Inc."
        },
        "heading1": {
          "family": "lexend", // See supported fonts below
          "color": "#000000",
          "size": "20",
          "weight": "bold",
          "alignment": "center",
          "transform": "uppercase"
        },
        "heading2": {
          "family": "lexend",
          "color": "#333333",
          "size": "16",
          "weight": "bold",
          "alignment": "left",
          "transform": "none"
        },
        "heading3": {
          "family": "lexend",
          "color": "#333333",
          "size": "14",
          "weight": "bold",
          "alignment": "left",
          "transform": "none"
        },
        "heading4": {
          "family": "lexend",
          "color": "#333333",
          "size": "12",
          "weight": "bold",
          "alignment": "left",
          "transform": "none"
        },
        "heading5": {
          "family": "lexend",
          "color": "#333333",
          "size": "12",
          "weight": "regular",
          "alignment": "left",
          "transform": "none"
        },
        "heading6": {
          "family": "lexend",
          "color": "#333333",
          "size": "12",
          "weight": "regular",
          "alignment": "left",
          "transform": "none"
        },
        "paragraph": {
          "family": "lexend",
          "color": "#000000",
          "size": "11",
          "weight": "regular",
          "alignment": "justify",
          "transform": "none"
        },
        "table": {
          "family": "lexend",
          "size": "10",
          "color": "#000000",
          "bgColor": "#ffffff",
          "headerBgColor": "#f5f5f5",
          "headerColor": "#000000",
          "borderColorEnabled": true,
          "borderColor": "#cccccc",
          "alternatingRowBgColorEnabled": true,
          "alternatingRowBgColor": "#fafafa"
        }
      }
    }
  ]
}
```

## Partial success

By default a patch is all-or-nothing: if any operation fails, nothing is saved and the endpoint returns `422`. Set `allow_partial` to `true` to save the operations that succeed and get a report of the ones that failed:

```http
PATCH /api/builder/sessions/{session_id}/contract

{
  "allow_partial": true,
  "operations": [
    {
      "op": "replace",
      "path": "/document_name",
      "value": "Share Purchase Agreement, Hooli Inc."
    },
    {
      "op": "replace",
      "path": "/value/basic/title",
      "value": "Share Purchase Agreement"
    }
  ]
}
```

The second operation fails because the title is a translatable field and expects a `translatable` object, not a string. The document name is saved:

```json
{
  "data": {
    "state": {
      "document_name": "Share Purchase Agreement, Hooli Inc.",
      "...": "..."
    },
    "signees": []
  },
  "meta": {
    "status": { "code": 200, "text": "OK" },
    "failed_operations": [
      {
        "index": 1,
        "path": "/value/basic/title",
        "error": {
          "code": "invalid_operation",
          "message": "Operation \"replace\" at \"/value/basic/title\" failed: invalid value",
          "details": {
            "value": "Invalid input: expected object, received string"
          }
        }
      }
    ]
  }
}
```

`meta.failed_operations` lists the failed operations in request order; every operation not listed was applied. Each entry has:

- `index`: the zero-based position of the operation in `operations`.
- `path`: the operation's `path`.
- `error.code`: `invalid_operation` when the operation failed validation, or `operation_failed` when it could not be applied to the state. Use this, not `message`, in your code.
- `error.message`: a human-readable reason.
- `error.details`: validation messages keyed by what failed. Each value is a message or a list of messages. It is an empty array when there are no details.


With `allow_partial: true`, the response is always `200`, so check `meta.failed_operations` to see what was saved:

| `meta.failed_operations` | Result |
|  --- | --- |
| Empty | Every operation was applied and saved. |
| Some operations | The other operations were applied and saved. |
| Every operation | Nothing was saved. `data.state` holds the current state. |


Without `allow_partial`, `meta.failed_operations` is always empty, and the same request returns `422` and saves nothing:

```json
{
  "data": null,
  "meta": {
    "status": { "code": 422, "text": "Unprocessable Content" },
    "error": {
      "text": "Operation \"replace\" at \"/value/basic/title\" failed: invalid value",
      "details": {
        "value": "Invalid input: expected object, received string"
      }
    }
  }
}
```

**How operations are evaluated.** Operations run in order, and each one sees the changes of the operations applied before it. A failed operation leaves no changes behind, so later operations that depend on it may fail too. Be careful with array index paths: if `remove /value/parties/0` fails, a later `replace /value/parties/1/first_name` targets the second party of the array as it is, not the one you may have expected.

**Errors that fail the whole request.** Authentication errors (`401`), a missing session or participant (`404`), and malformed requests (`422` — for example a missing `X-Participant` header, an unsupported `op`, or a non-boolean `allow_partial`) are returned for the request as a whole, and nothing is applied.

> **Retrying.** A `5xx` response or a dropped connection does not guarantee that nothing was saved. Read the current state with [Get contract state](/embed-api/openapi/builder/getbuildersessioncontract) before retrying, and keep in mind that operations are not idempotent: resending an `add` to `/value/parties/-` appends another instance.


## Supported fronts

Currently supported fonts are:

- `arial`, `times-new-roman`, `georgia`, `eb-garamond`, `lexend`, `roboto`, `open-sans`, `montserrat`, `lato`, `poppins`, `raleway`, `anek`, `inter`


We can easily support any font available in Google Fonts. Please feel free to ask if you need a font that we are not currently offering.