Register a file that was PUT to the signed URL
const url = 'https://api.harperharbor.com/open/v1/image/uploadComplete';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"uploadId":"example","roleId":"2489E9AD-2EE2-8E00-8EC9-32D5F69181C0","folderIds":["example"],"fileName":"example","relativePath":"example"}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.harperharbor.com/open/v1/image/uploadComplete \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "uploadId": "example", "roleId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "folderIds": [ "example" ], "fileName": "example", "relativePath": "example" }'Complete an upload after validating its bytes, file type, dimensions and quota. Stored media is immediately usable without content review. The response matches /image/upload and does not include moderationState. Harbor assigns a stable libraryPrefix at https://
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
From /image/uploadIntent.
Optional card to attribute the file to (own card or any public card).
Optional folder ids to bind the new item to.
Original file name. The object is stored as u/<numeric user id>/<fileName> (path, control characters and #%?&<>:"|* are stripped or replaced; the extension is aligned to the sniffed type). Uploading the same name again replaces the existing item in place: same item id, same URL. Omitted → a generated name.
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.
Examplegenerated
{ "uploadId": "example", "roleId": "2489E9AD-2EE2-8E00-8EC9-32D5F69181C0", "folderIds": [ "example" ], "fileName": "example", "relativePath": "example"}Responses
Section titled “Responses”Stored item.
object
object
Library item id.
Public CDN URL of the stored file.
Detected MIME type (e.g. image/png, audio/wav, font/woff2).
0 for non-images.
0 for non-images.
Sanitized file name the object is stored under.
True when an existing item with the same file name was overwritten.
Example
{ "code": 0, "data": { "kind": "image" }}invalid_param; upload_invalid (credential not issued to this account, or tampered); upload_expired (credential older than its lifetime; start over); upload_size_mismatch (stored object is not byteSize bytes); invalid_file_type; quota_image_exceeded; quota_bytes_exceeded.
object
Error code (snake_case) or, for legacy handlers, a free-text/localized message.
Human-readable sentence. Present only on middleware errors and /me, /me/wallet.
Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.
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.
The token budget of required_context_tier for the selected model, for display.
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.
Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.
Only on insufficient_credits: the credits the wallet could set aside for it.
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.
object
Error code (snake_case) or, for legacy handlers, a free-text/localized message.
Human-readable sentence. Present only on middleware errors and /me, /me/wallet.
Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.
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.
The token budget of required_context_tier for the selected model, for display.
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.
Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.
Only on insufficient_credits: the credits the wallet could set aside for it.
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}upload_missing (nothing was PUT to the signed URL, or it was already consumed).
object
Error code (snake_case) or, for legacy handlers, a free-text/localized message.
Human-readable sentence. Present only on middleware errors and /me, /me/wallet.
Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.
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.
The token budget of required_context_tier for the selected model, for display.
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.
Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.
Only on insufficient_credits: the credits the wallet could set aside for it.
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.
object
Error code (snake_case) or, for legacy handlers, a free-text/localized message.
Human-readable sentence. Present only on middleware errors and /me, /me/wallet.
Whether retrying may succeed. Present only on middleware errors, /me, /me/wallet and service/share routes.
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.
The token budget of required_context_tier for the selected model, for display.
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.
Only on insufficient_credits: the credits this turn needs before it can start. Absent when the refusal came without an estimate.
Only on insufficient_credits: the credits the wallet could set aside for it.
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}