## What `customer_metadata` is

`customer_metadata` is an optional, top-level field on the evaluation request. It holds an opaque JSON object that you define and own. RiskOS™ stores the object, forwards it to the workflow engine, and echoes it back **unchanged** on every outbound surface:

- The synchronous response from `POST /api/evaluation`
- Asynchronous webhook callbacks (for example, `evaluation_completed`, `case_status_updated`)

RiskOS™ doesn't transform or interpret the contents as decisioning data. Use it to carry your own context — such as the internal service, product, or business flow that started a request — so you can identify, route, and handle responses without adding custom fields per customer.

> 📘  
> **Note**:  
> For the complete evaluation request schema, including all standard fields available alongside `customer_metadata`, see the [Evaluation API reference](https://help.socure.com/riskos/reference/postevaluation).

### When to use `customer_metadata` instead of `custom`

Both fields accept caller-defined JSON, but they serve different purposes:

| Use `customer_metadata` when… | Use [`custom`](https://help.socure.com/riskos/docs/custom-fields) when… |
| --- | --- |
| You want context returned to you untouched, for routing or correlation. | You need the data as a decisioning input for enrichments or integrations. |
| The data is for your systems, not for Socure to evaluate. | The data feeds workflow rules, case tiles, or reporting exports. |

## How to include `customer_metadata` in a request

Add `customer_metadata` as a top-level sibling of `id`, `timestamp`, `workflow`, and `data`. The value must be a JSON object of your own keys and values.

cURL

```curl
curl -X POST https://riskos.sandbox.socure.com/api/evaluation \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -d '{  
    "id": "onb-12345",  
    "timestamp": "2026-06-26T08:15:30.456Z",  
    "workflow": "consumer_onboarding",  
    "customer_metadata": {  
      "service": "checkout-api",  
      "business_unit": "consumer-lending",  
      "internal_ref": "order-998877"  
    },  
    "data": {  
      "individual": {  
        "given_name": "Jane",  
        "family_name": "Smith",  
        "email": "jane.smith@example.com"  
      }  
    }  
  }'  
```

You can reference the values in workflow conditions using the `$request.customer_metadata.*` path:

```undefined
$request.customer_metadata.business_unit == "consumer-lending"
```

## How to expect it back

### In the synchronous response

When you send `customer_metadata`, the response echoes the same object at the top level, unchanged:

JSON

```json
{
  "id": "onb-12345",
  "eval_id": "6dc8f39c-ecc3-4fe0-9283-fc8e5f99e816",
  "workflow": "consumer_onboarding",
  "eval_status": "evaluation_completed",
  "decision": "ACCEPT",
  "status": "CLOSED",
  "customer_metadata": {
    "service": "checkout-api",
    "business_unit": "consumer-lending",
    "internal_ref": "order-998877"
  },
  "environment_name": "Sandbox"
}
```

### In a webhook callback

For asynchronous workflows, `customer_metadata` appears at the **top level of the webhook envelope** — a sibling of `event_id`, `event_at`, `event_type`, and `data`. It is **not** nested inside `data`.

JSON

```json
{
  "event_id": "336ccd2a-b3a8-49a8-b2cc-89a4ae90feeb",
  "event_at": "2026-06-26T16:16:23.104744158Z",
  "event_type": "evaluation_completed",
  "customer_metadata": {
    "service": "checkout-api",
    "business_unit": "consumer-lending",
    "internal_ref": "order-998877"
  },
  "data": {
    "id": "onb-12345",
    "eval_id": "8770e076-f568-48a9-8201-dca13087e592",
    "workflow": "consumer_onboarding",
    "decision": "ACCEPT",
    "status": "CLOSED"
  }
}
```

## Field constraints

| Constraint | Rule |
| --- | --- |
| Type | A JSON object (`{ ... }`). Arrays, strings, numbers, and booleans at the top level are rejected. |
| Keys and values | Any valid JSON: string, number, boolean, array, or nested object. Designed for string-valued tags and IDs. |
| Nesting | Nested objects and arrays are allowed. All nested content counts toward the size limit. |
| Size | Up to **200 characters** total, measured across the entire serialized JSON (keys, values, braces, and quotes). |
| Numbers | JSON numbers are preserved as numbers. For exact-fidelity identifiers, send them as strings. |

> 🚧  
> **Do not include sensitive data**:  
> `customer_metadata` is stored and echoed verbatim. Do not place personally identifiable information (PII), secrets, or regulated data in it. Send identity data in the standard request fields under `data` instead.

### Validation errors

| Condition | HTTP status | Error code |
| --- | --- | --- |
| Value is not valid JSON | `400` | `INVALID_REQUEST` |
| Value is valid JSON but not an object | `400` | `INVALID_REQUEST` |
| Serialized object exceeds the character limit | `413` | `CUSTOMER_METADATA_TOO_LARGE` |

A size violation returns a message describing the limit and the size received:

JSON

```json
{
  "code": "CUSTOMER_METADATA_TOO_LARGE",
  "error": "customer_metadata exceeds maximum allowed size of 200 characters (received 412)"
}
```

## Backward compatibility

`customer_metadata` is optional. Existing integrations are unaffected:

- Omit the field and requests behave exactly as before.
- Send `null` and RiskOS™ treats it as absent — no value is stored or echoed.
- When the field is absent, it does not appear in the response or webhook payload.

## Common use cases

- **Route webhook callbacks.** Tag each request with the originating service (for example, `"service": "checkout-api"`) and route the callback to the right internal consumer when it arrives.
- **Correlate asynchronous results.** Carry your own `internal_ref` or order ID so you can match a paused-then-completed evaluation back to its originating transaction without server-side state management.
- **Segment by business context.** Attach a `business_unit`, `product`, or `environment` tag so multiple teams can share one RiskOS™ integration and filter responses by their own context.
- **Standardize across products.** Use one consistent context object across every internal product that calls RiskOS™, instead of requesting a custom field per customer or per team.
