# Create learn

URL: https://docs.hanzo.ai/docs/openapi/risk/risklearn

> Records a batch of events into the caller organisation's own aggregates and lets its model learn from them.

`POST /v1/risk/learn`

> [Risk →](https://docs.hanzo.ai/docs/openapi/risk) · [All API references →](https://docs.hanzo.ai/docs/openapi) · [Get started →](https://docs.hanzo.ai/docs/guides)

|               |                                        |
| ------------- | -------------------------------------- |
| **Address**   | `https://api.hanzo.ai/v1/risk/learn`   |
| **Method**    | `POST`                                 |
| **Operation** | `riskLearn`                            |
| **Auth**      | `Authorization: Bearer $HANZO_API_KEY` |

Records a batch of events into the caller organisation's own aggregates
and lets its model learn from them. It answers how many it learned from.

IT DOES NOT SCORE, AND THAT IS THE POINT. An observation is a value you record;
learning is a transformation over observations; a verdict is a query against the
result. This op is the first two. \[ops.score] is the third, it is pure, and it
is the ONE entry point to a verdict. They were one call, which meant you could
not record without training and could not train without being answered — and the
model ran twice over every event to produce a verdict the response carried and
no caller read.

TO OBSERVE AND JUDGE, COMPOSE THE TWO, and mind the order. Score FIRST, then
learn: the score is then the model's opinion of an event it has not yet learned
from, which is the question worth asking. The other order answers for a model
that has already absorbed the event it is judging.

This is the training path, and there is no job behind it: the model IS a set of
mass counters over half-space trees, so learning is an increment and the model
is current the instant the last event lands. Nothing from any other
organisation is in it, and nothing from this organisation leaves it.

A RETRY IS INERT. The record deduplicates on the event id you send, and an event
already in it moves nothing, costs nothing and is not counted — so a client that
timed out can send the same batch again and its model holds what it holds.
Without an id of your own there is nothing to converge on: two identical bodies
are two events.

## Request

8 fields, body `application/json` (required).

| Field              | In   | Type              | Required | Description                                                  |
| ------------------ | ---- | ----------------- | -------- | ------------------------------------------------------------ |
| `events`           | body | risk.riskEvent\[] | —        | Events are the things that happened, oldest first.           |
| `events[].at`      | body | string            | —        | At is when it happened, RFC 3339.                            |
| `events[].device`  | body | string            | —        | Device is the device fingerprint, if any.                    |
| `events[].id`      | body | string            | —        | ID is the caller's own stable identifier for the event.      |
| `events[].kind`    | body | string            | —        | Kind is whose behaviour this is: person, session or account. |
| `events[].nano`    | body | integer (int64)   | —        | Nano is the value moved, in nano-USD.                        |
| `events[].peer`    | body | string            | —        | Peer is the counterparty, if any.                            |
| `events[].subject` | body | string            | —        | Subject is the identifier on that kind.                      |

## Response

| Status    | Body              | Meaning |
| --------- | ----------------- | ------- |
| `200`     | risk.riskLearnOut | ok      |
| `default` | problem-details   | refused |

`200` body — 1 field.

| Field     | In   | Type            | Always | Description                                                                                                                                    |
| --------- | ---- | --------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `learned` | body | integer (int64) | —      | Learned is how many of the events the model actually learned from, and is also what the call is metered at: one screen per event learned from. |

Failure carries the platform error shape — see [Errors](https://docs.hanzo.ai/docs/errors).

## Examples

**CLI**

```bash
hanzo risk learn
```

**SDK**

**.ts**

```ts
import { Configuration, RiskApi } from 'hanzoai';

const api = new RiskApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.riskLearn({ events: [{"at":"<at>","device":"<device>","id":"<id>","kind":"<kind>"}] });
```

**.py**

```python
from hanzoai.cloud import ApiClient, Configuration
from hanzoai.cloud.api import RiskApi

client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = RiskApi(client).risk_learn(events=[{"at":"<at>","device":"<device>","id":"<id>","kind":"<kind>"}])
```

**.go**

```go
cfg := hanzoai.NewConfiguration()
cfg.AddDefaultHeader("Authorization", "Bearer "+os.Getenv("HANZO_API_KEY"))
client := hanzoai.NewAPIClient(cfg)

resp, _, err := client.RiskAPI.RiskLearn(context.Background()).Execute()
if err != nil {
	return err
}
```

**.rs**

```rust
use hanzo_client::apis::{configuration::Configuration, risk_api};

let mut cfg = Configuration::new();
cfg.bearer_access_token = std::env::var("HANZO_API_KEY").ok();

let result = risk_api::risk_learn(&cfg, Default::default()).await?;
```

**.java**

```java
import ai.hanzo.cloud.ApiClient;
import ai.hanzo.cloud.api.RiskApi;

ApiClient client = new ApiClient();
client.setBearerToken(System.getenv("HANZO_API_KEY"));

var result = new RiskApi(client).riskLearn();
```

**HTTP**

```bash
curl -X POST https://api.hanzo.ai/v1/risk/learn \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
       "events": [
         {
           "at": "<at>",
           "device": "<device>",
           "id": "<id>",
           "kind": "<kind>"
         }
       ]
     }'
```

**MCP**

Tool `risk`, op `riskLearn` — POST the JSON-RPC envelope to `https://api.hanzo.ai/v1/mcp`.

```bash
curl -X POST https://api.hanzo.ai/v1/mcp \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
       "jsonrpc": "2.0",
       "id": 1,
       "method": "tools/call",
       "params": {
         "name": "risk",
         "arguments": {
           "op": "riskLearn",
           "input": {
             "events": [
               {
                 "at": "<at>",
                 "device": "<device>",
                 "id": "<id>",
                 "kind": "<kind>"
               }
             ]
           }
         }
       }
     }'
```

---

[Risk API](https://docs.hanzo.ai/docs/openapi/risk) · [All Hanzo APIs](https://docs.hanzo.ai/docs/openapi) · [Interactive reference](https://docs.hanzo.ai/docs/openapi/reference)
