Bring your own image
Generated visuals are not the only option. You can put your own image into your media library through the TrueTone API, then attach it wherever an image_asset_id is accepted. Both doors are free calls, and both land the image in the same library the dashboard shows.
Door one: save an image from a link
POST /api/v1/assets/ingest-url pulls an image from a public URL into your library:
{
"url": "https://example.com/team/headshot.jpg",
"label": "Team headshot"
}A 201 returns the stored asset:
{
"asset": {
"id": "7d2f5a1c-0000-4000-8000-000000000000",
"fileName": "Team headshot",
"mimeType": "image/jpeg",
"sizeBytes": 184320,
"url": "https://...",
"createdAt": "2026-08-28T15:04:05Z"
},
"creditsCharged": 0
}The fetch happens once, under guard, before anything is stored. The URL must be https, must resolve publicly, must actually be an image (PNG, JPEG, GIF, or WebP, checked by content rather than by file extension), and must fit the size cap named in the image_too_large refusal. A refused URL stores nothing and returns a 400 with a stable reason; the reasons are listed on the Errors page. From then on the image is served from your library’s own URL, and the source is never fetched again.
Door two: mint an upload link
When the image is a local file rather than a link, POST /api/v1/assets/upload-link mints a browser upload page. The request has no body:
{
"uploadUrl": "https://hub.truetone.ai/upload/...",
"expiresAt": "2026-08-28T15:19:05Z",
"expiresInSeconds": 900,
"creditsCharged": 0
}Hand uploadUrl to a person and they drag the file in; the page enforces the same image checks as the ingest door. The link is single use and short-lived, with the exact expiry stated in the response. It is bound to your account at mint time, so nothing in the upload itself decides where the image lands.
Treat uploadUrl as a credential: anyone holding it can put one image into your library until it expires or is used.
Using the image
Everywhere an image_asset_id is accepted, it must name an asset you own. An id that does not exist or belongs to someone else answers 404 not_found, nothing is written, and nothing is charged.
On a seed
POST /api/v1/seeds accepts image_asset_id alongside the seed content:
{
"title": "First-time buyer myths",
"content": "Five things buyers keep getting wrong about down payments...",
"category": "purchase",
"image_asset_id": "7d2f5a1c-0000-4000-8000-000000000000"
}When both image_asset_id and image_url are present, the library asset wins.