<!-- Markdown twin of https://pixelvault.dev/blog/host-images-with-your-coding-agent -->

[← Back to blog](/blog)

By [PixelVault](/about) July 26, 2026

# 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](https://img.pixelvault.dev/proj_mkoboesmx25d/img_466rm9gvkt49.jpg?w=1400&fmt=auto&q=auto)

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](/blog/image-hosting-for-ai-agents) 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](https://github.com/pixelvault-dev/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](/blog/images-into-cloud-coding-agents), 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](/docs) 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](https://github.com/pixelvault-dev/skill) — 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](/blog/nano-banana-image-hosting) 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](/blog/mcp-image-hosting) 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](/blog/host-images-for-github-readme) 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](/docs), 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](/pricing).

[Read the docs →](/docs)
