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

# Upload artwork

> Upload immutable print-ready artwork for a letter or a postcard

## Overview

Stores one print-ready file and returns its `asset_id`. The body is the raw file, not JSON or a form. A letter takes a PDF of 1 to 60 pages. A postcard side takes a PDF, PNG or JPEG. 20 MB maximum. A stored asset never changes. Free.

Pass the `asset_id` to [Preview](https://docs.oneshotagent.com/api-reference/physical-mail/preview.md) as `artwork.file` (letter) or `artwork.front` and `artwork.back` (postcard).

## Authentication

`X-Agent-ID` plus a signed `x-agent-proof` with `scope: "write"`, or an access token. See [proof authentication](https://docs.oneshotagent.com/sdk/overview.md#read-authentication). 60 requests a minute per agent.

## Headers

**Content-Type** `string` *required*

`application/pdf`, `image/png` or `image/jpeg`. The bytes must be that type.

## Request Body

The raw bytes of the file. Maximum 20 MB.

## Response

**asset\_id** `string`

Asset ID to reference from a preview

**content\_hash** `string`

SHA-256 of the uploaded bytes, hex encoded

**mime\_type** `string`

The `Content-Type` the file was stored with

```bash cURL theme={null}
curl -X POST https://win.oneshotagent.com/v1/tools/physical-mail/assets \
  -H "Content-Type: application/pdf" \
  -H "X-Agent-ID: 0xYourWalletAddress" \
  -H "x-agent-proof: <base64 signed proof, scope=write>" \
  --data-binary @letter.pdf
```

```typescript SDK theme={null}
import { OneShot } from "@oneshot-agent/sdk";

const agent = new OneShot({ privateKey: process.env.AGENT_PRIVATE_KEY });

const asset = await agent.physicalMail.uploadArtwork(pdfBytes, "application/pdf");
console.log(asset.asset_id);
```

```python Python theme={null}
asset = client.physical_mail.upload_artwork(pdf_bytes, "application/pdf")
print(asset["asset_id"])
```

```json Response theme={null}
{
  "asset_id": "00000000-0000-4000-8000-000000000000",
  "content_hash": "<sha-256 hex>",
  "mime_type": "application/pdf"
}
```

```json Invalid Artwork (400) theme={null}
{
  "error": "invalid_artwork"
}
```

## Errors

| Code | When |
| - | - |
| `400 invalid_artwork` | Empty body, over 20 MB, unsupported `Content-Type`, or bytes that are not that type |
| `503 physical_mail_not_configured` | Not available yet |


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.