Desktop Pricing Docs Blog About GitHub View as Markdown (Agent) Get started
Human Agent
← Back to blog

How to host an image with your coding agent

You're deep in a Claude Code or Codex session, and now there's an image — a screenshot the agent just took, an asset it downloaded, a diagram it rendered — that needs to become a URL. The agent stalls at that last mile. Here's the exact prompt to hand it, why the raw API call keeps tripping agents up, and the one-command skill that makes hosting boring.

A coding agent moving an image out of a terminal and turning it into a permanent hosted URL through PixelVault
The last mile: getting an image out of a terminal session and turning it into a permanent, embeddable URL — without leaving the agent.

Coding agents are great at making images exist. Claude Code takes a screenshot of a failing test. Codex downloads a logo, renders a chart, or generates a diagram. Then comes the part where the whole flow grinds to a halt: the file is sitting on disk in a sandbox, and you need a link — something you can paste into a PR, a Slack message, a doc, or an og:image tag. So you break out of the session, open a browser, drag the file somewhere, copy the URL back. The agent did the work in seconds; you spend the next few minutes playing courier.

The broader case for why agents get stuck hosting images is its own post; this is the hands-on version — closing that gap step by step. We'll do it by hand first (so you see exactly what's involved), then collapse it to a single command with the open-source PixelVault skill.

First, the by-hand way — and why agents fumble it

Hosting an image on PixelVault is one HTTP call. From a shell the agent already has — and if your agent runs in the cloud or over SSH, the same call works there, no tunnel required — it's this:

# The by-hand upload — auth required, multipart only, URL is at .data.url
curl -s -X POST https://api.pixelvault.dev/v1/images \
  -H "Authorization: Bearer $PIXELVAULT_API_KEY" \
  -F "file=@screenshot.png" | jq -r '.data.url'

# → https://img.pixelvault.dev/proj_abc123/img_xyz789.png

Simple enough — once. The trouble is that an agent has to get four small things right, every fresh session, with no memory of the last one:

  • The endpoint. POST /v1/images on api.pixelvault.dev — not the img. serve domain, which is a common wrong guess.
  • The auth. A Bearer token in the Authorization header. Miss it and you get a 401.
  • The format. The upload is multipart form-data with the field named file — not a JSON body, not base64, not a remote URL. Agents love to guess JSON here and get a 400.
  • The response shape. The URL lives at .data.url, inside a data envelope — not at the top level. Read the wrong path and the agent confidently hands you undefined.

None of these are hard. All of them are the kind of detail an agent re-derives (and occasionally re-breaks) on every new task. One more thing to know: brand-new accounts can upload 5 images before email verification is required — verify once and the gate lifts. If you do want to drive it by hand, this is the prompt that gets it right the first time:

# The prompt, if you're doing it without the skill:
Host ./screenshot.png on PixelVault and give me the URL.
POST it to https://api.pixelvault.dev/v1/images with my
PIXELVAULT_API_KEY as a Bearer token, as multipart field `file`.
The URL is at .data.url in the JSON response.

The one-command way: install the skill

The better fix is to stop making the agent remember any of that. PixelVault ships a small, open-source skill catalog — plain SKILL.md files any agent can read — and one of them, pixelvault-upload, does exactly this job. Install it once and configure your key:

# Install the skill once (writes skill files your agent discovers)
npx skills add pixelvault-dev/skill

# Configure your key once (from your Claude Code / Codex session)
/pixelvault-setup

# From now on, hosting is one command:
/pixelvault-upload screenshot.png
# → https://img.pixelvault.dev/proj_abc123/img_xyz789.png

Now the endpoint, the header, the multipart form, and the .data.url read all live inside the skill. Your prompt drops to the thing you actually meant:

# With the skill installed, the whole prompt is just:
Host screenshot.png and give me the URL.

# Claude Code runs /pixelvault-upload for you (Codex: see the rule
# below). No endpoint, no auth header, no multipart, no .data.url.

The agent recognizes the intent, runs /pixelvault-upload, and hands back a permanent https://img.pixelvault.dev/… URL — globally cached on Cloudflare's edge, served with zero egress, and it won't rot. Same catalog also covers the adjacent jobs: /pixelvault-list to see recent uploads, /pixelvault-transform to resize or convert straight from the URL, and /pixelvault-generate if the image doesn't exist yet and you want to make one first.

Claude Code and Codex

npx skills add pixelvault-dev/skill writes the skill files into your project. Claude Code discovers them and picks up /pixelvault-upload automatically — that's the auto-discovery it's built for. Codex doesn't auto-discover skills the same way, so point it at one explicitly with a single line in your AGENTS.md; that's what makes it reach for the hosted-upload flow instead of improvising a curl:

# AGENTS.md (Codex) — one rule so it reaches for the skill every time
When I ask you to host or share an image, use the pixelvault-upload skill
and return the permanent img.pixelvault.dev URL. Never paste base64
or a temporary link into a file — host it and use the URL.

That one rule is worth more than it looks: it stops the agent from re-inventing the by-hand call (and re-making the four mistakes above) on every task. If your agent speaks MCP instead, that's the other door — connect PixelVault's remote MCP server and upload_image shows up as a native tool with no skill files at all. Skill or MCP, the agent ends up in the same place: a permanent URL, no browser. A common target is an image in a README or PR the agent is writing — worth hosting properly, since GitHub's own attachment URLs break once the repo goes public or the image is viewed outside github.com.

What you get back — and what to do with it

The response is a permanent, immutable CDN URL. Because PixelVault does transforms from that same URL, one upload becomes every size you need by adding query params — ?w=1200&h=630&fmt=auto for a social card, ?w=700&q=auto for an inline image in a doc. The agent uploads once; the variants come for free. That's the whole point of hosting from inside the session: the URL is immediately usable everywhere the agent is already writing — the PR description, the Markdown, the email template, the OG tag.

A note on ownership

Your images live in PixelVault's storage on Cloudflare's edge, served from a zero-egress CDN — so an image that gets popular doesn't hand you a bandwidth bill. Bring-your-own-key (BYOK) is on the roadmap with the paid plans, alongside custom domains, so you'll be able to point PixelVault at your own R2 or S3 bucket and own the bytes while keeping the same one-call hosting. It's not shipped yet; we'll say so plainly here when it is.

Try it

Give your agent a hosting tool and the courier work disappears. Grab a key by registering with a single API call, run npx skills add pixelvault-dev/skill, and the next time an image needs a home your agent handles it in one step. The free tier includes 200 MB storage, 500 uploads/month, and 1 GB bandwidth — no credit card. Paid plans start at $9/month.

Read the docs →