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

# Update Document Attributes

> Add mapped attributes (metadata) to a document in Chamelio Knowledge Center using an OAuth access token, and optionally overwrite existing values.

## Endpoint

```
POST /v2/core/contracts/{document_id}/attributes
```

## Authentication

This endpoint requires an OAuth access token. Send it as a bearer token:

```bash theme={null}
Authorization: Bearer your_access_token
```

**Required scope:** `sor:write`

The token must belong to a user (a `client_credentials` token gets a `400`). Access is checked at the organization level: any user whose token has `sor:write` can update any document in the organization.

## Path Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `document_id` | integer | Yes | ID of the document (contract) whose attributes you want to add |

> [!NOTE]
>
> `document_id` is the document's ID in the Knowledge Center, the same ID that [Search](https://docs.chamelio.ai/api-reference/endpoint/v2/core/search.md) and [Get Contract Data](https://docs.chamelio.ai/api-reference/endpoint/v2/core/get-contract-data.md) use. It is **not** the `document_processing_id` returned by [Upload](https://docs.chamelio.ai/api-reference/endpoint/v2/core/upload.md).

## Request Body

| Field | Type | Required | Default | Description |
| - | - | - | - | - |
| `attributes` | array | Yes | — | Attributes to add. Must contain at least one item |
| `override_existing_fields` | boolean | No | `false` | When `true`, an attribute that already exists on the document with a different value is overwritten. When `false`, it is skipped |

### Attribute Object

| Field | Type | Required | Description |
| - | - | - | - |
| `name` | string | Yes | Attribute name (for example `opportunity_id`) |
| `data_type` | string | Yes | One of `text`, `number`, `boolean`, `date`, `link` |
| `value` | string | Yes | Attribute value. Numbers and booleans are accepted and converted to strings. `null` is rejected |

An attribute is identified by its `name` **and** `data_type`. Sending `contract_value` as `number` and `contract_value` as `text` creates two separate attributes.

## Request Example

```bash cURL theme={null}
curl -X POST "https://platform.chamelio.ai/v2/core/contracts/42/attributes" \
  -H "Authorization: Bearer your_access_token" \
  -H "Content-Type: application/json" \
  -d '{
    "attributes": [
      { "name": "opportunity_id", "data_type": "text", "value": "006Hs00001AbCdE" },
      { "name": "effective_date", "data_type": "date", "value": "2025-06-01" },
      { "name": "contract_value", "data_type": "number", "value": "50000" },
      { "name": "auto_renew", "data_type": "boolean", "value": "false" }
    ],
    "override_existing_fields": false
  }'
```

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

document_id = 42

url = f"https://platform.chamelio.ai/v2/core/contracts/{document_id}/attributes"
headers = {
    "Authorization": "Bearer your_access_token",
    "Content-Type": "application/json"
}

payload = {
    "attributes": [
        {"name": "opportunity_id", "data_type": "text", "value": "006Hs00001AbCdE"},
        {"name": "effective_date", "data_type": "date", "value": "2025-06-01"},
        {"name": "contract_value", "data_type": "number", "value": "50000"}
    ],
    "override_existing_fields": False
}

response = requests.post(url, json=payload, headers=headers)
print(response.json())
```

```javascript JavaScript theme={null}
const documentId = 42;

const response = await fetch(
  `https://platform.chamelio.ai/v2/core/contracts/${documentId}/attributes`,
  {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer your_access_token',
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      attributes: [
        { name: 'opportunity_id', data_type: 'text', value: '006Hs00001AbCdE' },
        { name: 'effective_date', data_type: 'date', value: '2025-06-01' },
        { name: 'contract_value', data_type: 'number', value: '50000' }
      ],
      override_existing_fields: false
    })
  }
);

const data = await response.json();
console.log(data);
```

## Response

### Success Response

**Status Code:** `200 OK`

```json theme={null}
{
  "document_id": 42,
  "success_count": 3,
  "skipped_count": 1
}
```

### Response Fields

| Field | Type | Description |
| - | - | - |
| `document_id` | integer | ID of the document |
| `success_count` | integer | Number of attributes written (added or overwritten) |
| `skipped_count` | integer | Number of attributes that were not written (see below) |

The response returns counts only. It doesn't say which attributes were written or skipped. To see the stored values, call [Get Contract Data](https://docs.chamelio.ai/api-reference/endpoint/v2/core/get-contract-data.md) with `include_attributes: true`.

### How existing attributes are handled

| The document already has… | `override_existing_fields: false` | `override_existing_fields: true` |
| - | - | - |
| No attribute with this `name` and `data_type` | Added | Added |
| The same `name` and `data_type`, same value | Skipped | Skipped |
| The same `name` and `data_type`, different value | Skipped | Overwritten |

Attributes not included in the request are left unchanged. There's no way to delete an attribute through the API.

If two attributes in the same request have the same `name` and `data_type` but different values, the second is stored with a suffix (`_1`, `_2`, …).

### Error Responses

| Status | When |
| - | - |
| `400` | The token isn't tied to a user (a `client_credentials` token) |
| `401` | The access token is missing, invalid, or expired, or an `X-API-Key` was sent (v2 accepts OAuth tokens only) |
| `403` | The token doesn't have the `sor:write` scope |
| `404` | No document with this `document_id` exists in your organization |
| `422` | The body is invalid: `attributes` is empty, a field is missing, `value` is `null`, or `data_type` isn't one of the allowed values |
| `500` | The update failed on the server |

```json 422 example theme={null}
{
  "detail": [
    {
      "type": "enum",
      "loc": ["body", "attributes", 0, "data_type"],
      "msg": "Input should be 'text', 'number', 'boolean', 'date' or 'link'"
    }
  ]
}
```

## Use Cases

* **Sync CRM IDs** - Store an opportunity or account ID on the contract so it can be found later with [Search](https://docs.chamelio.ai/api-reference/endpoint/v2/core/search.md) (attribute filter, for example `opportunity_id_text`)
* **Post-upload enrichment** - Add metadata after a document has been processed
* **Data correction** - Fix an attribute value by resending it with `override_existing_fields: true`
