# Create artifacts — POST /v1/train/jobs/{id}/artifacts

URL: https://docs.hanzo.ai/docs/openapi/train/post-train-jobs-by-id-artifacts

> Records an output its executor is about to store, named by the SHA-256 of its bytes, and answers a presigned PUT for exactly that object under the org's prefix — S3 accepts it only with the checksum and encrypts it under the org's key — or, over 64 MiB, a presigned PUT per 64 MiB part, or stored: true when the org already holds those bytes.

`POST /v1/train/jobs/{id}/artifacts`

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

|               |                                                     |
| ------------- | --------------------------------------------------- |
| **Address**   | `https://api.hanzo.ai/v1/train/jobs/{id}/artifacts` |
| **Method**    | `POST`                                              |
| **Operation** | `post_train_jobs_by_id_artifacts`                   |
| **Auth**      | `Authorization: Bearer $HANZO_API_KEY`              |

Records an output its executor is about to store, named by
the SHA-256 of its bytes, and answers a presigned PUT for exactly that object
under the org's prefix — S3 accepts it only with the checksum and encrypts it
under the org's key — or, over 64 MiB, a presigned PUT per 64 MiB part, or
stored: true when the org already holds those bytes. The executor then reports
an artifact event naming it, and the object is read back, those bytes and
sealed under the org's key, before it counts as stored.

## Request

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

| Field    | In   | Type            | Required | Description                                                               |
| -------- | ---- | --------------- | -------- | ------------------------------------------------------------------------- |
| `id`     | path | string          | yes      | ID names the job, from the path.                                          |
| `id`     | body | string          | —        | ID names the job, from the path.                                          |
| `kind`   | body | string          | —        | Kind is checkpoint, lora, capability, basis or merged.                    |
| `lease`  | body | string          | —        | Lease is the task's lease.                                                |
| `meta`   | body | object          | —        | Meta describes it: its base and revision, its modules, a basis's address. |
| `meta.*` | body | any             | —        |                                                                           |
| `name`   | body | string          | —        | Name labels it.                                                           |
| `sha256` | body | string          | —        | SHA256 is the hex digest of the bytes.                                    |
| `size`   | body | integer (int64) | —        | Size is the byte count.                                                   |
| `task`   | body | string          | —        | Task is the reporting task.                                               |

## Response

| Status    | Body              | Meaning |
| --------- | ----------------- | ------- |
| `200`     | train.registerOut | ok      |
| `default` | problem-details   | refused |

`200` body — 15 fields.

| Field                      | In   | Type            | Always | Description                                                                   |
| -------------------------- | ---- | --------------- | ------ | ----------------------------------------------------------------------------- |
| `sha256`                   | body | string          | —      | SHA256 names the artifact.                                                    |
| `stored`                   | body | boolean         | —      | Stored says the org already holds these bytes: nothing to upload.             |
| `upload`                   | body | train.upload    | —      |                                                                               |
| `upload.expires`           | body | integer (int64) | —      | Expires is when the grant stops being accepted, unix seconds.                 |
| `upload.headers`           | body | object          | —      | Headers must be sent verbatim with the single PUT; the signature covers them. |
| `upload.headers.*`         | body | string          | —      |                                                                               |
| `upload.method`            | body | string          | —      | Method is PUT.                                                                |
| `upload.parts`             | body | train.part\[]   | —      | Parts are the object's consecutive byte ranges, each PUT to its own address.  |
| `upload.parts[].headers`   | body | object          | —      | Headers must be sent verbatim; the signature covers them.                     |
| `upload.parts[].headers.*` | body | string          | —      |                                                                               |
| `upload.parts[].number`    | body | integer (int64) | —      | Number orders the part from 1.                                                |
| `upload.parts[].offset`    | body | integer (int64) | —      | Offset is the part's first byte in the object.                                |
| `upload.parts[].size`      | body | integer (int64) | —      | Size is the part's byte count.                                                |
| `upload.parts[].url`       | body | string          | —      | URL is the presigned address to PUT the range to.                             |
| `upload.url`               | body | string          | —      | URL is the presigned address of a single PUT; empty when Parts is given.      |

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

## Examples

**CLI**

`hanzo` has no subcommand for this operation — the CLI serves only what cloud's live route table confirms. Use HTTP or an SDK.

**SDK**

**.ts**

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

const api = new TrainApi(new Configuration({ accessToken: process.env.HANZO_API_KEY }));
const { data } = await api.postTrainJobsByIdArtifacts({ id: 'id', id: "<id>", kind: "<kind>" });
```

**.py**

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

client = ApiClient(Configuration(access_token=os.environ["HANZO_API_KEY"]))
result = TrainApi(client).post_train_jobs_by_id_artifacts(id='id', 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.TrainAPI.PostTrainJobsByIdArtifacts(context.Background()).Execute()
if err != nil {
	return err
}
```

**.rs**

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

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

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

**.java**

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

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

var result = new TrainApi(client).postTrainJobsByIdArtifacts();
```

**HTTP**

```bash
curl -X POST https://api.hanzo.ai/v1/train/jobs/<id>/artifacts \
  -H "Authorization: Bearer $HANZO_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
       "id": "<id>",
       "kind": "<kind>"
     }'
```

**MCP**

MCP declares no tool for **train** — `tools/list` on `https://api.hanzo.ai/v1/mcp` names the products it does reach. Use HTTP or an SDK.

---

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