Skip to Content
For developersGuidesBring your own image

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.

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.

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.

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.

The whole flow

Get the image in

Ingest from a URL, or mint an upload link and drag the file in.

Keep the id

The asset.id from either door is the handle everything else uses. GET /api/v1/assets lists what you have if you lose track.

Attach it

Pass image_asset_id when saving a seed or generating a social post.

Last updated on