Avatar photo URL rejected: http, a port, a password or localhost
Sume refuses an avatar image_url that is not public HTTPS: no http, port, user:password or localhost. It fails on validation, with no fetch. Fix and retry.
Sume rejects an avatar image_url with 400 invalid_request when it uses http://, names localhost or a private address, includes a user:password@ part, or sets an explicit port such as :8443. The rejection happens on validation, before Sume makes a single request to your host, so nothing is fetched and no job is created. The docs say the same in one line: the URL must be a fetchable public HTTPS image URL, and Sume rejects localhost, private-network, non-HTTPS and non-image responses (Create new avatar, read 2026-10-05).
A quick way to remember the rule is that Sume only accepts the kind of URL a stranger's browser could open: public, encrypted, on the default port, with no login embedded in the address. If your link needs a VPN, a cookie, a header or a password in the URL, Sume cannot use it. The fix is almost always to publish a copy of the photo at a link that follows those four properties, then pass that link as input.image_url. Doing this before the call avoids a round trip and keeps the failed request out of your logs.
Six URLs the tests refuse
The API suite pins six cases. Each one returns 400 invalid_request with the network untouched, on both the create route and the older model-run route.
- Use https:// only.
- Drop the port: serve the file on 443.
- Remove user:password@ and use a query token if you need a secret.
- Use a public DNS name, not localhost, .internal, .local, .test or a private range.
- Test the URL from outside your own network.
| URL shape | Problem | Result |
|---|---|---|
| http://assets.example.com/photo.png | Not HTTPS | 400 invalid_request |
| https://localhost/photo.png | Loopback host | 400 invalid_request |
| https://127.0.0.1/photo.png | Loopback address | 400 invalid_request |
| https://169.254.169.254/latest/meta-data | Link-local address | 400 invalid_request |
| https://user:password@assets.example.com/photo.png | Credentials in the URL | 400 invalid_request |
| https://assets.example.com:8443/photo.png | Explicit port | 400 invalid_request |
What counts as private
The private-host list is a name and prefix check on the hostname: localhost, names ending in .localhost, .internal, .local or .test, 0.0.0.0, 127. addresses, 10., 169.254., 192.168., 172.16 to 172.31, and IPv6 loopback, fc, fd and fe80 ranges. A staging bucket on an internal DNS name is therefore refused even if it is reachable from your own network.
Fix the URL
Move the photo to a public host on port 443 and keep the secret in the query string, not in the userinfo part. A query token is allowed: the same test suite sends photo.txt?token=private through the fetch and asserts that the error never echoes it. The curl below is a valid create call with a hosted photo.
curl -X POST https://api.sume.com/v1/avatar-1.0/generate \
-H "Authorization: Bearer $SUME_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: avatar-photo-url-fix-001" \
-d '{
"avatar_handle": "reference_presenter",
"input": {
"type": "photo",
"image_url": "https://assets.example.com/headshot.jpg?token=abc123"
}
}'Why the rules exist
These are not Sume-specific whims. They are the standard server-side request forgery guard: a service that fetches URLs on your behalf must not be steered at internal addresses, cloud metadata endpoints, or authenticated URLs. That is why the metadata address 169.254.169.254 appears in the test list, next to loopback.
For you the practical consequence is hosting. A photo on a laptop tunnel, a staging bucket on an internal domain or a private network storage endpoint cannot be used directly. Copy it to a public bucket or CDN first.
Which code you will see
Errors on this path include details.field set to file.url, and details.suggested_input. For a URL that was never fetched, the two codes you may meet are invalid_request for the shape problems above and image_not_fetchable once a fetch was tried and failed. Branch your client on the code, not on the message text, because messages can be reworded and codes are the contract described in the error reference.
What comes after validation
Passing validation is only the first gate. The next gates are the content type (unsupported_image_type) and, when your host redirects, the redirect rules. The shared image_not_fetchable code in the error table lists the same advice: use a public HTTPS image URL and retry.
Sources
Related posts
More in Developers
- Avatar submit response: what next_action tells your client to do
next_action has three values on avatar submits: poll_status while queued or processing, fetch_result once completed, inspect_events for failed or canceled jobs.
- Avatar sync wait: timed_out vs capacity_exhausted, and what to do
A sync avatar submit can return 2xx with sync.timed_out or sync.capacity_exhausted true. Both mean keep the job: poll status_url, never submit a new paid job.
- Avatar script too long? The 4 to 60 second window and how to split it
Sume accepts an avatar talking video only when the script estimates 4 to 60 seconds. Split longer scripts into scenes or jobs; costs from $0.74 to $33.
- Avatar video 400: script must include at least one word
A Sume talking-video request with an empty or whitespace-only script returns 400 invalid_request, with no job or ledger entry. Guard blank template fields.
Written by Sume