Skip to content

Upload a media file to the caller's library

POST
/open/v1/image/upload
curl --request POST \
--url https://api.harperharbor.com/open/v1/image/upload \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: multipart/form-data' \
--form file=@file \
--form roleId=2489E9AD-2EE2-8E00-8EC9-32D5F69181C0 \
--form folderIds=example \
--form relativePath=example

Upload one image, video, audio file or font. Harbor supports JPG, PNG, GIF, WebP, MP4, WebM, MP3, WAV, OGG, WOFF and WOFF2, up to 10 MiB per file. File-byte signatures and the declared media category must agree. Ownership and storage quota checks apply; stored media is immediately usable without content review. Responses do not include moderationState. Harbor assigns a stable libraryPrefix at https:///u/. Named uploads use /; each path segment is URL-encoded. The first selected folder name prefixes that path. Original names and subdirectories are preserved (Unicode NFC); empty/dot/traversal segments, backslashes, controls and overly long paths are rejected. The same path replaces content atomically while preserving URL, asset ID and existing folder/card references. Completed-upload retries do not restore older content. Existing unnamed URLs remain valid.

Media typemultipart/form-data
object
file
required

The file. Accepted types, detected from the bytes: image/jpeg, image/png, image/webp (incl. animated), image/gif, image/svg+xml, video/mp4, video/webm, audio/mpeg (ID3 or raw MPEG frame sync), audio/wav, audio/ogg, font/woff2, font/woff, application/wasm. text/javascript and application/json have no byte signature: they are accepted only when the part’s Content-Type or the file extension (.js, .mjs, .json) declares them, and the bytes must be UTF-8 text (JSON must parse as a whole). QuickTime video (.mov) is refused with invalid_param and detail.reason quicktime; upload MP4 instead. Max 104857600 bytes.

string format: binary
roleId

Optional card to attribute the file to (own card or any public card).

string format: uuid
folderIds

Optional; repeat the field for each folder id to bind the new item to.

Array<string>
relativePath

Harbor extension when capabilities.relativePaths is true: author-provided relative file path, e.g. my-card/expressions/happy.png. Overrides fileName; the first selected folder name is prepended. Same-path uploads replace content. Other providers may not support this field.

string
<= 1024 characters

Stored item.

Media typeapplication/json
object
code
required
integer
Allowed values: 0
data
required
object
imageId
required

Library item id.

integer format: int64
imageUrl
required

Public CDN URL of the stored file.

string format: uri
kind
required
string
Allowed values: image video audio font code data
mimeType
required

Detected MIME type (e.g. image/png, audio/wav, font/woff2).

string
byteSize
required
integer format: int64
pixelWidth
required

0 for non-images.

integer
pixelHeight
required

0 for non-images.

integer
fileName

Sanitized file name the object is stored under.

string
replaced

True when an existing item with the same file name was overwritten.

boolean
Example
{
"code": 0,
"data": {
"kind": "image"
}
}

invalid_param (no file part / unreadable; also returned instead of 413 when a chunked/untruthful-Content-Length body exceeds 100 MiB while the multipart form is being read), invalid_file_type (unsupported bytes or undecodable image), quota_image_exceeded, quota_bytes_exceeded.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

unauthorized.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

file_too_large — request or file larger than 100 MiB.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}

generic_error — storage or database failure.

Media typeapplication/json
object
error
required

Error code (snake_case) or, for legacy handlers, a free-text/localized message.

string
message

Human-readable sentence. Present only on middleware errors and /me, /me/wallet.

string
retryable

Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.

boolean
required_context_tier

Only on a first-turn context_capacity_exceeded: the smallest context tier that fits the whole card with room to talk. Save it as context in player settings and resend. Absent when no tier fits.

integer
>= 1 <= 5
required_context_tokens

The token budget of required_context_tier for the selected model, for display.

integer
constant_lore_trim_fits

Only on a first-turn context_capacity_exceeded: whether saving trimConstantLore=true lets the card play at the current tier. Absent on mid-conversation capacity errors.

boolean
credits_needed

Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.

integer
credits_available

Only on insufficient_credits: the credits the wallet could set aside for it.

integer
Examplegenerated
{
"error": "example",
"message": "example",
"retryable": true,
"required_context_tier": 1,
"required_context_tokens": 1,
"constant_lore_trim_fits": true,
"credits_needed": 1,
"credits_available": 1
}